<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<title>bytes.zone - Posts</title>
	<link href="https://bytes.zone/posts/index.xml" rel="self" type="application/atom+xml"/>
  <link href="https://bytes.zone/posts/"/>
	<generator uri="https://www.getzola.org/">Zola</generator>
	<updated>2025-12-04T00:00:00+00:00</updated>
	<id>https://bytes.zone/posts/index.xml</id>
	<entry xml:lang="en">
		<title>Software estimation that doesn&#x27;t suck</title>
		<published>2025-12-04T00:00:00+00:00</published>
		<updated>2025-12-04T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/software-estimation/" type="text/html"/>
		<id>https://bytes.zone/posts/software-estimation/</id>
		<content type="html">&lt;p&gt;You&#x27;ve been asked to give an estimate about when you&#x27;re gonna be able to ship some code. Fine fine, except that there&#x27;s something riding on this… a trade show, seasonal deadline, etc. Point is: there&#x27;s another part of the business depending on you doing a good job here.&lt;&#x2F;p&gt;
&lt;p&gt;But software estimates are notoriously inaccurate, to the degree that people frequently double or halve them depending on what they think of the team. Think about that for a sec: a change of &lt;strong&gt;50% in either direction based solely on personal factors.&lt;&#x2F;strong&gt; Absolutely wild.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Part of that is that software engineers have a weird aversion to figuring out how to do better here. As a result, a lot of people never get better than off-the-cuff &quot;I can knock that out in a week&quot; style estimates, and develop a reputation for being inaccurate. These skills are worth learning, though, if only because giving useful estimates means that you have the time you need to do the rest of your job correctly! You can also build credibility as someone who understands how to navigate complex systems, and end up with more of a say in the processes you&#x27;re a part of.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to share a process that I&#x27;ve personally used to prepare and update estimates for all sorts of software projects. It&#x27;s based based off the advice in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.microsoftpressstore.com&#x2F;store&#x2F;software-estimation-demystifying-the-black-art-9780735690851&quot;&gt;&lt;em&gt;Software Estimation: Demystifying the Black Art&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; by Steve McConnell. I&#x27;m not following this book to the letter, but the process ends up being enough to communicate with to some degree of accuracy. That&#x27;s fine, since that&#x27;s typically what you&#x27;re really after. However, it&#x27;s possible that I&#x27;ve fatally compromised some important statistical property. If you read this and you notice, I&#x27;d appreciate a heads-up!&lt;&#x2F;p&gt;
&lt;p&gt;This is going to be a longer post, but here&#x27;s the overall process in five steps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Break down the work into high-level outcome-focused areas.&lt;&#x2F;li&gt;
&lt;li&gt;Break each item down into tasks small enough to feel familiar.&lt;&#x2F;li&gt;
&lt;li&gt;Go to your team and ask them to give a &quot;about this long&quot; guess as well as a best case and a worst-case for each item.&lt;&#x2F;li&gt;
&lt;li&gt;Do a little math to get a range based on percentage confidence.&lt;&#x2F;li&gt;
&lt;li&gt;Keep your estimates up to date as the project progresses.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This has worked well for me in companies which lean more towards big-plan-up-front as well as companies that try to stick with a more &quot;agile&quot; approach.&lt;&#x2F;p&gt;
&lt;p&gt;However, sometimes people read this and think &quot;oh no! It&#x27;s waterfall!&quot; and bounce off it without getting any benefit. So: there&#x27;s nothing about this process that prevents you from breaking the work into smaller independently-releasable components, or shrinking the project scope, or putting things behind feature flags for beta users, or whatever release strategy you&#x27;re comfortable with. If you&#x27;re in an organization that&#x27;s all-in on agile, you could even use this process to just make sure you&#x27;re not overcommitting in a single sprint.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s also usually true that scope or project management style are things you&#x27;re inheriting instead of something you have direct influence over. Even when you do, working up a proper estimate builds long-term credibility that lets you have more influence over the system.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#cassandra&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;high-level-breakdown&quot;&gt;High-Level Breakdown&lt;&#x2F;h2&gt;
&lt;p&gt;To get our top level breakdown, we need to address two problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;People usually communicate estimates as single points, but ranges are more realistic.&lt;&#x2F;strong&gt; There are always a bunch of possible outcomes, and we need to be able to cover them. So instead of saying &quot;3 weeks&quot;, you might say &quot;we&#x27;re 90% confident that we&#x27;ll land it in 1–5 weeks.&quot; That is, given all our possible outcomes, 90% of them end up shipping in that time. If you &lt;em&gt;must&lt;&#x2F;em&gt; give a single point, give the upper end of the range.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;People are really bad at giving percentage-confident estimates to work up a range.&lt;&#x2F;strong&gt; That gets worse as the range expands: you could easily give me a 90% confident estimate for the weight of a glass of water, but if I ask you for the weight of all the water in Lake Michigan you&#x27;re likely to get it wrong by a couple orders of magnitude. (If you want to try, make a guess and then check the answer in the footnotes.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#lake-michigan&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;We&#x27;ll have to overcome both issues to succeed. To start, we&#x27;re going to break down work into high level areas. Say we&#x27;re adding emoji reactions to posts on some businessware app. We might think of these as the high-level components:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Clicking the reaction button and selecting an emoji adds it to the post.&lt;&#x2F;li&gt;
&lt;li&gt;Clicking an existing reaction adds +1 to it along with your name. If you had already done that, it removes the +1 instead.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From here, we&#x27;d break this down into some finer-grained work items:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Add reaction button to posts&lt;&#x2F;li&gt;
&lt;li&gt;Handle request to add reaction to post&lt;&#x2F;li&gt;
&lt;li&gt;Send reactions+attribution to the frontend along with post payload&lt;&#x2F;li&gt;
&lt;li&gt;Show people who reacted on hover&lt;&#x2F;li&gt;
&lt;li&gt;Add +1 to the reaction on click
&lt;ul&gt;
&lt;li&gt;Uses the same backend route as adding a new reaction, just a slightly different UX&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Remove your own reaction if clicked when present&lt;&#x2F;li&gt;
&lt;li&gt;Handle request to remove reaction from post&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You might get these by talking through the functionality with a designer or PM. Starting out high-level and user-focused usually helps to ensure you don&#x27;t miss anything.&lt;&#x2F;p&gt;
&lt;p&gt;In addition to breaking down the work, this is a good place to make sure that we&#x27;re not missing any implicit work that we need to do. Missing items are the biggest reason why my estimates have been inaccurate in the past, so making sure to consider all the work in the project—even the indirect or wrap-up stuff—helps a lot!&lt;&#x2F;p&gt;
&lt;p&gt;For example, does this work require a refactor? What&#x27;s new&#x2F;unusual about this? What happens in failure modes? I ask questions about the project using something I call &quot;the &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;feature-proofing-checklist&#x2F;&quot;&gt;feature-proofing checklist&lt;&#x2F;a&gt;.&quot; As I said, missing work has been the biggest source of inaccuracy when preparing estimates, so don&#x27;t skip it!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;task-level-breakdown&quot;&gt;Task-Level Breakdown&lt;&#x2F;h2&gt;
&lt;p&gt;Next we need to break these tasks down further to prepare them for estimation. You&#x27;ll know something is small enough if you could imagine it being in a single PR. If your team regularly uses huge PRs, break it down to items that would take less than two days instead.&lt;&#x2F;p&gt;
&lt;p&gt;You&#x27;d do this for all of the tasks, but for the sake of keeping things brief, I&#x27;m only going to do it for a couple:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Add reaction button to posts
&lt;ul&gt;
&lt;li&gt;Upgrade post component to our new standard pattern&lt;&#x2F;li&gt;
&lt;li&gt;Add reaction button, styles, event handlers with DOM tests&lt;&#x2F;li&gt;
&lt;li&gt;Test for keyboard and screen reader accessibility&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Handle request to add reaction to post
&lt;ul&gt;
&lt;li&gt;Add API endpoint to handle POST&lt;&#x2F;li&gt;
&lt;li&gt;Add database table to store many-to-many relationship between posts and users with reaction data&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;m spitballing here, since this isn&#x27;t a real app, but let&#x27;s pretend that the existing post component is pretty messy. Maybe it&#x27;s old code—one of the first things added in this project—and it hasn&#x27;t had any love in a while. This is the perfect time to upgrade it, since doing so will remove incidental complexity and make future updates faster.&lt;&#x2F;p&gt;
&lt;p&gt;The important thing here is to go from &quot;weight of Lake Michigan&quot; to &quot;weight of a glass of water&quot;: you want to get these into small enough chunks that the work gets familiar enough that people can imagine actually doing it. This is something you were probably going to have to do anyway to make the work legible to stakeholders; you might as well get the bonus of an accurate estimate.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-big-is-each-task&quot;&gt;How Big is Each Task?&lt;&#x2F;h2&gt;
&lt;p&gt;Next, we&#x27;ll prepare time-based estimates. We need three numbers for each task in your breakdown. Specifically:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;normal-case&lt;&#x2F;strong&gt; estimate.
&lt;ul&gt;
&lt;li&gt;What&#x27;s your guess about how long this item will take?&lt;&#x2F;li&gt;
&lt;li&gt;Aim for 50% confidence on these tasks. Half the time you do this task, it should take longer. The other half, it should take shorter.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;worst-case&lt;&#x2F;strong&gt; estimate.
&lt;ul&gt;
&lt;li&gt;If everything went wrong—broken builds, libraries not having needed features, unexpectedly-complex code—how long would it take? I like to use the guideline of &quot;how long would this have to take before we would choose to take a different approach?&quot;&lt;&#x2F;li&gt;
&lt;li&gt;This should almost always be quite a bit longer than the normal-case estimate.&lt;&#x2F;li&gt;
&lt;li&gt;Aim for 90% confidence here. (If you did this task 10 times, 9 of them would take less time than the worst-case estimate.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;strong&gt;best-case&lt;&#x2F;strong&gt; estimate.
&lt;ul&gt;
&lt;li&gt;If someone got into some amazing flow state and everything magically fell into place, how long would it take?&lt;&#x2F;li&gt;
&lt;li&gt;This should always be shorter than the normal-case estimate, although for very short tasks it may not be &lt;em&gt;much&lt;&#x2F;em&gt; shorter.&lt;&#x2F;li&gt;
&lt;li&gt;Aim for 90% confidence again here. (If you did this task 10 times, 9 of them would take more time than the best-case estimate.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;It will probably help to put these numbers in a spreadsheet!&lt;&#x2F;p&gt;
&lt;p&gt;Even though it&#x27;s a bit more work, this is the best place to get your team involved. You&#x27;ll find out areas that need to be broken down further, as well as areas that you thought would be difficult but maybe won&#x27;t be. These are some of my favorite discussions when planning, since it gets assumptions into the open where we can deal with them, and reveals differences in opinion between different team members.&lt;&#x2F;p&gt;
&lt;p&gt;This might seem like a lot, but it usually goes quickly. Don&#x27;t skip all three numbers, either: if you ask people for just one number you&#x27;ll usually get something that&#x27;s closer to the best-case estimate than the normal case. As we&#x27;ll see in a minute, that&#x27;ll mean an overly-optimistic final estimate!&lt;&#x2F;p&gt;
&lt;p&gt;For our example, let&#x27;s say we talked with our team and got these estimates:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Item&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Worst Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Normal Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Best Case&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Upgrade post component to new standard pattern&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add reaction button, styles, event handlers with DOM tests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Test for keyboard and screen reader accessibility&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.5 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add API endpoint to handle reaction POST&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add database table to store many-to-many relationship between posts+users with reaction data.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 hour&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;building-an-estimate&quot;&gt;Building an Estimate&lt;&#x2F;h2&gt;
&lt;p&gt;Finally we&#x27;re getting to the point where we can produce something other people would recognize as an estimate!&lt;&#x2F;p&gt;
&lt;p&gt;First, we&#x27;ll need to calculate the &quot;expected case.&quot; In this style of estimation, that&#x27;s pretty simple: just sum up the normal case times. If you&#x27;ve kept to a standard of 50% confidence for these, you&#x27;re doing well! For the part of this project we&#x27;re using as an example, we get 4 days 6 hours (which I&#x27;m choosing to call 4.75 days for maths&#x27;s sake.)&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Item&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Worst Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Normal Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Best Case&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Upgrade post component to new standard pattern&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add reaction button, styles, event handlers with DOM tests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Test for keyboard and screen reader accessibility&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.5 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add API endpoint to handle reaction POST&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add database table to store many-to-many relationship between posts+users with reaction data.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 hour&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Total&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;12.5 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.75 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.375 days&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Our next step will be to turn this into a range by getting the standard deviation and then using it to create a percent-confidence-based estimate.&lt;&#x2F;p&gt;
&lt;p&gt;If you have fewer than 10 tasks&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#law-of-large-numbers&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, this math is pretty easy: &lt;code&gt;stddev = (sum(worstCase) - sum(bestCase)) &#x2F; 3.3&lt;&#x2F;code&gt;. (You&#x27;re just going to have to trust me on the magic constant for now!)&lt;&#x2F;p&gt;
&lt;p&gt;In our case, that&#x27;s &lt;code&gt;(12.5 - 2.375) &#x2F; 3.3&lt;&#x2F;code&gt;, or 3.07.&lt;&#x2F;p&gt;
&lt;p&gt;However, if you have 10 tasks or more it&#x27;s better to calculate &lt;code&gt;(worstCase - bestCase) &#x2F; 3.3&lt;&#x2F;code&gt; for each individual task, then square it to get the variance. To get the final standard deviation, we&#x27;ll take the square root of the sum of the variances. Like this:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Item&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Worst Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Normal Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Best Case&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Stddev&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Variance&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Upgrade post component to new standard pattern&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.756&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.574&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add reaction button, styles, event handlers with DOM tests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.909&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.826&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Test for keyboard and screen reader accessibility&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.5 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.379&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.143&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add API endpoint to handle reaction POST&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 day&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.455&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.207&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Add database table to store many-to-many relationship between posts+users with reaction data.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 days&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2 hours&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1 hour&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.568&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.323&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;That&#x27;s a total variance of 2.073, for a final standard deviation of 1.440.&lt;&#x2F;p&gt;
&lt;p&gt;(That&#x27;s pretty different than the group formula&#x27;s answer of 3.07; that&#x27;s because with few data points we can&#x27;t take advantages of the law of large numbers and have to assume that we&#x27;re 90% confident in the entire data set instead of in each individual point.)&lt;&#x2F;p&gt;
&lt;p&gt;Finally, you take your expected case and compute a range based on what percentage confidence you want to communicate:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: right&quot;&gt;Percentage Confident&lt;&#x2F;th&gt;&lt;th&gt;Calculation&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;2%&lt;&#x2F;td&gt;&lt;td&gt;expected - 2 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;10%&lt;&#x2F;td&gt;&lt;td&gt;expected - 1.28 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;16%&lt;&#x2F;td&gt;&lt;td&gt;expected - standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;20%&lt;&#x2F;td&gt;&lt;td&gt;expected - 0.84 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;25%&lt;&#x2F;td&gt;&lt;td&gt;expected - 0.67 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;30%&lt;&#x2F;td&gt;&lt;td&gt;expected - 0.52 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;40%&lt;&#x2F;td&gt;&lt;td&gt;expected - 0.25 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;50%&lt;&#x2F;td&gt;&lt;td&gt;expected&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;60%&lt;&#x2F;td&gt;&lt;td&gt;expected + 0.25 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;70%&lt;&#x2F;td&gt;&lt;td&gt;expected + 0.52 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;75%&lt;&#x2F;td&gt;&lt;td&gt;expected + 0.67 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;80%&lt;&#x2F;td&gt;&lt;td&gt;expected + 0.84 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;84%&lt;&#x2F;td&gt;&lt;td&gt;expected + standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;90%&lt;&#x2F;td&gt;&lt;td&gt;expected + 1.28 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;98%&lt;&#x2F;td&gt;&lt;td&gt;expected + 2 * standard deviation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;So for our project, using the individual standard deviation calculation, we&#x27;d get a 90% confident estimate by calculating &lt;code&gt;4.75 + 1.28 * 1.44&lt;&#x2F;code&gt;, or 6.59 days. Filling out the table, we get:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: right&quot;&gt;Percentage Confident&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Estimate&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;2%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.87&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;10%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.91&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;16%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3.31&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;20%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3.54&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;25%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3.79&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;30%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.00&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;40%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.38&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;50%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.75&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;60%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.11&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;70%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.50&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;80%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.96&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;84%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;6.19&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;90%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;6.59&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;98%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;7.63&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;communicating-the-estimate&quot;&gt;Communicating the Estimate&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve all heard Spock or a robot or whatever say things like &quot;there is a 95.83462% chance of failure!&quot; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tvtropes.org&#x2F;pmwiki&#x2F;pmwiki.php&#x2F;Main&#x2F;LudicrousPrecision&quot;&gt;There&#x27;s even a TV Tropes page on it.&lt;&#x2F;a&gt; But when you&#x27;re communicating estimates? Don&#x27;t do that!&lt;&#x2F;p&gt;
&lt;p&gt;We can calculate our estimates to whatever precision we want. Since our units are days, calculating to the hundredths place means we&#x27;re claiming a precision of 14 minutes and 24 seconds.&lt;&#x2F;p&gt;
&lt;p&gt;This seems reasonable until you remember that our inputs are… well, if you&#x27;re doing this process for the first time, let&#x27;s call them &quot;fuzzy.&quot; Even when you&#x27;re trying to get to 90% accuracy, you might get lucky and hit 70% the first time.&lt;&#x2F;p&gt;
&lt;p&gt;But at this point, you do need to give someone a number—boss, PM, whoever. My usual way to do it: take the 50th percentile and the 98th percent confidence numbers&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#why-50-and-98&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; and put them into human language.&lt;&#x2F;p&gt;
&lt;p&gt;I want to justify each step here, so:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&quot;We expect this to take between 4.75 and 7.63 days.&quot; Precise but no good, since whoever you&#x27;re giving this too will hear accuracy when you only intended to communicate precision. So…&lt;&#x2F;li&gt;
&lt;li&gt;&quot;We expect this to take between 5 and 8 days.&quot; Better, and if I were communicating internally (e.g. with a PM assigned to the team) I might stop here. Note that I rounded up; rounding down to 4 days would take us from 50% confident to 30%. Overall, however, it&#x27;s still a fairly narrow range. I might improve it to…&lt;&#x2F;li&gt;
&lt;li&gt;&quot;We expect this to take 1–2 weeks.&quot; We&#x27;re 90% confident this work will take &lt;em&gt;at least&lt;&#x2F;em&gt; 4.75 days, so rounding up to a workweek is not unreasonable. And 8 workdays definitely represents the better part of two workweeks.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This is different than the kinds of random padding people do with off-the-cuff estimates (&quot;engineers always pad by 20%; I&#x27;ll cut it by that much&quot;) since we&#x27;re working from reasonable estimates for individual items. However, we know we&#x27;re not going to be 100% accurate, or even accurate to within a tenth of a day in many cases. It&#x27;s reasonable to represent the fuzziness in our inputs with fuzziness in our outputs.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adjusting-for-inaccuracy&quot;&gt;Adjusting for Inaccuracy&lt;&#x2F;h2&gt;
&lt;p&gt;As you proceed through the project, you&#x27;re going to immediately get useful data about how accurate your estimates were. The evidence shows up immediately in your bug tracker: how long did it take between starting and finishing the task? I can tell you that you&#x27;re not gonna hit 100% of your estimates! Fortunately, we can account for that.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s talk about where we got the magic divisor 3.3 from. That&#x27;s actually the amount of standard deviations that you think the range spans, and it depends on the percentage confidence you are trying to hit during the estimation process. Since I told you to aim for 90%, we use 3.3. Here are some more:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: right&quot;&gt;Actual Outcomes within Range&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Divisor for standard deviations&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;10%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.25&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;20%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.51&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;30%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.77&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;40%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;50%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.4&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;60%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1.7&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;70%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.1&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;80%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.6&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;90%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;3.3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: right&quot;&gt;99.7%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;6.0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This works in reverse, too: if we find that we&#x27;ve been inaccurate in our estimates as the project progresses, we can recalculate the estimate based on our real accuracy.&lt;&#x2F;p&gt;
&lt;p&gt;So let&#x27;s pretend that we&#x27;re done with the part of the project we&#x27;ve been estimating so far and have hit a milestone. Hooray and congrats! Remembering that this is just the first part of the project, let&#x27;s update the estimate for the rest.&lt;&#x2F;p&gt;
&lt;p&gt;Say we&#x27;ve got 15 tasks and our actual working time fell within the bounds of the estimate for 12 of them. That ends up being 80% coverage, not 90%. Redoing the calculations, we get a standard deviation of 1.83, up from 1.44. That&#x27;s not a huge jump, but it&#x27;s enough to know that we need to widen our range. If we were to recalculate our sample tasks, the most precise version of the range would go from 4.75–7.63 days to 6.58–8.41 days.&lt;&#x2F;p&gt;
&lt;p&gt;This may be an unpleasant surprise to your stakeholders, but it&#x27;s better to share this kind of news early instead of waiting until the very end of the project to drop that everything is going to take 25% longer than you originally estimated. Point is, you now have the tools to adjust! In the second project you estimate with this method, you&#x27;ll account for more of the things that you missed the first time.&lt;&#x2F;p&gt;
&lt;p&gt;Because you know things will shift around as you get more data, you should agree with your stakeholders that you&#x27;ll be providing updates on the estimate as you move through the project. If they&#x27;re expecting to hear from you, delivering that news gets way easier (and it&#x27;s not that much additional work if you&#x27;ve kept the spreadsheet from earlier around.)&lt;&#x2F;p&gt;
&lt;p&gt;You can also begin in a better place once you have some historical data. If you know that your team almost always ends up at 70%, you can use that to calculate your initial estimate. (You should still work on identifying gaps and building skill, though, so that you can get to 90%!)&lt;&#x2F;p&gt;
&lt;p&gt;Basically, as soon as you can ground your estimate in reality, do that!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s It!&lt;&#x2F;h2&gt;
&lt;p&gt;Just to get everything in one place, here are the steps we&#x27;ve just been over:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Break down work into high-level areas.&lt;&#x2F;li&gt;
&lt;li&gt;Break those areas down until each task feels like something you&#x27;d put in a PR.&lt;&#x2F;li&gt;
&lt;li&gt;Work with your team to estimate the best-case, normal-case, and worst-case scenarios for each task.&lt;&#x2F;li&gt;
&lt;li&gt;Figure out the standard deviation represented by the range between best and worst case, then use it to calculate a time range that you can communicate. (50% and 98% is nice.)&lt;&#x2F;li&gt;
&lt;li&gt;Re-estimate at least at agreed-upon milestones. If you have to share bad news, share it quickly so that your team can get ahead of it and build trust and credibility.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you&#x27;ve enjoyed this, or want to learn more, I&#x27;d really recommend picking up &lt;em&gt;Software Estimation: Demystifying the Black Art&lt;&#x2F;em&gt;. In addition to this technique (which is based off of several chapters, but most notably chapter 10) there&#x27;s a lot about ways to build up an estimate based off analogies to historical data, as well as useful tactics for communicating with stakeholders.&lt;&#x2F;p&gt;
&lt;p&gt;Give this a try, though! I&#x27;d love to know how it turns out for you!&lt;&#x2F;p&gt;
&lt;p&gt;However, before I leave you…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reasonable-objections&quot;&gt;Reasonable Objections&lt;&#x2F;h2&gt;
&lt;p&gt;People tend to be skeptical of this process. Seems like a lot? For what benefit? So I want to address some of that:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;time-cost&quot;&gt;Time Cost&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;This seems like a lot of effort? Is the improvement here really worth it?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I think so, yes! (Or I wouldn&#x27;t be writing about it, would I?)&lt;&#x2F;p&gt;
&lt;p&gt;Basically: as a team leader, this is work you&#x27;d be doing anyway. Even if you aren&#x27;t doing it directly, someone else (maybe your PM) is breaking down projects into smaller components that can be worked as tasks.&lt;&#x2F;p&gt;
&lt;p&gt;After that, it&#x27;s pretty common for a stakeholder to count the tasks&#x2F;stories&#x2F;points in the project and divide by the team&#x27;s velocity to get a simple number. This process provides benefits above and beyond that because:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;We&#x27;re breaking down work down past where an external stakeholder or PM would necessarily have expertise. Doing this requires you to build a good idea of the feature&#x2F;project&#x2F;requirements, and puts you in a position to be a good partner.&lt;&#x2F;li&gt;
&lt;li&gt;It gives your team a chance to come up to speed on the same, as well as creating opportunities for them to point out issues.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s generally more accurate than a simple velocity calculation.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I talked a lot about the statistical machinery in this post since it&#x27;s important to use that correctly. However, a lot of the benefit of this process comes from the discussions you have to have to run it.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;anchoring-bias&quot;&gt;Anchoring Bias&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;How do you run the estimation sessions? Don&#x27;t people anchor on the first number they hear? Doesn&#x27;t this process also bias towards the loudest or most senior person&#x27;s view?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is sort of orthogonal to the estimation process, but it&#x27;s important!&lt;&#x2F;p&gt;
&lt;p&gt;First, I&#x27;ve played a game with people when running these meetings. Instead of just opening the floor and saying &quot;what do you think is the worst-case estimate here?&quot; I&#x27;ll ask people to show a number of fingers on the count of three. Then we have a discussion about why there&#x27;s a broad range, or simply accept the lowest&#x2F;highest answer for a narrow one (depending on if we&#x27;re doing worst-case or best-case.)&lt;&#x2F;p&gt;
&lt;p&gt;All that goes out the window, though, if a team has unhealthy group dynamics. If you have a single person or group dominating the conversation, you&#x27;re going to get a worse result. Fixing this is outside the scope of this post.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-about-fixing-50-confidence&quot;&gt;What about fixing 50% confidence?&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;How do you calibration 50% confidence for the normal case estimates? Seems like a tricky skill to learn. Most teams will be guessing.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yep, most teams will be guessing at first. However, we&#x27;ll get data pretty quickly as to how well we guessed. Unfortunately, I don&#x27;t have a convenient statistical trick to adjust the expected case if you&#x27;re consistently ending up on one side of the line. However, that&#x27;s data that you can take into the next projects and learn from.&lt;&#x2F;p&gt;
&lt;p&gt;McConnell also has a number of strategies in the book that help increase accuracy here.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;identifying-gaps&quot;&gt;Identifying Gaps&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;You mentioned that the biggest inaccuracies have been due to missing work. How do you get better at that skill?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;A combination of best practice and battle scars. After each gap, I&#x27;ve been collecting questions to ask to avoid the issue in the future. Again, my list is available at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;feature-proofing-checklist&#x2F;&quot;&gt;feature-proofing checklist&lt;&#x2F;a&gt; and I encourage you to steal it and make it your own! (I&#x27;ve given this to new team leaders as a handout for years, and it&#x27;s usually been pretty helpful!)&lt;&#x2F;p&gt;
&lt;p&gt;Some notes on mine:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It starts with a bunch of &quot;why&quot; questions. If you can&#x27;t answer those for your team, you&#x27;re not in a good place. This also helps push back on junk work that people haven&#x27;t thought through.&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s a lot of work, but this is another case where you should be doing most of this anyway.&lt;&#x2F;li&gt;
&lt;li&gt;You don&#x27;t need to answer all the questions up front or by yourself. The first section is work with your PM or stakeholders, the second is for gap coverage, and the third has a few questions for nearer the tail end of the project.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;what-if-your-estimate-is-unacceptably-long&quot;&gt;What if your estimate is unacceptably long?&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;What if someone expects a feature to take 3 weeks and you come back with a 3-month estimate?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yeah, this happens. It&#x27;s painful, but a good opportunity to make implicit assumptions explicit. While it can be annoying to have to negotiate&#x2F;cut scope, that&#x27;s part of the job sometimes too.&lt;&#x2F;p&gt;
&lt;p&gt;In my experience, this only has to happen once or twice before people start asking your opinion much earlier in the process. So there&#x27;s a virtuous cycle here as well, in terms of doing a good job on engineering leadership!&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;cassandra&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Of course, it&#x27;s possible that you&#x27;re in a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Cassandra&quot;&gt;Cassandra&lt;&#x2F;a&gt; situation and credibility and accuracy aren&#x27;t the problems. If that&#x27;s the case, I&#x27;ve been there and this process isn&#x27;t the way out. Sorry!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;lake-michigan&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;about 6 trillion US (short) tons, or 5 trillion metric tons.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;law-of-large-numbers&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;If you have fewer than 10 items in your list, you may want to consider breaking them down more. At 10 items and above you start to benefit from the law of large numbers, where errors in your estimates will start cancelling out.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;why-50-and-98&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Why 50% and 98%? Two reasons. First, people are generally happy with &quot;whether we finish early is basically a coin flip, but you can make plans around hitting the end date.&quot; Second, I can remember the calculations off the top of my head: 50% is &lt;code&gt;expected + stddev&lt;&#x2F;code&gt; and 98% is &lt;code&gt;expected + 2 * stddev&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Should we design for iffy internet?</title>
		<published>2025-06-16T00:00:00+00:00</published>
		<updated>2025-06-16T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/should-we-design-for-iffy-internet/" type="text/html"/>
		<id>https://bytes.zone/posts/should-we-design-for-iffy-internet/</id>
		<content type="html">&lt;p&gt;I keep hearing claims like this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Not everyone in the US has access to stable, reliable internet, even in 2025.&lt;&#x2F;li&gt;
&lt;li&gt;Web developers should stop assuming people have fast internet connections and slim their payloads accordingly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This seems intuitively true to me—programmers are gonna have better connectivity because that takes money, and programmers are well-paid. But what&#x27;s the actual scope of the problem?&lt;&#x2F;p&gt;
&lt;p&gt;I dug around, and here&#x27;s some data. My goal here is not to beat anyone over the head with &quot;THOU SHALT NOT ASSUME GOOD INTERNET&quot; but to give an idea about the scope of broadband rollout in the US in a way that can help inform choices we make when designing software.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;If you don&#x27;t feel like reading this whole thing, here&#x27;s the bottom line up front:&lt;&#x2F;strong&gt; you can probably assume internet access in somewhere around 97% of US households, but you should not assume that it&#x27;s better than around 25Mbps down and 3Mbps up, and latency may be significantly worse than you previously assumed. This is likely worse for B2C software than B2B.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;I&#x27;m going to pull from two US government agencies here: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fcc.gov&quot;&gt;Federal Communications Commission (FCC)&lt;&#x2F;a&gt;and the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nces.ed.gov&quot;&gt;National Center for Education Statistics (NCES)&lt;&#x2F;a&gt; (part of the Department of Education.) All the data I&#x27;m referencing was published well before the current administration started gutting the bureaucracy, so I think it&#x27;s fairly reliable.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;assumptions&quot;&gt;Assumptions&lt;&#x2F;h2&gt;
&lt;p&gt;Before we begin, there are a bunch of ways to define &quot;stable&quot; or &quot;reliable&quot; internet connections. For the purposes of this post, I&#x27;m defining that as a &lt;strong&gt;terrestrial link with at least 25Mbps down and 3Mbps up.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Terrestrial because—well, have you ever tried to use a satellite connection for anything real? Latency is awful, and the systems tend to go down in bad weather. They also have had fairly low data caps historically so you had to use your connection judiciously, although this may be changing.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#satellite&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#my-history&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;li&gt;
&lt;li&gt;25&#x2F;3Mpbs because that&#x27;s a fairly common cable package speed, and also around the minimum I’ve been able to use modern SaaS apps on without it getting ridiculously frustrating. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;support.zoom.com&#x2F;hc&#x2F;en&#x2F;article?id=zm_kb&amp;amp;sysparm_article=KB0060748#h_d278c327-e03d-4896-b19a-96a8f3c0c69c&quot;&gt;Zoom says&lt;&#x2F;a&gt; you need 1–4 Mbps in both directions to do a video call.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The FCC&#x27;s broadband map lets you set both these criteria. Let&#x27;s have a look at it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-fcc-broadband-map&quot;&gt;The FCC Broadband Map&lt;&#x2F;h2&gt;
&lt;p&gt;The FCC tracks access to broadband internet in the US. As a result, they publish a map that&#x27;s more accurate than the maps you see from ISPs. This isn&#x27;t &lt;em&gt;completely&lt;&#x2F;em&gt; error-free, but individuals and companies can both submit data on coverage at a street address level and challenge inaccuracies in the existing data. For our purposes, I think it&#x27;s good enough!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to grab some screenshots of the map to fix it at the current data for discussion. That way we can get an idea about regional coverage.&lt;&#x2F;p&gt;
&lt;p&gt;Strangely, they don&#x27;t let you zoom out enough to grab a screenshot of the whole country so I&#x27;m going to look at the west. That&#x27;ll get both urban and rural coverage, as well as several famously internet-y locations (San Francisco Bay Area, Seattle.)&lt;&#x2F;p&gt;
&lt;p&gt;If you want to follow along or compare how the data has changed since this post was published, you can get the map yourself at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;broadbandmap.fcc.gov&quot;&gt;broadbandmap.fcc.gov&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First, here&#x27;s a hex map of coverage using the criteria we set above. The darker each hex is, the higher the percent coverage within it. Dark blue is 100%, white is 0%, grays or faded blues are in between.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;us-national-broadband-map-terrestrial-25-3-2025-06-10.png&quot; alt=&quot;A map of the western US with a color scale overlaid showing access to broadband. Higher-population areas are shown to have better access, generally, with the exception of the Dakotas which have excellent access throughout the states.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This shows pretty much what I&#x27;d expect: coverage is fine in and around cities and less great in rural areas.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#mobile&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; (The Dakotas are an interesting exception; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.bek.coop&#x2F;&quot;&gt;there&#x27;s a co-op up there&lt;&#x2F;a&gt; that connected a ton of folks with gigabit fiber. Pretty cool!)&lt;&#x2F;p&gt;
&lt;p&gt;Although, remember that these are &lt;em&gt;minimum&lt;&#x2F;em&gt; criteria. When I&#x27;m building software, I&#x27;m mostly doing it on my home internet. A test just now says I get 367&#x2F;71Mpbs. What does the country look like if I were to expect all my users to have similar connections? The map lets us filter for 250&#x2F;25; let&#x27;s look:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;us-national-broadband-map-terrestrial-250-25-2025-06-10.png&quot; alt=&quot;A map of the western US with a color scale overlaid showing access to broadband at a higher level. Rural areas show much less availability than in the previous map, although access in the Dakotas is still excellent.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Cities are pretty much unchanged, but rural coverage gets much worse. (Except again for North Dakota, the reigning champion of rural fiber.)&lt;&#x2F;p&gt;
&lt;p&gt;So what does this tell us about how we should design software? One big takeaway: if you design for the &lt;em&gt;availability&lt;&#x2F;em&gt; of fast internet connections, you&#x27;ll exclude many people in rural areas.&lt;&#x2F;p&gt;
&lt;p&gt;This may or may not be OK for your market—&quot;good internet&quot; tends to be in population centers, and population centers tend to contain more businesses and consumers. You have to make that call!&lt;&#x2F;p&gt;
&lt;p&gt;However, it&#x27;s also worth keeping in mind that this is a map of commercial availability, not market penetration. Hypothetically, you could get the average speed of a US residential internet connection, but the FCC doesn&#x27;t make such a statistic available.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#speed-stat-third-party&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;However, we have another source of data!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;student-internet-access&quot;&gt;Student Internet Access&lt;&#x2F;h2&gt;
&lt;p&gt;The US Department of Education occasionally tracks student internet access. Since classwork and homework have moved more online (especially during the early days of COVID) it&#x27;s useful data for policy-making. The &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.thefreelibrary.com&#x2F;Relationship+Between+Internet+Access+and+Literacy+Among+OECD...-a0795707532&quot;&gt;most recent data I could find is from 2021&lt;&#x2F;a&gt; but it gives us both a baseline for internet availability and some demographic data. Unfortunately, they don&#x27;t track bandwidth, just raw availability. Still useful, though.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the top-line statistics for all students in the US across several versions of the report:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Category&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Available 2019&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Available 2021&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Difference&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Any internet access&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;94.6%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;97.1%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;+2.5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Smartphone-only&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;6.5%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4.5%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-2.0&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No internet Access&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.4%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;2.9%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-2.5&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;In 2021, they say that these statistics cover 66,108,000 students, which means 2.97 million students only had mobile access as of 2021.&lt;&#x2F;p&gt;
&lt;p&gt;Access gets worse, of course, with lower household income. Here&#x27;s how those stats look for the lowest quartile:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Category&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Available 2019&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Available 2021&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Difference&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Any internet access&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;88.6%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;94.4%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;+5.8&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Smartphone-only&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;14.1%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;9.8%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-4.3&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No internet access&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;11.4%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;5.6%&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-5.8&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Despite the fact that these numbers are going down, that&#x27;s still a huge number of people in absolute terms (1.87 million students in this category with only mobile access.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-do-we-do-with-this-information&quot;&gt;What Do We Do With This Information?&lt;&#x2F;h2&gt;
&lt;p&gt;So that&#x27;s a lot of words to say this: despite gains in the last couple of years, it&#x27;s still not safe to assume that every user of your software either has access to stable internet, or is willing and able to pay to get high speeds.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I&#x27;m deliberately not making any moral judgments here. If you think you&#x27;re in a situation where you can ignore this data, I&#x27;m not going to come after you. But if you dismiss it out of hand, you&#x27;re likely going to be putting your users (and business) in a tough spot.&lt;&#x2F;p&gt;
&lt;p&gt;I think it&#x27;s worth considering a couple of scenarios in the parts of your software that someone interacts with regularly:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;What if that person is on a slow link? If you&#x27;ve never had bad internet access, maybe think of this as plane wifi. Rural satellite connections behave very similarly: high latency, speed and (sometimes) low data caps.&lt;&#x2F;li&gt;
&lt;li&gt;What if that person is on a mobile&#x2F;metered&#x2F;capped network? Remember that 4G is like 5&#x2F;1Mbps, and 3G is even worse. Big downloads are probably not a great idea.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This is also a very US-centric view, plus it doesn&#x27;t consider latency from distance between your data center and your user&#x27;s device. Still, though, I think this shows that this problem is real and we should take it into account when designing software.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;satellite&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Despite these drawbacks, satellite has high availability because you just need to have access to power instead of laying cable or building towers. It&#x27;s still the best option for a lot of the rural US.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;my-history&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;I grew up in a semi-rural area (mountains) and this was the only real option. As a kid, I really hated it! The latency was too bad to play games with my friends online, and I also regularly hit our household&#x27;s download cap and got us all throttled to basically-dialup for the rest of the month.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;mobile&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;I&#x27;m not going to embed a screenshot, but mobile coverage looks pretty much the same if you set the same bandwidth requirements. According to the FCC, that&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;5G_NR&quot;&gt;5G-NR&lt;&#x2F;a&gt; at 35&#x2F;3Mbps. 4G has far better coverage in rural areas, but only gets 5&#x2F;1Mbps.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;speed-stat-third-party&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;The ones published by various speed testing companies vary so wildly that I don&#x27;t think they&#x27;re worth paying attention to, even for broad decision-making.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Local-First Foundations</title>
		<published>2025-01-22T00:00:00+00:00</published>
		<updated>2025-01-22T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/local-first-foundations/" type="text/html"/>
		<id>https://bytes.zone/posts/local-first-foundations/</id>
		<content type="html">&lt;p&gt;I&#x27;m writing a book about local-first software tentatively called &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;local-first-from-scratch&#x2F;&quot;&gt;Local-First From Scratch&lt;&#x2F;a&gt;. This is a snippet from that book—an intro to why you should care about local-first software and what it gets you. I thought it ended up being a pretty good summary of what the whole project is about, so I wanted to share it as a blog post as well!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;If you picked up this book, you might have some idea about what &quot;local-first&quot; means&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#lf-manifesto&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;, but let&#x27;s get on the same page: other than &quot;works offline&quot;, what are we aiming for?&lt;&#x2F;p&gt;
&lt;p&gt;In short, local-first software moves ownership of data from &quot;somewhere in the cloud&quot; to your local devices.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;This has a bunch of consequences. We like some of them:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Updates are practically instant because the network does not have to be involved. No loading times or spinners! You can also work completely offline.&lt;&#x2F;li&gt;
&lt;li&gt;You are in control of your data and control who gets access to it.&lt;&#x2F;li&gt;
&lt;li&gt;A company going through an &quot;incredible journey&quot; acquihire doesn&#x27;t mean you lose access to your work.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Of course, &lt;em&gt;implementing&lt;&#x2F;em&gt; these ideas is a little harder than just storing data in a local SQLite database. There&#x27;s a cost paid in complexity and figuring out new ways of doing things. For example, we&#x27;d typically enable sharing by having a central server hold all the state. But we can&#x27;t do that if the user is offline and can&#x27;t reach the server!&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#local-first-business-case&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; So local-first also raises some questions we need to answer:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You probably have more than one device (phone, computer, etc.) Which one is the &quot;real&quot; copy?&lt;&#x2F;li&gt;
&lt;li&gt;If you want to share your work with someone else, how?&lt;&#x2F;li&gt;
&lt;li&gt;When you share—either with other devices or other people—how do you avoid conflicting writes?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;We want satisfying answers to these without giving up local control. The typical approach—and the one we&#x27;ll implement in this book—is CRDTs.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;lf-manifesto&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;If you want to learn more about local-first principles, Ink &amp;amp; Switch&#x27;s essay &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.inkandswitch.com&#x2F;local-first&#x2F;&quot;&gt;&lt;em&gt;Local-first software: You own your data, in spite of the cloud&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; is a great place to keep going. You may hear folks refer to the &quot;local-first manifesto&quot;—this is the thing they&#x27;re talking about.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;local-first-business-case&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;This blows up a lot of SaaS business models that depend on a subscription to a central server. We&#x27;re not going to address the business ramifications of local-first software in this book. If you&#x27;re interested in that, as of early 2025 the Local-first Software Discord hosts frequent online meetups where people share their experiences.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;That&#x27;s it for the preview. Keep an eye out for the book hopefully later in 2025!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>platform engineers work on the meta-product</title>
		<published>2024-09-23T00:00:00+00:00</published>
		<updated>2024-09-23T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/platform-engineers-work-on-the-meta-product/" type="text/html"/>
		<id>https://bytes.zone/posts/platform-engineers-work-on-the-meta-product/</id>
		<content type="html">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;what-is-platform-engineering&#x2F;&quot;&gt;what is platform engineering?&lt;&#x2F;a&gt;, I gave this definition:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Platform engineering, as a discipline, takes a coherent approach to improving an engineering organization&#x27;s output in ways that the rest of the business can see and understand.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;ve had some thoughts and discussions since then, though, and I think that definition needs revising. Specifically, I tried to be very clear in the last post that I didn&#x27;t think platform engineering could&#x2F;should be gatekept. Problem is, that previous definition has some scoping issues—mostly around vagueness—that could work against that goal.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;For one thing, it gives a bad-faith interpretation some gotchas: you could be doing the work of platform engineering, but have someone argue that your approach is not &quot;coherent&quot; or that the business cannot see and understand your work. I gave examples in the last post, but that feels like a bad substitute for the definition itself being clearer.&lt;&#x2F;p&gt;
&lt;p&gt;For another, the language here is too broad: there are differences between SRE, devops, and platform engineering, but this definition does not give you the tools to distinguish between them. To be fair, I think this is minor; this definition gives enough information to get started, or to explain what you&#x27;re doing to someone outside your immediate work. But I also think definitions should allow for clear distinctions, and this one doesn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, I think I see a way around all these problems in two steps. First: who is a platform engineer?&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;A platform engineer is someone who does the work of platform engineering.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you make art, you&#x27;re an artist. If you make music, you&#x27;re a musician. If you do the work of platform engineering, you&#x27;re a platform engineer. That definition resists gatekeeping, which I like. But what is the work?&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Platform engineers work on the internal product that other teams use to make the customer-facing product.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Or more succinctly:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Platform engineers work on the meta-product.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I like a couple things about this. First, this clearly includes the things we care about: CI&#x2F;CD, developer experience, tooling, moving observability tools to where developers can see and use them, etc. If it’s part of the internal product, then it&#x27;s in scope.&lt;&#x2F;p&gt;
&lt;p&gt;Second, it distinguishes between the work of platform engineering and SREs or devops. For example, when I&#x27;ve worked in devops or SRE roles, I&#x27;ve cared a lot about what applications are running where, what kind of traffic loads we&#x27;re expecting, or how much we&#x27;re spending to deliver the product. These are all customer-facing concerns, and might not be in scope for platform engineering.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; The meta-product here would be working on things like adopting Terraform or Kubernetes, which automates a lot of that work and allows teams to ship the product more efficiently.&lt;&#x2F;p&gt;
&lt;p&gt;Now, someone might say &quot;ah, but SREs care a lot about automation! That&#x27;s the meta-product and actually a big part of the gig!&quot; Yes, good, that&#x27;s correct! But check out the new definition: are they working on the meta-product? Yes? Then they&#x27;re doing platform engineering in addition to SREing. That&#x27;s fine; I acknowledge the overlap here. Part of the utility of this definition is that it allows us to recognize this overlap, while still making a distinction. People hired into platform engineering roles may (and probably will) work with SREs on cloud automation projects, but that&#x27;s not their only thing.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s examine this from the dev side as well: at a small company, you probably don&#x27;t have someone specifically doing platform engineering as their whole role (for, say, more than 50% of their time.) But you still need a platform: gotta set up CI and a deployment target, at minimum. Congratulations, you&#x27;re doing platform engineering! Nobody can tell you you’re not.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, in the platform engineering role itself, this serves as a good charter for an individual or team. Where there’s overlap, you can share the load. Where there’s not, it’s clearly your responsibility.&lt;&#x2F;p&gt;
&lt;p&gt;So there you have it: a better definition. This still isn’t perfect, but I think it’s a big improvement over the last one.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;I know about (and have worked on) teams named &quot;platform engineering&quot; who spend most of their time taking care of cloud infrastructure. I think that&#x27;s more a titling issue than anything, and it depends more on the age of the organization than their level of seriousness. Terms vary, people use language differently, it&#x27;s fine.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>what is platform engineering?</title>
		<published>2024-09-09T00:00:00+00:00</published>
		<updated>2024-09-09T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/what-is-platform-engineering/" type="text/html"/>
		<id>https://bytes.zone/posts/what-is-platform-engineering/</id>
		<content type="html">&lt;p&gt;Today I&#x27;m starting my third&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#jobnum&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; platform engineering job. During the interview process, I had a few people ask me what exactly a platform engineer does, and I realized that…&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I&#x27;ve had my own short answer—&quot;a platform engineer finds and reduces friction in the engineering organization&quot;—but that&#x27;s more an example than a proper definition, plus it doesn&#x27;t cover everything.&lt;&#x2F;li&gt;
&lt;li&gt;There are a bunch of confusing&#x2F;contradictory answers floating around the internet if you just search it. This is made worse by vendors who seem to want to gatekeep the term to sell products. (Can you really even &lt;em&gt;be&lt;&#x2F;em&gt; a platform engineer if you&#x27;re not running an IDP???&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#idp&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; Contact our sales team!)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Anyway, I though it&#x27;d be useful to try and sum up the things that platform engineering is&#x2F;does in a succinct definition. &lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt; Here&#x27;s what I came up with. I&#x27;m not saying it&#x27;s perfect, but it&#x27;s helped me understand my role better:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Platform engineering, as a discipline, takes a coherent approach to improving an engineering organization&#x27;s output in ways that the rest of the business can see and understand.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Breaking some of that down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;an coherent approach:&lt;&#x2F;strong&gt; The thing that sets platform engineering apart is that it focuses on making an integrated whole out of processes that are often otherwise approached in a piecemeal (or incoherent) way. (You could also say &quot;holistic&quot; or &quot;integrative&quot; or &quot;product-minded approach.&quot;)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;improving an engineering organization&#x27;s output:&lt;&#x2F;strong&gt; By focusing on output, platform engineering takes a fairly wide area of responsibility. Improvement can mean a bunch of different things, as can output.
&lt;ul&gt;
&lt;li&gt;Some examples: improving p99 response times, increasing test coverage, decreasing CI pipeline runtime, coordinating penetration tests, consolidating docs, finding new project management tools, auditing and classifying runtime exceptions, and training teams in new tools and methodologies. (As a matter of fact, I&#x27;ve done all of those at some point or another as a platform engineer.)&lt;&#x2F;li&gt;
&lt;li&gt;As a counterexample, platform engineers typically do not work on the &quot;people&quot; side of the engineering organization. For example, we don&#x27;t train managers or consult with teams on the best ways to work together. But we &lt;em&gt;do&lt;&#x2F;em&gt; conduct ourselves with the human side in mind.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;in ways the rest of the business can see and understand:&lt;&#x2F;strong&gt; to be effective, platform engineers need to be able to talk about our work in ways that the rest of the business can buy into. For example, this might mean being able to talk about both the system&#x27;s ability to handle increases in transaction volume and what that means for scaling runway. Usually this means quantifying achievements, but it also may mean being able to talk about, say, the impacts of developer experience on voluntary attrition.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To put it another way, platform engineers work &lt;em&gt;on&lt;&#x2F;em&gt; the engineering organization instead of just &lt;em&gt;in&lt;&#x2F;em&gt; it, at least to some degree. They can&#x27;t spend 100% of their time here though: platform engineering needs to have a solid idea of the day-to-day challenges and pains the organization experiences in order to do an effective job.&lt;&#x2F;p&gt;
&lt;p&gt;This also isn&#x27;t work that can be gate-kept, no matter how much a vendor might want to. You need to do at least some of this work to have a functioning software business in the first place. For example, how does your code make it to production?&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#devops&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; How are builds and tests run? Engineers need to do this work regardless of if they have someone specifically focused on platform engineering, but it might be at the cost of not focusing on the things that they excel at.&lt;&#x2F;p&gt;
&lt;p&gt;You sometimes hear people talking about this with the terms &quot;inner loop&quot; vs &quot;outer loop.&quot; Time spent on the primary process of improving the product (coding, writing tests, etc) is the inner loop, and time spent on stuff like deployments or requirement definition is the outer loop. The value in hiring someone to do platform engineering for your business is that they can work on minimizing the time everybody else has to spend in the outer loop.&lt;&#x2F;p&gt;
&lt;p&gt;There are a couple other interesting things that fall out of this definition too. For example, it explains why platform engineering tends to be the interface between product engineering and security or QA (because they&#x27;re responsible for output and coherence) and why platform engineers may be later hires at companies (because smaller companies may have more visibility into cross-departmental processes.)&lt;&#x2F;p&gt;
&lt;p&gt;At any rate, I found this definition helpful and I hope you do too!&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;jobnum&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Third-ish, anyway. Maybe fourth, as the eldest of those was a devops-ish role focused entirely on building a deployable PaaS on top of Apache Mesos. At any rate, at least two of the four jobs predate the term &quot;platform engineering&quot; as we know it now by a couple of years. The principles here have been relevant in all those positions, though!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;idp&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Integrated developer platform, basically an integrated portal to an organization&#x27;s docs, best practices, deployment patterns, service catalog, etc.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;devops&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;This hints at the fact that platform engineers, devops, and SREs have very closely related responsibilities. I&#x27;d say the defining difference is what work each would consider &quot;the inner loop.&quot; DevOps and SREs I&#x27;ve worked with have been primarily concerned with safely delivering and running software while platform engineers I&#x27;ve worked with tend to focus more on developer productivity and &quot;shift left&quot; work. This has not been globally true, though.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Kratky in the basement</title>
		<published>2024-04-01T00:00:00+00:00</published>
		<updated>2024-04-01T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/kratky-in-the-basement/" type="text/html"/>
		<id>https://bytes.zone/posts/kratky-in-the-basement/</id>
		<content type="html">&lt;p&gt;&lt;em&gt;Hello hello! Today is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.aprilcools.club&#x2F;&quot;&gt;April Cools&lt;&#x2F;a&gt;, which is like April Fools but for posting stuff outside what you normally write about instead of unfunny jokes. Hope you enjoy!&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I want to eat more salads, but:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Viral and bacterial outbreaks come up pretty regularly in the US food system. I don&#x27;t feel super paranoid about produce where you can wash or avoid eating the skin, but I feel worse in cases where usually you eat the whole plant.&lt;&#x2F;li&gt;
&lt;li&gt;Large-scale farming techniques sometimes seem pretty dubious, even in products labeled as organic.&lt;&#x2F;li&gt;
&lt;li&gt;Because of how our food system is set up, produce is regularly transported hundreds of miles before being consumed.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Luckily for me, doing hydroponic gardening at home in my basement is a way around this:&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;ol&gt;
&lt;li&gt;I won&#x27;t get listeria cross-contamination from pig farms or whatever—there are no pigs in my basement!&lt;&#x2F;li&gt;
&lt;li&gt;Growing indoors means I have precise control over the environment the plants are growing in. There is no need for pesticide or herbicide. (And I have lots of choice in what nutrients I use.)&lt;&#x2F;li&gt;
&lt;li&gt;I can walk downstairs to harvest lettuce which avoids all the waste and carbon emissions from transportation, plus the produce could literally not be fresher.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Plus I think hydroponics are cool,&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; which is another helpful motivation!&lt;&#x2F;p&gt;
&lt;p&gt;Problem is, I&#x27;ve always overcomplicated things… for example, I&#x27;ve drawn up plans to turn the entire house into a hydroponic garden, feed the whole neighborhood, start a local plastic recycling and hydroponic produce market, etc. You get the idea.&lt;&#x2F;p&gt;
&lt;p&gt;So earlier this year, I decided to commit to doing something as small as I could: get at least one salad on a plate before the end of March. And I did it! Here&#x27;s how.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hydroponics-crash-course&quot;&gt;Hydroponics Crash Course&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m still a beginner and I&#x27;ll get some of this wrong for sure, but before I can explain how my setup works I need to share a little theory. Plants need like 4 things&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; to thrive:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Water&lt;&#x2F;li&gt;
&lt;li&gt;Bioavailable nutrients&lt;&#x2F;li&gt;
&lt;li&gt;Light&lt;&#x2F;li&gt;
&lt;li&gt;Air&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;First up, water and nutrients are taken together in hydroponics. Mixing the right hydroponic nutrient solution could be a long post all by itself, so I&#x27;ll just sum it up by sharing some details that I was confused by at first:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Nutrient formulations are marked with three-number value like 10-10-10 or 8-15-36. Those values are nitrogen-phosphorous-potassium, or NPK. As I understand it, they&#x27;re percentages: a 10-10-10 mix has 10% of each. That&#x27;s a good starter mix for lettuce, but I&#x27;ve read that 8-15-36 will also work.&lt;&#x2F;li&gt;
&lt;li&gt;You measure pH and electrical conductivity in the nutrient solution.
&lt;ul&gt;
&lt;li&gt;You need to care about pH because if it&#x27;s too high or low then plants can&#x27;t actually absorb the nutrients from the water.&lt;&#x2F;li&gt;
&lt;li&gt;You need to care about electrical conductivity because it&#x27;s a good proxy for how much total nutrient is in the solution (since saltier water is more electrically conductive.) Too much and you&#x27;ll get &quot;nutrient burn&quot; where the edges of leaves turn brown. Too little and the plant just won&#x27;t grow correctly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Light is usually a cost&#x2F;value tradeoff. Good grow lights are expensive, and smaller ones usually have a price premium due to home marijuana growers in the US market.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; I bought mine from &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;spider-farmer.com&#x2F;&quot;&gt;spider-farmer.com&lt;&#x2F;a&gt; (got a pair of SF300s for $99 on sale, $135 normal price.) There is not a lot to think about if you&#x27;re willing to accept this as the biggest cost of the initial setup, but the most important thing is that you have to have a full-spectrum light: plants grow best in reds and blues, basically (which makes sense if you think about it; leaves are green because they&#x27;re reflecting those wavelengths instead of absorbing them for photosynthesis.)&lt;&#x2F;p&gt;
&lt;p&gt;That leaves air, which different systems adapt to in different ways. The basic problem is that plants can drown if you submerge their roots in water that isn&#x27;t properly aerated. This is a problem in soil gardening too; it&#x27;s why you need to ensure good drainage in your planting beds. In most systems I was looking at, there was some level of circulation or aeration which required a bunch of pumps and air stones. But it turns out that you don&#x27;t need all that at a small scale: you can just use the Kratky method.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-kratky-method&quot;&gt;The Kratky Method&lt;&#x2F;h3&gt;
&lt;p&gt;So that all leads to Kratky: You basically take a bucket, make sure the plants can get their roots wet, and just let them grow. Plants will grow regular roots down into the reservoir, and send off air roots (they&#x27;re hairier and shorter than the ones that absorb water) sideways above the waterline as the water evaporates.&lt;&#x2F;p&gt;
&lt;p&gt;The big benefit is that you don&#x27;t need pumps, air stones, or any powered components aside from a light (and if you do it outside, you don&#x27;t even need that.) That kind of simplicity is what I was after!&lt;&#x2F;p&gt;
&lt;p&gt;The big drawback is that the nutrient concentration will go up over time as the water evaporates, so you need to start lower. For lettuce, I&#x27;ve read that starting at half-strength nutrient concentration is fine. You also will probably not be able to cycle new plants into the system, as their roots will not be able to reach the lower water levels.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;my-setup&quot;&gt;My Setup&lt;&#x2F;h2&gt;
&lt;p&gt;So here&#x27;s how I put all that theory into practice. As a reminder, I wanted to get a single salad out of a system that was as simple as I could possibly make it.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how things looked at the start of the grow. I only needed one light for six plants (which I calculated based on how much room a typical lettuce plant needs to grow.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kratky-in-the-basement&#x2F;kratky-27gal-20231226.jpeg&quot; alt=&quot;a photo of six seedling plants with 1 to 3 small leaves each in individual holes in the top of a yellow 27-gallon bin underneath a grow light&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what it looked like near the middle of the grow below the waterline. You can see the air roots coming off the sides of the longer roots!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kratky-in-the-basement&#x2F;kratky-27gal-20220122-roots.jpeg&quot; alt=&quot;a photo of a tray of six plants being lifted out of the the nutrient solution to show their roots. Most of the roots are long and slender, but near the tops they look hairy: those are the air roots.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then finally, here&#x27;s how the plants looked near the end of the grow about 45 days in:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;kratky-in-the-basement&#x2F;kratky-27gal-20240219.jpeg&quot; alt=&quot;a photo of the same six plants underneath a grow light, but with large healthy leaves.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;They stayed at this stage for about another month while we used all the plants in salads. We did that slowly at first, because we weren&#x27;t 100% sure if they were actually safe to eat, but we sped up pretty quickly as we didn&#x27;t see any ill effects. I wish we had eaten them faster, though: the nutrient concentration went up in the reservoir and most of the plants ended up with nutrient burn that I had to trim off before eating.&lt;&#x2F;p&gt;
&lt;p&gt;The easiest way I found to harvest the plants was to cut them off at the stem with a sharp knife, then chop them the same as I would a grocery store lettuce. I plucked all the leaves off one as an experiment, though: it took me a lot longer, but then the plant was able to regrow most of its leaves for a second harvest. If it was easier to change the water in this system, I might do that more.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, here&#x27;s the shopping list if you wanted to replicate something like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;A reservoir. I used a 27 gallon (~100 liter) tote with holes drilled in the lid.&lt;&#x2F;li&gt;
&lt;li&gt;Net cups and grow media. I used some standard net cups off of amazon with some rockwool that I got from a local hydroponics shop.&lt;&#x2F;li&gt;
&lt;li&gt;Seeds.&lt;&#x2F;li&gt;
&lt;li&gt;Lights. I used Spider Farmer SF300s, which I plan to use for future grows as well. Link above.&lt;&#x2F;li&gt;
&lt;li&gt;Nutrient solution. I don&#x27;t have a strong recommendation here—I bought some vegetative growth nutrient mix from the hydroponics shop. It&#x27;s clearly designed for the vegetative phase of marijuana, but it has about the right NPK and it worked fine. I monitored the pH and conductivity for the first 30 days or so, but stopped once it stabilized.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I want to make some improvements already, though:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I&#x27;m going to switch to 5 gallon (18 liter) buckets for future grows. 25 gallons of water weighs about 200 pounds (~90kg), and the tote isn&#x27;t really set up for that much weight. Using small buckets would let me run different experiments with pH and nutrient composition as well.&lt;&#x2F;li&gt;
&lt;li&gt;I did the grow in my basement. That meant a pretty constant temperature of about 60°F (15.5°C.) It might be worth getting some kind of grow tent with a small heater to raise the temperature.&lt;&#x2F;li&gt;
&lt;li&gt;My hydroponics setups have a fan blowing on the plants to stimulate them in this kind of environment. I think I ought to try that!&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m not sold on rockwool as a grow medium—I found out while writing this post that I have not been sufficiently careful about handling it! I have coco pellets too, but I&#x27;m not sure how to start seeds in that.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So that&#x27;s where I&#x27;m at on hydroponics! I&#x27;m really excited to improve on this, but I&#x27;m already pretty happy with where things are right now.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;If you don&#x27;t think hydroponics are cool, that&#x27;s fine. But if the other stuff in this list resonates with you, consider looking up local farmers markets or produce co-ops, which address some of these problems in similar ways.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Ok, so there&#x27;s actually a bunch more here. Plants need the right temperature, humidity, protection from insects or animals, a place to put down roots, etc. But you need to solve for these four to even get started.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;I call it the &quot;weed tax&quot;, although I can&#x27;t recall if I read that or came up with it.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>sourcing secrets from 1Password</title>
		<published>2024-02-27T00:00:00+00:00</published>
		<updated>2024-02-27T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/sourcing-secrets-from-opw/" type="text/html"/>
		<id>https://bytes.zone/posts/sourcing-secrets-from-opw/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;thing-a-month-meta&#x2F;&quot;&gt;setting up a personal Kubernetes cluster&lt;&#x2F;a&gt; recently.
The whole thing is set up with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;argoproj.github.io&#x2F;&quot;&gt;Argo CD&lt;&#x2F;a&gt;, which basically means that I make changes to the cluster by committing to a git repo and pushing.
Really handy, especially when I make a mistake and need to clean something up or revert.&lt;&#x2F;p&gt;
&lt;p&gt;One problem with this, though, is that it&#x27;s probably not a great idea to commit secrets to the repository.
I don&#x27;t want things like passwords and API keys sitting there in clear text on my laptop or on the remote.&lt;&#x2F;p&gt;
&lt;p&gt;If I were setting up a cluster that other people were going to operate, I&#x27;d probably set up something like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.vaultproject.io&#x2F;&quot;&gt;Vault&lt;&#x2F;a&gt;, but since it&#x27;s just me I can get away with creating the secrets by hand.
That said, I still don&#x27;t want to leave them on disk unencrypted!&lt;&#x2F;p&gt;
&lt;p&gt;Enter the &lt;code&gt;opw&lt;&#x2F;code&gt; CLI for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;1password.com&#x2F;&quot;&gt;1Password&lt;&#x2F;a&gt;. &lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
It can output secret data as JSON, and I can use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jqlang.github.io&#x2F;jq&#x2F;&quot;&gt;&lt;code&gt;jq&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to transform that into Kubernetes secret objects, which I can pipe into &lt;code&gt;kubectl&lt;&#x2F;code&gt; to apply in the cluster.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a simple example: an image pull secret.
I have the secret stored in 1Password under a specific vault.
If I run &lt;code&gt;op item get --vault k8s &quot;image pull secret&quot;&lt;&#x2F;code&gt;, I&#x27;ll get something like the following:&lt;&#x2F;p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;ID&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;          y63hsk7qy5osug5n2qupwhtz3e&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Title&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;       image pull secret&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Vault&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;       k8s (2qaqjigknr3htch273i2mho5vi)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Created&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;     2 weeks ago&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Updated&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;     2 weeks ago by Brian Hicks&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Favorite&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Version&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;     1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Category&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    LOGIN&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;Fields&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;  password&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    password-goes-here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;  username&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    username-goes-here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I can also ask for it in JSON format by adding &lt;code&gt;--format json&lt;&#x2F;code&gt; to the end:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;y63hsk7qy5osug5n2qupwhtz3e&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;title&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;image pull secreet&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;version&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;vault&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;2qaqjigknr3htch273i2mho5vi&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;k8s&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;category&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;LOGIN&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;last_edited_by&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;B76OR7I6DNENPAQOQFFO6FKQOM&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;created_at&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;2024-02-12T18:37:38Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;updated_at&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;2024-02-12T18:37:38Z&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;additional_information&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;brian@brianthicks.com&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;fields&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;CONCEALED&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;purpose&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;PASSWORD&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;label&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;password-goes-here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;reference&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;op:&#x2F;&#x2F;k8s&#x2F;image pull secret&#x2F;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;password_details&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;        &amp;quot;strength&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;FANTASTIC&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;STRING&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;purpose&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;USERNAME&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;label&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;username-goes-here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;reference&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;op:&#x2F;&#x2F;k8s&#x2F;image pull secret&#x2F;username&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;notesPlain&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;STRING&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;purpose&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;NOTES&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;label&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;notesPlain&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;reference&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;op:&#x2F;&#x2F;k8s&#x2F;image pull secret&#x2F;notesPlain&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can then pipe that into the following jq program to get an object indexed by the field names:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.fields | map({ key: .label, value: .value }) | from_entries&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That produces this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;password&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;password-goes-here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;username&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;username-goes-here&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  &amp;quot;notesPlain&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; null&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we can add onto that jq program to produce some JSON in the shape of a Kubernetes secret:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.fields | map({ key: .label, value: .value }) | from_entries | {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    apiVersion: &amp;quot;v1&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    kind: &amp;quot;Secret&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    type: &amp;quot;kubernetes.io&#x2F;dockerconfigjson&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    metadata: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        name: &amp;quot;ghcr-image-pull-secret&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    data: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;quot;.dockerconfigjson&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            auths: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                &amp;quot;image-host.io&amp;quot;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    auth: &amp;quot;\(.username):\(.password)&amp;quot; | @base64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        } | @base64&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Assuming that&#x27;s in &lt;code&gt;image-pull-secret.jq&lt;&#x2F;code&gt;, we can now load the secret into Kubernetes like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;op&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; item get&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --vault&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; k8s &amp;quot;image pull secret&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --format&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    jq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; image-pull-secret.jq&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    kubectl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; apply&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; whatever-namespace&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; -&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Like everything, this has some tradeoffs: the secrets are stored securely and never hit the disk, which is &lt;em&gt;great&lt;&#x2F;em&gt;!
But in exchange, I have to run a command manually if I want to update the secrets.
This means that if I&#x27;m creating a new app, it&#x27;ll probably fail at least once.
Same if I have to move my cluster (&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;micro&#x2F;thing-a-month-02-05&#x2F;&quot;&gt;which I already did once&lt;&#x2F;a&gt;) and have to apply all the manifests from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;So in the end, would I recommend this?
Yes, if you&#x27;re in my situation!
But if you&#x27;re running a cluster where other people have to operate it too, this is probably not the best approach.
It&#x27;s good to know it&#x27;s possible, though!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>modeling CRDTs in Alloy - counters</title>
		<published>2023-11-27T00:00:00+00:00</published>
		<updated>2023-11-27T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-crdts-in-alloy-counters/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-crdts-in-alloy-counters/</id>
		<content type="html">&lt;p&gt;Hey, welcome back! &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-crdts-in-alloy-introduction-and-the-importance-of-idempotence&#x2F;&quot;&gt;Last time, we introduced conflict-free replicated datatypes (CRDTs) and modeled them in Alloy by using boolean &lt;code&gt;OR&lt;&#x2F;code&gt; as our merge function&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As a quick recap, CRDTs give you eventual consistency, no matter how out of sync the data originally was. This makes them great for local-first or networked multiplayer applications. If you can write a &lt;code&gt;merge&lt;&#x2F;code&gt; function for a data structure that is commutative, associative, and idempotent, you might be able to build a CRDT on top of it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Further, we&#x27;re modeling all these semantics in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.org&quot;&gt;Alloy&lt;&#x2F;a&gt;, a lightweight formal methods tool good for modeling and analyzing data structures. We&#x27;ll see some cool stuff it can do in a second.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;counting-birds&quot;&gt;Counting Birds&lt;&#x2F;h2&gt;
&lt;p&gt;Last time our document was a single boolean. That&#x27;s useful, but we can&#x27;t build an application on top of it, so let&#x27;s keep moving. To get much further, we&#x27;re going to have to figure out how to sync numbers back and forth. Let&#x27;s use the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.birdcount.org&#x2F;&quot;&gt;the Great Backyard Bird Count&lt;&#x2F;a&gt; as an example. Say we&#x27;re counting &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.allaboutbirds.org&#x2F;guide&#x2F;Red-tailed_Hawk&#x2F;id&quot;&gt;red-tailed hawks&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Everyone starts at zero.&lt;&#x2F;li&gt;
&lt;li&gt;Person A sees a hawk! Ka-kaw! They increment the counter by one.&lt;&#x2F;li&gt;
&lt;li&gt;Person B sees another hawk! Screeee! They increment the counter by one, too.&lt;&#x2F;li&gt;
&lt;li&gt;Person C sees &lt;em&gt;two&lt;&#x2F;em&gt; hawks. Wow! Of course, they increment the counter by two.&lt;&#x2F;li&gt;
&lt;li&gt;Day&#x27;s over; good job everyone! Sync up your apps and lets see how many hawks we saw total.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;At first glance, we might want to use &lt;code&gt;+&lt;&#x2F;code&gt; as the merge function. But remember, that&#x27;s not idempotent. So this might happen among three people:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The group has seen four hawks total. Person A and person B have seen 1 each, person C has seen 2. Nobody has talked to anyone else.&lt;&#x2F;li&gt;
&lt;li&gt;Person A and person B sync and set both their totals to &lt;code&gt;1 + 1 = 2&lt;&#x2F;code&gt; (one from each, the initial count)&lt;&#x2F;li&gt;
&lt;li&gt;Person A and person C sync and set both their totals to &lt;code&gt;2 + 2 = 4&lt;&#x2F;code&gt; (two from A because of the previous sync)&lt;&#x2F;li&gt;
&lt;li&gt;Person B and person C sync and set both their totals to &lt;code&gt;2 + 4 = 6&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Further syncs happen, and the result increases every time.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In other words, we can return with a huge number of hawks when really we only saw four. That&#x27;s not what we wanted! This fails because &lt;code&gt;+&lt;&#x2F;code&gt; is not idempotent: adding any meaningful number will always change the result.&lt;&#x2F;p&gt;
&lt;p&gt;What if we used &lt;code&gt;max&lt;&#x2F;code&gt; instead? It&#x27;s commutative, associative, and idempotent, but the semantics don&#x27;t work out here: if we used &lt;code&gt;max&lt;&#x2F;code&gt; to merge, we&#x27;d only get to see the max of hawks that any &lt;em&gt;one person&lt;&#x2F;em&gt; had seen.&lt;&#x2F;p&gt;
&lt;p&gt;However, &lt;code&gt;max&lt;&#x2F;code&gt; would work if we synced one counter for each person and then added them together locally to get the result! Let&#x27;s look at an example:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Just like before, the group has seen four hawks total. Person A and person B have seen 1 each, person C has seen 2. Nobody has talked to anyone else.&lt;&#x2F;li&gt;
&lt;li&gt;Person A and person B sync their totals, going from person A&#x27;s &lt;code&gt;{a: 1}&lt;&#x2F;code&gt; and person B&#x27;s &lt;code&gt;{b: 1}&lt;&#x2F;code&gt; to &lt;code&gt;{a: 1, b: 1}&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Person A and person C sync their totals, going from person A&#x27;s &lt;code&gt;{a: 1, b: 1}&lt;&#x2F;code&gt; and person C&#x27;s &lt;code&gt;{c: 2}&lt;&#x2F;code&gt; to &lt;code&gt;{a: 1, b: 2, c: 2}&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Person B and person C sync their totals, finishing the sync. Now everyone has &lt;code&gt;{a: 1, b: 1, c: 2}&lt;&#x2F;code&gt; and anyone can compute that we&#x27;ve seen four hawks total.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;When you have a conflicting value (say B saw another hawk later) you take the &lt;code&gt;max&lt;&#x2F;code&gt; of &lt;em&gt;that person&#x27;s&lt;&#x2F;em&gt; count when syncing.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This also has the nice property of moving information through the system without requiring everyone to talk to everyone else. Consider what would happen if we did A+B, A+C, A+B instead of ending with B+C. We&#x27;d end up with the same value, since A+C would have given A the count to pass along to B, except that B and C never have to talk directly. It&#x27;s eventually consistent!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;moving-to-alloy&quot;&gt;Moving to Alloy&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s model this scheme in Alloy. We&#x27;ll model an ID to represent each individual birdwatcher:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; ID&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll also model a &lt;code&gt;Document&lt;&#x2F;code&gt; to allow each person to keep their count and the count of each of their peers:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Document&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; ID &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt; lone&lt;&#x2F;span&gt;&lt;span&gt; Natural&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;A -&amp;gt; lone B&lt;&#x2F;code&gt; means &quot;each A maps to at most one B.&quot; In other words, it makes &lt;code&gt;ID&lt;&#x2F;code&gt; unique in &lt;code&gt;counts&lt;&#x2F;code&gt; for each &lt;code&gt;Document&lt;&#x2F;code&gt; (the way you&#x27;d expect a dictionary to work in an implementation.)&lt;&#x2F;p&gt;
&lt;p&gt;We have an error already, though. When I run this, Alloy gives me and edge case where we use the same ID for multiple documents:&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;duplicate-ids-in-counter-document.png&quot; alt=&quot;An Alloy instance showing two documents using the same ID.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If this happened we&#x27;d lose data since we couldn&#x27;t distinguish between values we needed to add and those we needed to &lt;code&gt;max&lt;&#x2F;code&gt;. Better disallow that in the model:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;no&lt;&#x2F;span&gt;&lt;span&gt; two documents can have the same ID&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; &amp;quot;disj&amp;quot; means d1 and d2 can&amp;#39;t be the same document&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no disj&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;As always, adding a &lt;code&gt;fact&lt;&#x2F;code&gt; weakens our spec by making assumptions. So let&#x27;s think: can we actually get rid of ID collisions when we implement this spec? Well, maybe… in real life, we&#x27;d use some ID scheme that we were reasonably sure would never collide. In production CRDTs, it seems like this means using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Universally_unique_identifier&quot;&gt;UUIDs&lt;&#x2F;a&gt; a lot of the time. Good enough for me; let&#x27;s move on.&lt;&#x2F;p&gt;
&lt;p&gt;With this change, we can get some more interesting instances of the data structure. For example, here&#x27;s what it could look like when these &lt;code&gt;counts&lt;&#x2F;code&gt; maps are filled out:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;two-counters-with-conflicting-counts.png&quot; alt=&quot;An Alloy instance showing two documents with various counts in need of syncing.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;(You don&#x27;t need to know how to read this example, but if you want to: the keys in the map are represented by the labels on the arrows. For example, &quot;Document 0 has a count for ID 1 of 0&quot;.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;merging&quot;&gt;Merging&lt;&#x2F;h2&gt;
&lt;p&gt;We don&#x27;t have a way to sync yet, so let&#x27;s start fixing that. Let&#x27;s define &lt;code&gt;merge&lt;&#x2F;code&gt;, as well as adding checks that it satisfies our three laws:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; ID &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt; lone&lt;&#x2F;span&gt;&lt;span&gt; Natural]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; (ID &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt; lone&lt;&#x2F;span&gt;&lt;span&gt; Natural) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; intent: set each value in the document to the maximum value&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; of that key in all the documents we&amp;#39;re currently merging.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; allCounts &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;d2 &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    { id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; count&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Natural &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; allCounts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;        and&lt;&#x2F;span&gt;&lt;span&gt; count &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;&#x2F;max&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;allCounts&lt;&#x2F;span&gt;&lt;span&gt;[id]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsCommutative&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsAssociative&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    merge&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsIdempotent&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alloy says it can&#x27;t find a counterexample for these three checks, so it looks like we might be in business.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;actions&quot;&gt;Actions&lt;&#x2F;h2&gt;
&lt;p&gt;Next let&#x27;s set up some things our documents can do. First, we should be able to increment a number in a document by removing the old count for this document and ID and adding the new one:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; increment&lt;&#x2F;span&gt;&lt;span&gt;[d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; current &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;counts&lt;&#x2F;span&gt;&lt;span&gt;[d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    counts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      counts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      -&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;current&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      +&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;bounded_inc&lt;&#x2F;span&gt;&lt;span&gt;[current]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;About all the arrows: &lt;code&gt;a-&amp;gt;b-&amp;gt;c&lt;&#x2F;code&gt; creates a three-valued relation (you can think of it like a 3-tuple if it helps: &lt;code&gt;(a, b, c)&lt;&#x2F;code&gt;.) Since &lt;code&gt;counts&lt;&#x2F;code&gt; is actually a mapping of document-to-id-to-natural, we need this level of precision. However, this is the only place we&#x27;ll this since writing &lt;code&gt;d.counts&lt;&#x2F;code&gt; will pop the &lt;code&gt;Document&lt;&#x2F;code&gt; off the left-hand side of the relation, leaving us with &lt;code&gt;ID -&amp;gt; Natural&lt;&#x2F;code&gt; (as in our &lt;code&gt;sig&lt;&#x2F;code&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;I had to write a &lt;code&gt;bounded_inc&lt;&#x2F;code&gt; helper function to write &lt;code&gt;increment&lt;&#x2F;code&gt;. Here it is:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; bounded_inc&lt;&#x2F;span&gt;&lt;span&gt;[n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Natural]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Natural {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  n &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;&#x2F;last&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; implies&lt;&#x2F;span&gt;&lt;span&gt; nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;&#x2F;last&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; nat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;&#x2F;inc&lt;&#x2F;span&gt;&lt;span&gt;[n]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This was necessary because Alloy&#x27;s numbers are actually just regular &lt;code&gt;sig&lt;&#x2F;code&gt;s with an ordering.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Since we only deal with a bounded number of instances for each sig, addition and counting has a gotcha. Say you only knew the numbers 1 and 2: counting works fine up to a certain point, but if someone asked you &quot;ok, and what&#x27;s after two?&quot; you wouldn&#x27;t know. This is exactly what&#x27;s going on with Alloy, which will return a blank set if you run out of numbers. Specs can define semantics specific to their applications, though, and in ours I&#x27;ve decided that incrementing past the max is simply not allowed—a number will just stop growing a certain point.&lt;&#x2F;p&gt;
&lt;p&gt;This should not really affect the soundness of our spec, but choosing limits like this requires careful thought. Alloy is not designed for numerical computing, and forcing it means dealing with weird edge cases like this. Fortunately, that&#x27;s the only place in the spec we should have to deal with this.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;now-we-can-sync&quot;&gt;Now We Can Sync!&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s move on to &lt;code&gt;sync&lt;&#x2F;code&gt;, which should merge two documents:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; merged &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;counts] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    counts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; counts &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt; (d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;merged &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;merged)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next the little bit of boilerplate that we need to treat these as actions:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; intent: set every document to 0 for its own ID&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  counts &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; { d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; ID&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; count&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Zero &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;id &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; id }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;some&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; increment&lt;&#x2F;span&gt;&lt;span&gt;[d])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;some&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, let&#x27;s check idempotence &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-crdts-in-alloy-introduction-and-the-importance-of-idempotence&#x2F;&quot;&gt;like last time&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SyncIsIdempotent&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2]; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2]) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; counts&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; counts&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alloy can&#x27;t find a counterexample for this, so I think we&#x27;re done! These documents should work as CRDTs! In fact, our counters here model something called a &quot;GCounter&quot; (a &quot;grow-only&quot; counter.) There are plenty of implementations out there if you want search for that term and find some real code to read!&lt;&#x2F;p&gt;
&lt;p&gt;But… GCounters can only count up. That&#x27;s a problem, right? You might think &quot;ah, we can get around that—each node could set its own count to whatever it wanted!&quot; But we&#x27;d hit problems there on the first sync: since our &lt;code&gt;merge&lt;&#x2F;code&gt; function uses &lt;code&gt;max&lt;&#x2F;code&gt;, any time a node&#x27;s counter decremented it&#x27;d go back up to the highest-seen value on the next sync.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a nice way out of this predicament, though: use two counters per document, &lt;code&gt;positive&lt;&#x2F;code&gt; and &lt;code&gt;negative&lt;&#x2F;code&gt;. Both counters always go up (so &lt;code&gt;max&lt;&#x2F;code&gt; doesn&#x27;t give us trouble) and when you need to get the value out of the document, you sum up all the values of both counters, then get the final value from &lt;code&gt;positive - negative&lt;&#x2F;code&gt;. Ta-da! The final can now go up and down, and you have something called a &quot;PNCounter&quot; (a &quot;positive&#x2F;negative&quot; counter.)&lt;&#x2F;p&gt;
&lt;p&gt;You might also look at this and think &quot;yikes, that looks like a lot of memory for a single number… that would just be a single integer if this wasn&#x27;t distributed.&quot; Yep! There&#x27;s always going to be some overhead for syncing. You can minimize it, though, and people who make production-class CRDTs (like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.inkandswitch.com&#x2F;&quot;&gt;Ink and Switch&lt;&#x2F;a&gt;) clearly think about data compression a lot! In fact, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jakelazaroff.com&#x2F;words&#x2F;making-crdts-98-percent-more-efficient&#x2F;&quot;&gt;Jake Lazaroff has written an excellent blog post about doing this in an image-based CRDT&lt;&#x2F;a&gt;. Go read that next!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.discourse.group&#x2F;t&#x2F;merging-two-sets-of-relations-by-some-rule&#x2F;407&quot;&gt;Alcino Cunha on the Alloy forums for helping me understand how set comprehensions could be used for &lt;code&gt;merge&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, as well as pointing out that it should be possible to specify our checks with only events. Thanks also to Jake Lazaroff for reviewing an early draft and suggesting better ways to structure this.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;This only works if you can trust the people doing the counts, though. If person B says &quot;wow, I heard person A saw FIFTY HAWKS&quot; then that information will spread without any recourse for A to say &quot;er, no, I only saw one.&quot; These kind of security and sharing situations seems to be open questions in the field. I guess you could sign values, but that&#x27;d add a lot of overhead in any decently-sized document. Tricky!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Images throughout this post are generated by Alloy—that&#x27;s one of the things I like best about it. Visualizing edge cases like this makes it really easy to talk about potential drawbacks to modeling with other engineers or people making product decisions.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;That is, the numbers you work with &lt;em&gt;in sigs&lt;&#x2F;em&gt;. You can get the cardinality of a set (e.g. &lt;code&gt;#Document&lt;&#x2F;code&gt;) without these restrictions. However, you can&#x27;t store the cardinality in a &lt;code&gt;sig&lt;&#x2F;code&gt;, so we still have to work around this restriction.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;4&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Briefly: you can make a GSet (grow-only set) whose &lt;code&gt;merge&lt;&#x2F;code&gt; is the union of two sets. That gives you the set equivalent of our GCounter—it can only add new items. To remove items, you do the same trick as PNCounter and keep two sets, then get the final value by computing &lt;code&gt;union(add) - union(remove)&lt;&#x2F;code&gt;. This means that you can never re-add an item that you&#x27;ve removed, though. We&#x27;ll touch on how to get around that next time.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>sticky table headers with bottom borders</title>
		<published>2023-11-13T00:00:00+00:00</published>
		<updated>2023-11-13T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/sticky-table-headers-with-bottom-borders/" type="text/html"/>
		<id>https://bytes.zone/posts/sticky-table-headers-with-bottom-borders/</id>
		<content type="html">&lt;p&gt;Say you have a table like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;thead&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Column 1&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Column 2&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;Column 3&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;th&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;tr&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;thead&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;tbody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &amp;lt;!-- ... --&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;tbody&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You might try to separate the header from the body like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.fancy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt; thead&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt; tr&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  border-bottom&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; solid #ccc&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That works, but what if you want the &lt;code&gt;&amp;lt;thead&amp;gt;&lt;&#x2F;code&gt; element to have &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;CSS&#x2F;position#values&quot;&gt;&lt;code&gt;position: sticky&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; too? If you used a border, the border will keep scrolling while the header sticks. Oh no!&lt;&#x2F;p&gt;
&lt;p&gt;I got around this by using &lt;code&gt;box-shadow&lt;&#x2F;code&gt; instead. It works, despite feeling a little hacky:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.fancy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt; thead&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt; tr&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  box-shadow&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; inset 0&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; #ccc&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Except that Safari won&#x27;t display the shadow below the header. More hacks required! Apply the shadow to all the cells inside it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;css&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.fancy&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #85E89D;&quot;&gt; thead td&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  box-shadow&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; inset 0 -1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;px&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; #ccc&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This feels even hackier than the original fix, but it works fine in all the browsers that I tested!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>modeling CRDTs in Alloy - introduction and the importance of idempotence</title>
		<published>2023-10-09T00:00:00+00:00</published>
		<updated>2023-10-09T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-crdts-in-alloy-introduction-and-the-importance-of-idempotence/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-crdts-in-alloy-introduction-and-the-importance-of-idempotence/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been interested in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.inkandswitch.com&#x2F;local-first&#x2F;&quot;&gt;local-first software&lt;&#x2F;a&gt; for a long time, and recently attended an event about it with a bunch of luminaries from various research groups. I learned a lot, and it rekindled my interest in syncable data structures.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve tried to sync data over the years with varying degrees of success. For example, I&#x27;ve known about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Operational_transformation&quot;&gt;operational transformations (OT)&lt;&#x2F;a&gt; for a while (via &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;quilljs.com&#x2F;&quot;&gt;Quill&lt;&#x2F;a&gt;, but I have never been able to get the syncing operations working to my satisfaction. Message ordering is crucially important in OT, and it&#x27;s hard to get right. I get the impression that the engineers who worked on Google Wave (which also used OT) also struggled with this. One of them (Joseph Gentle) was quoted as saying &quot;Unfortunately, implementing OT sucks. There&#x27;s a million algorithms with different tradeoffs, mostly trapped in academic papers. […] Wave took 2 years to write and if we rewrote it today, it would take almost as long to write a second time.&quot; (Worth noting that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=12311984&quot;&gt;he later said it might not take as long today&lt;&#x2F;a&gt;, but still… pain!)&lt;&#x2F;p&gt;
&lt;p&gt;In contrast to OT, conflict-free replicated data types (CRDTs) seem really promising! &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Conflict-free_replicated_data_type&quot;&gt;Wikipedia has a good summary&lt;&#x2F;a&gt;, which I&#x27;ll quote:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;In distributed computing, a conflict-free replicated data type (CRDT) is a data structure that is replicated across multiple computers in a network, with the following features:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The application can update any replica independently, concurrently and without coordinating with other replicas.&lt;&#x2F;li&gt;
&lt;li&gt;An algorithm (itself part of the data type) automatically resolves any inconsistencies that might occur.&lt;&#x2F;li&gt;
&lt;li&gt;Although replicas may have different state at any particular point in time, they are guaranteed to eventually converge.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;These properties appeal to me! If CRDTs can automatically and independently converge, no matter how far apart they drift, then you could store them wherever and however you like. Offline support should be simple, and adding live collaboration features should not mean spending years figuring out edge cases. Sign me up!&lt;&#x2F;p&gt;
&lt;p&gt;What are CRDTs made from, though? It looks like typically it&#x27;s some internal stuff, a function to resolve the internal stuff to a &lt;code&gt;value&lt;&#x2F;code&gt;, and a function to &lt;code&gt;merge&lt;&#x2F;code&gt; two of the data structure together. The merge function is the number-one-most-important thing about this whole arrangement, and has to satisfy&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; these three laws:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;commutative (so &lt;code&gt;merge(a, b)&lt;&#x2F;code&gt; must be the same as &lt;code&gt;merge(b, a)&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;associative (so &lt;code&gt;merge(merge(a, b), c)&lt;&#x2F;code&gt; must be the same as &lt;code&gt;merge(a, merge(b, c))&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;idempotent (so &lt;code&gt;merge(a, b)&lt;&#x2F;code&gt; must be the same as &lt;code&gt;merge(merge(a, b), b)&lt;&#x2F;code&gt;.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If all these are true, we can always merge two states, regardless of how many changes have been made since we last merged, and get the same result on both sides of the sync. Cool!&lt;&#x2F;p&gt;
&lt;p&gt;These seem like the kinds of things that &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; would be good at modeling, so let&#x27;s try it! Over the next couple of posts, I&#x27;m going to build a handful of data structures that follow these three rules and compose them into data structures we could build an application on top of.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s get a taste for this with &lt;code&gt;OR(bool, bool)&lt;&#x2F;code&gt;. First off, does it satisfy our three properties?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;commutative: &lt;code&gt;OR(True, False)&lt;&#x2F;code&gt; is the same as &lt;code&gt;OR(False, True)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;associative: &lt;code&gt;OR(OR(True, False), True)&lt;&#x2F;code&gt; is the same as &lt;code&gt;OR(True, OR(False, True))&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;idempotent: calling &lt;code&gt;OR(x, True)&lt;&#x2F;code&gt; gives the same result as &lt;code&gt;OR(OR(x, True), True)&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So it looks like &lt;code&gt;OR&lt;&#x2F;code&gt; might be fine, but let&#x27;s be sure. We can check this in Alloy by first defining&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; a boolean:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;enum Bool { True&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; False }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then a merge function, which we check is correct:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; OR&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsCorrect&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; read as: given `a` and `b`, which are `Bool`, the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; following condition must hold.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;or&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And finally, our three properties:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsCommutative&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; a]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsAssociative&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; c] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; c]]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; MergeIsIdempotent&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Great! Alloy can&#x27;t find any errors with &lt;code&gt;OR&lt;&#x2F;code&gt;.&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; We expected that, so let&#x27;s break it in interesting ways. What if the merge function was &lt;code&gt;XOR(bool, bool)&lt;&#x2F;code&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; instead?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[a&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  a &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; b &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; False &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now Alloy finds that we&#x27;ve broken idempotency: &lt;code&gt;merge[False, True]&lt;&#x2F;code&gt; is &lt;code&gt;True&lt;&#x2F;code&gt;, but &lt;code&gt;merge[merge[False, True], True]&lt;&#x2F;code&gt; is &lt;code&gt;False&lt;&#x2F;code&gt;. This would cause big problems if we used &lt;code&gt;XOR&lt;&#x2F;code&gt; as a merge function: the act of syncing could change our values! We can imagine this sequence of events:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Node A sets the value to &lt;code&gt;True&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Node B sets the value to &lt;code&gt;False&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The nodes sync, both resolving to &lt;code&gt;True&lt;&#x2F;code&gt; (since &lt;code&gt;XOR(True, False)&lt;&#x2F;code&gt; is &lt;code&gt;True&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;The nodes sync again, both resolving to &lt;code&gt;False&lt;&#x2F;code&gt; (since &lt;code&gt;XOR(True, True)&lt;&#x2F;code&gt; is &lt;code&gt;False&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Further syncs would stay at &lt;code&gt;False&lt;&#x2F;code&gt; until someone changed the value to &lt;code&gt;True&lt;&#x2F;code&gt;, at which point we&#x27;d repeat from step 3.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;But in a fun twist of fate, we don&#x27;t &lt;em&gt;have&lt;&#x2F;em&gt; to imagine this; we can ask Alloy to generate these traces for us. First we&#x27;ll make a document that contains a changeable value:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Document&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Bool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we&#x27;ll define a &lt;code&gt;not&lt;&#x2F;code&gt; for our boolean and a couple of operations (flipping an arbitrary value and syncing two documents):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; bool_not&lt;&#x2F;span&gt;&lt;span&gt;[b&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Bool {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  b &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; True &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; False &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;else&lt;&#x2F;span&gt;&lt;span&gt; True&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; intent: when `flip` is true, the provided document will go&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; from True to False or False to True in the next time step.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; This serves as our proxy for someone clicking a button in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; a UI or whatever.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; flip&lt;&#x2F;span&gt;&lt;span&gt;[d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; implementation: `value` is actually a set of tuples from&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; documents to booleans, and `value&amp;#39;` is the same but in the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; next time step. We set the entire value explicitly&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; (instead of just saying like `d&amp;#39;.value = False`) because&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; otherwise Alloy could say &amp;quot;and what if something changes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; that you weren&amp;#39;t expecting?&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; Syntax: `-&amp;gt;` creates a tuple, and `++` replaces all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; existing tuples referencing `d` in the set.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  value&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; value &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;bool_not&lt;&#x2F;span&gt;&lt;span&gt;[d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;value]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; intent: merge two documents together in a way that all the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; `merge` rules hold. Maybe imagine we have `d1` locally and&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; are getting `d2` over the network (but remember that order&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; should not matter!)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; merged &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; merge&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;value&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;value] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    value&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; value &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt; (d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;merged &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;merged)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now wire them up so these events can happen over time:&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#6&quot;&gt;6&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; `Document` is a set of documents, and `-&amp;gt;` will apply pairwise&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; for the sets in its arguments, so this means this says &amp;quot;all&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; documents start at `False`&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  value &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; False&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; this says &amp;quot;start with `init`, then pick one of these things to&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; happen at each time step.&amp;quot; Putting it in a `fact` means that&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; any assertions we make from now on assume that we want these&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; instructions.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; flip&lt;&#x2F;span&gt;&lt;span&gt;[d])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;some&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can now check that our sync is idempotent:&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#7&quot;&gt;7&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SyncIsIdempotent&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; at all times, if sync happens twice in a row (the `;` syntax) then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; the next value (`value&amp;#39;`) and the one after that (`value&amp;#39;&amp;#39;`) should&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; be the same.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2]; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2]) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; value&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; value&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we make &lt;code&gt;OR&lt;&#x2F;code&gt; our &lt;code&gt;merge&lt;&#x2F;code&gt; function, this works fine and Alloy can&#x27;t find any counterexamples. If we use &lt;code&gt;XOR&lt;&#x2F;code&gt; for &lt;code&gt;merge&lt;&#x2F;code&gt;, though, Alloy finds the trace I mentioned could show up earlier. Here&#x27;s what it shows us.&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Step&lt;&#x2F;th&gt;&lt;th&gt;Description&lt;&#x2F;th&gt;&lt;th&gt;Image&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1.&lt;&#x2F;td&gt;&lt;td&gt;We start off with all documents at &lt;code&gt;False&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;td&gt;&lt;img src=&quot;&#x2F;images&#x2F;xor-trace-step-1-and-4.png&quot; alt=&quot;an Alloy instance showing two documents both set to False&quot; &#x2F;&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2.&lt;&#x2F;td&gt;&lt;td&gt;One of the documents flips to &lt;code&gt;True&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;td&gt;&lt;img src=&quot;&#x2F;images&#x2F;xor-trace-step-2.png&quot; alt=&quot;the previous instance, but with one document now set to True&quot; &#x2F;&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3.&lt;&#x2F;td&gt;&lt;td&gt;The documents sync, converging to &lt;code&gt;True&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;td&gt;&lt;img src=&quot;&#x2F;images&#x2F;xor-trace-step-3.png&quot; alt=&quot;the previous instance, but with both documents now set to True&quot; &#x2F;&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4.&lt;&#x2F;td&gt;&lt;td&gt;The documents sync again, converging to &lt;code&gt;False&lt;&#x2F;code&gt; because &lt;code&gt;XOR(True, True)&lt;&#x2F;code&gt; (the previously synced state) is &lt;code&gt;False&lt;&#x2F;code&gt;.&lt;&#x2F;td&gt;&lt;td&gt;&lt;img src=&quot;&#x2F;images&#x2F;xor-trace-step-1-and-4.png&quot; alt=&quot;the previous instance, but with both documents now set to False&quot; &#x2F;&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;5.&lt;&#x2F;td&gt;&lt;td&gt;Stay here forever or return to 2.&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;In case you&#x27;re encountering them for the first time, these diagrams are generated by Alloy—they&#x27;re pretty fantastic for showing other people exactly what can happen in a system. (That&#x27;s also why &lt;code&gt;Document 0&lt;&#x2F;code&gt; is &lt;code&gt;d1&lt;&#x2F;code&gt; and so on. The documents are defined separately, and the &lt;code&gt;d1&lt;&#x2F;code&gt; and &lt;code&gt;d2&lt;&#x2F;code&gt; labels come from our &lt;code&gt;check&lt;&#x2F;code&gt; so we can see what&#x27;s doing what.)&lt;&#x2F;p&gt;
&lt;p&gt;Alloy can do more than this, too. If we modify our condition to have 3 distinct documents like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SyncIsIdempotent&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all disj&lt;&#x2F;span&gt;&lt;span&gt; d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Document &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d2]; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;sync&lt;&#x2F;span&gt;&lt;span&gt;[d2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d3]) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; value&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; value&amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then Alloy shows the situation getting even sillier. You only need one of the nodes to be &lt;code&gt;False&lt;&#x2F;code&gt;, at which point it&#x27;s possible that the documents as a whole could never converge again and just flap back and forth randomly forever. It all depends on which documents sync with which other documents. Turns out idempotency is pretty important!&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s it for today. Next time we&#x27;ll go from booleans-of-dubious-usefulness to counters!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Big thanks to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jakelazaroff.com&#x2F;&quot;&gt;Jake Lazaroff&lt;&#x2F;a&gt; for his help reviewing this post. If you&#x27;re interested in this stuff, he also recently published &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jakelazaroff.com&#x2F;words&#x2F;an-interactive-intro-to-crdts&#x2F;&quot;&gt;an interactive intro to CRDTs&lt;&#x2F;a&gt; that you may enjoy!&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;1&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;This is generally true but it&#x27;s not always true when syncing. There&#x27;s a further distinction here between operation- and state-based CRDTs. I&#x27;m not going to get into it right now, but if I understand correctly, this distinction is about reducing the data you have to transfer by relaxing one of the three requirements while adding strict causal message ordering. For now, we&#x27;re going to model state-based CRDTs, where this won&#x27;t come up.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;2&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;I think argument position doesn&#x27;t matter for idempotency, since commutativity and associativity should mean we can rearrange the arguments however we like, but I don&#x27;t have a proof of this.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;3&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;Alloy has a boolean type I could just import, but it&#x27;s useful to define exactly the operations you want to understand the system better, so I&#x27;ve done that instead.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;4&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;Note that I&#x27;m not saying &quot;Alloy &lt;em&gt;proved&lt;&#x2F;em&gt; that this is correct.&quot; That&#x27;s intentional! Alloy doesn&#x27;t do proofs; it only exhaustively checks models up to a certain bound (four instances of each &lt;code&gt;sig&lt;&#x2F;code&gt; by default.) I have only had a handful of times where I&#x27;ve had to increase the bound, though. In practice, I&#x27;ve been more than happy to trade exhaustiveness for speed.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;5&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;5&lt;&#x2F;sup&gt;
&lt;p&gt;Exclusive OR. In other words, &quot;both of these can&#x27;t be &lt;code&gt;True&lt;&#x2F;code&gt;.&quot; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Exclusive_or&quot;&gt;Wikipedia has more details, as usual&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;6&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;6&lt;&#x2F;sup&gt;
&lt;p&gt;I&#x27;m skipping over a lot of detail here. If this is your first time seeing this, there are more details and explanation on the setup here at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-1-blobs-and-trees&#x2F;&quot;&gt;modeling Git internals in Alloy&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;7&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;7&lt;&#x2F;sup&gt;
&lt;p&gt;I&#x27;m &lt;em&gt;pretty sure&lt;&#x2F;em&gt; this is the right way to check this, but I had some trouble working it out and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.discourse.group&#x2F;t&#x2F;how-to-assert-an-operation-is-idempotent&#x2F;405&quot;&gt;had to ask on the Alloy forum&lt;&#x2F;a&gt;. If you try to do something similar and run into difficult, you might try asking there too!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Modeling in-flight requests in Alloy</title>
		<published>2023-10-02T00:00:00+00:00</published>
		<updated>2023-10-02T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-in-flight-requests-in-alloy/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-in-flight-requests-in-alloy/</id>
		<content type="html">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;, sometimes I want to model clients and servers (or any relationship where two entities are talking.) With network stuff, it&#x27;s always possible for a message to not be received, or to be received twice, or out of order, et cetera. I&#x27;d like to model that!&lt;&#x2F;p&gt;
&lt;p&gt;Because I just want to learn the basics here, I&#x27;ll try to keep this as simple as possible. So instead of requests and responses or any other protocol, we&#x27;ll stick to just messages:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Message&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Delivered&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; in&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Message&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In English, &quot;We have a set of &lt;code&gt;Message&lt;&#x2F;code&gt;s, some of which have been delivered and some of which haven&#x27;t. Both sets can change over time.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;In addition to the things we define in a model like this, we have to think about the world they live in. Here I&#x27;m imagining that these messages form a stream going from some node A to another node B—no return messages, though, only one way!&lt;&#x2F;p&gt;
&lt;p&gt;Next we&#x27;ll take care of a little boilerplate: how the system starts (with no messages) and a do-nothing message so our traces can run forever.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; Message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; do_nothing&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Delivered&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Delivered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(&lt;code&gt;Message&#x27;&lt;&#x2F;code&gt; means &quot;&lt;code&gt;Message&lt;&#x2F;code&gt; in the next step.&quot; If this is unfamiliar to you, here&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-3-operations-on-blobs-and-trees&#x2F;&quot;&gt;a previous post where I went into more detail about how time works in Alloy&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Next we want to send a message:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; send_message&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; action: we want exactly `one` new message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; without making any other changes to `Messages`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Message {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame (holding everything else still)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Delivered&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Delivered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can do something similar to model the request being received:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; receive_message&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; action: mark exactly one message as delivered.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Delivered {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Delivered&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Delivered &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Note that this can&#x27;t be true (that is, can&#x27;t happen) if we don&#x27;t have at least one message in &lt;code&gt;Message - Delivered&lt;&#x2F;code&gt;, which can read as &quot;The set of &lt;code&gt;Message&lt;&#x2F;code&gt; minus the set of &lt;code&gt;Delivered&lt;&#x2F;code&gt;.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we set up a state machine telling Alloy when each predicate can be true over time:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    do_nothing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; send_message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; receive_message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That works pretty well! If you step through the traces here, you can get instances where messages are delivered out of order, never delivered, etc. We can&#x27;t get duplicate messages in this model, but that wouldn&#x27;t be the worst thing in the world to add!&lt;&#x2F;p&gt;
&lt;p&gt;Since this is demonstrating the lack of safety on the network, we can&#x27;t add a check like &quot;all messages are eventually delivered&quot;, but we can make assertions that the individual actions are doing what they say they will. For example, we know that if &lt;code&gt;send_message&lt;&#x2F;code&gt; is true then we will always have exactly one new message and no changes to &lt;code&gt;Delivered&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SendMessageNextStepHasExactlyOneNewMessage&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always send_message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies one&lt;&#x2F;span&gt;&lt;span&gt; Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; SendMessageDoesNotChangeDelivered&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always send_message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; Delivered&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Delivered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I debated including &lt;code&gt;Delivered&#x27; = Delivered&lt;&#x2F;code&gt; above, though… it&#x27;s a property I care about, but it&#x27;s also a frame condition. If you squint and think of this like a unit test, it&#x27;s just testing that the implementation is what I expect. But on the other hand, if Alloy allows express my intent this way, where&#x27;s the problem?&lt;&#x2F;p&gt;
&lt;p&gt;We can make similar assertions about &lt;code&gt;receive_message&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; ReceiveMessageOnlyMarksMessagesAsDelivered&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always receive_message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies one&lt;&#x2F;span&gt;&lt;span&gt; Delivered&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Delivered&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; ReceiveMessageDoesNotChangeMessages&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always receive_message &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; Message&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(If you&#x27;re typing this in by hand and want to go back to just exploring instances, don&#x27;t forget you can add a &lt;code&gt;run {}&lt;&#x2F;code&gt; block at the end to generate something more explorable!)&lt;&#x2F;p&gt;
&lt;p&gt;So with that, we have a fairly nice skeleton to add more network operations onto. For example, imagine that these messages mutated some state. We can now make sure that the state changes are correct in the face of out-of-order or dropped messages. Give it a try and see if you can extend the model to do that!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>tsort</title>
		<published>2023-09-25T00:00:00+00:00</published>
		<updated>2023-09-25T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/tsort/" type="text/html"/>
		<id>https://bytes.zone/posts/tsort/</id>
		<content type="html">&lt;p&gt;On the command line, you can use &lt;code&gt;tsort&lt;&#x2F;code&gt; to sort a graph into a list (a topological ordering.) Say you have this chart showing what you need to get dressed:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;tsort-clothes.png&quot; alt=&quot;A graph of dependency relationships among pieces of clothing. It shows you need an undershirt to put on a shirt, shirt to put on a tie, underwear to put on pants, pants to put on a belt, and socks to put on shoes.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can write all those arrows down in a file as dependencies (in order of &quot;X blocks Y,&quot; for example &quot;Shirt blocks Tie,&quot; &quot;Socks block Shoes.&quot;)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Shirt Tie&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Undershirt Shirt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Shirt Belt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Pants Belt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Underwear Pants&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Socks Shoes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Pants Shoes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you pass that to &lt;code&gt;tsort&lt;&#x2F;code&gt; you get this, one order in which you could put on clothes to get dressed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Socks&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Undershirt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Underwear&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Shirt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Pants&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Tie&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Shoes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Belt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It kinda suggests lanes of parallelism, too. For example, you could hypothetically put socks, undershirt, and underwear at the same time. Granted, you&#x27;d have to have some machine like in Wallace and Gromit in &lt;em&gt;The Wrong Trousers&lt;&#x2F;em&gt;, but that turned out so well for them! 😆&lt;&#x2F;p&gt;
&lt;p&gt;On a more serious note, I&#x27;ve used this to order tasks in projects in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linear.app&quot;&gt;Linear&lt;&#x2F;a&gt;. Combined with blocker relationships, it makes it clear to everyone which tasks should go first.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Trying and Failing to Implement Artificial Ignorance</title>
		<published>2023-09-18T00:00:00+00:00</published>
		<updated>2023-09-18T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/trying-and-failing-to-implement-artificial-ignorance/" type="text/html"/>
		<id>https://bytes.zone/posts/trying-and-failing-to-implement-artificial-ignorance/</id>
		<content type="html">&lt;p&gt;I like the ideas behind &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ranum.com&#x2F;security&#x2F;computer_security&#x2F;papers&#x2F;ai&#x2F;index.html&quot;&gt;artificial ignorance&lt;&#x2F;a&gt;. Summed up:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You can&#x27;t know all the &quot;bad&quot; events that can happen in a system. Modern attackers have basically unlimited kinds of attacks.&lt;&#x2F;li&gt;
&lt;li&gt;You &lt;em&gt;can&lt;&#x2F;em&gt; know all the unexceptional, boring events that could happen. That list might go on for a long time, but it&#x27;ll end at &lt;em&gt;some&lt;&#x2F;em&gt; point.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;System logs tend to contain all the events, so removing the unexceptional events should leave you with just the &quot;interesting&quot; ones. &lt;em&gt;Hypothetically&lt;&#x2F;em&gt; you could make yourself a big regex and then use &lt;code&gt;grep&lt;&#x2F;code&gt; and other shell tools to look over the logs and email yourself interesting events. This won&#x27;t catch just security events, either: you&#x27;ll get warnings about bad configurations, disk space, etc. You&#x27;ll refine this over time, too—new unexceptional events will happen over time and you’ll add them to the list. Doing this means you’ll only ever see novel things you need to deal with.&lt;&#x2F;p&gt;
&lt;p&gt;I find this idea compelling. It seems analogous to running a linter on code: you get things you would not have caught otherwise that might have caused problems later. Saves time and stress, right?&lt;&#x2F;p&gt;
&lt;p&gt;Except, when I tried to apply this I ran into problems. Basically, the sticking point shows up when creating the list: it grows and grows and ends up too long to manage. Having to create and maintain a list of all the things I don&#x27;t care about makes for too much work, even after turning down log levels wherever I could. Over the past couple of months, I&#x27;ve put probably 5 or 6 hours into sitting down with &lt;code&gt;journalctl&lt;&#x2F;code&gt; dumps and writing regex. Too much! I give up!&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t seem to shake the idea, though… I just like the payoff too much!&lt;&#x2F;p&gt;
&lt;p&gt;That time cost has to come down, though… maybe approaching the task a different way? Maybe it would help to take this service-by-service or day-by-day instead of looking at a whole system for a whole month? Or maybe I just need to accept that some services will always produce an unreasonable amount of logs (looking at you, Gitea) and give up on them totally? Surely I can find a way forward!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>crunch_str</title>
		<published>2023-09-11T00:00:00+00:00</published>
		<updated>2023-09-11T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/crunch-str/" type="text/html"/>
		<id>https://bytes.zone/posts/crunch-str/</id>
		<content type="html">&lt;p&gt;When writing &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;crunch-str&#x2F;@content&#x2F;montage.md&quot;&gt;montage&lt;&#x2F;a&gt; I wanted to have a little view of my current task up in the task bar with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xbarapp.com&#x2F;&quot;&gt;xbar&lt;&#x2F;a&gt;. The GraphQL API made quick work of that—I just asked for the current session and printed it to stdout. Xbar can pick that up and display it, no problem. It looks like this right now as I write this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;montage-xbar.png&quot; alt=&quot;a macOS status bar item saying &amp;quot;write about crunch_str in Montage&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s all well and good, but sometimes my sessions have longer names like &quot;scrub active projects and promote inactive ones&quot; or even longer for tickets at work. Then it takes up way too much room on the screen and pushes other stuff I want in the status bar off the screen. That&#x27;s annoying! I want it to use, like… 40–50 characters, &lt;em&gt;max&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At this point a reasonable person would just truncate the session name to the first 40 characters or whatever and add an ellipsis. But I don&#x27;t want to be reasonable! I want this to be offbeat and fun!&lt;&#x2F;p&gt;
&lt;p&gt;I got to thinking about more fun ways to shorten these strings. It&#x27;s safe to assume I &lt;em&gt;kinda&lt;&#x2F;em&gt; know what the task says, so could I make the task shorter by removing letters from the string that weren&#x27;t essential to understanding? What if…&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;if I know a shorter version of a word, I just substitute it. For example, I can use &quot;&amp;amp;&quot; instead of &quot;and&quot; or &quot;7&quot; instead of &quot;seven.&quot; Since this is for me, text speak is also fine: &quot;u&quot; for &quot;you&quot;, &quot;y&quot; for &quot;why&quot;, etc.&lt;&#x2F;li&gt;
&lt;li&gt;if a word has double letters, remove &#x27;em! &quot;Occurrence&quot; becomes &quot;ocurence.&quot;&lt;&#x2F;li&gt;
&lt;li&gt;drop vowels from the middle of words. That transforms &quot;ocurence&quot; into &quot;ocrnc&quot;&lt;&#x2F;li&gt;
&lt;li&gt;drop consonants from the middle of words. &quot;ocrnc&quot; becomes &quot;ornc&quot;, &quot;onc&quot;, &quot;oc&quot; over time&lt;&#x2F;li&gt;
&lt;li&gt;if a word has two letters left, make it acronym-y. &quot;oc&quot; is now &quot;O&quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;This does make the words unintelligible pretty fast, though, so I shorten longer words first (reasoning that a longer word has more chance to remain legible if shortened.)&lt;&#x2F;p&gt;
&lt;p&gt;This works pretty well! There&#x27;s the odd session that gets crunched into nonsense, but clicking the status in my task bar shows the full version of the session so it&#x27;s fine.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s delightful, too! For example, I had a short session today to fix something in my neovim setup. The task &quot;fix alternative test thing for our particular test layout&quot; crunchified down to &quot;fix arntv test thing 4 r ptclr test lout.&quot; Useful as a reminder, but inscrutable if you don&#x27;t know what the session was about in the first place. Just the level of weirdness I was looking for!&lt;&#x2F;p&gt;
&lt;p&gt;All that said, I implemented this whole thing as a Rust crate in the Montage workspace. I can&#x27;t imagine other uses for it, but it&#x27;s reusable if you want to shorten strings in a silly way yourself! You can get it at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;montage&#x2F;src&#x2F;branch&#x2F;main&#x2F;crunch_str&#x2F;src&#x2F;lib.rs&quot;&gt;git.bytes.zone&lt;&#x2F;a&gt;. Like the rest of &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;montage&#x2F;&quot;&gt;montage&lt;&#x2F;a&gt;, it&#x27;s licensed BSD 3-Clause. Have fun!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>once p is true, it&#x27;s always true</title>
		<published>2023-08-28T00:00:00+00:00</published>
		<updated>2023-08-28T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/once-p-is-true-its-always-true/" type="text/html"/>
		<id>https://bytes.zone/posts/once-p-is-true-its-always-true/</id>
		<content type="html">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;, to say &quot;once this is true, it&#x27;s always true&quot;, you say:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;always (p &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; always p)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is true because &lt;code&gt;always&lt;&#x2F;code&gt; means &quot;in all states starting from this one.&quot; So, the outer &quot;always&quot; means that the condition is always true starting from the initial time. Once &lt;code&gt;p&lt;&#x2F;code&gt; is true, the &lt;code&gt;implies&lt;&#x2F;code&gt; says that it will always remain true.&lt;&#x2F;p&gt;
&lt;p&gt;If you did just &lt;code&gt;p implies always p&lt;&#x2F;code&gt; without the wrapper, that would be saying &quot;if &lt;code&gt;p&lt;&#x2F;code&gt; is true &lt;em&gt;at the initial time&lt;&#x2F;em&gt; then &lt;code&gt;p&lt;&#x2F;code&gt; will always be true.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Source: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.discourse.group&#x2F;t&#x2F;how-do-you-say-once-this-is-true-its-always-true&#x2F;294&quot;&gt;Alcino Cunha&#x27;s answer to my question about this on the Alloy discourse instance&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>montage</title>
		<published>2023-08-21T00:00:00+00:00</published>
		<updated>2023-08-21T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/montage/" type="text/html"/>
		<id>https://bytes.zone/posts/montage/</id>
		<content type="html">&lt;p&gt;I&#x27;ve liked the concept of the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;francescocirillo.com&#x2F;products&#x2F;the-pomodoro-technique&quot;&gt;Pomodoro technique&lt;&#x2F;a&gt; (basically 25 minute work and 5 minutes rest on a loop through the day) for a long time. I get a lot out of it! Problem is, I&#x27;d like to use an app for this but nothing I&#x27;ve tried has been &quot;sticky&quot; enough for me to use long-term… they all have some drawback or other I don&#x27;t want to live with. So like any good software person, I made my own! Long story short, you can get it at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;montage&quot;&gt;git.bytes.zone&#x2F;brian&#x2F;montage&lt;&#x2F;a&gt;. Here&#x27;s what makes it different:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Other apps I&#x27;ve used assume they can take over for your todo list or daily log. Montage only asks that I label my sessions for status and reporting.&lt;&#x2F;li&gt;
&lt;li&gt;Other apps are &quot;polite&quot; with easily-ignorable chimes. Montage gets in my face by yelling at me with the system text-to-speech commands every two seconds when the timer is up. I showed this to a colleague, who described it as &quot;dystopian&quot;, but it&#x27;s the dystopia I chose!&lt;&#x2F;li&gt;
&lt;li&gt;Most other apps I&#x27;ve used don&#x27;t expose a nice API for session control or reporting, but Montage is built around &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;montage&#x2F;src&#x2F;commit&#x2F;dc8749ef12604347d9e57a348e8fb8c97cef9e53&#x2F;montage_client&#x2F;schema.graphql&quot;&gt;a GraphQL API&lt;&#x2F;a&gt; and uses that for everything—there&#x27;s nothing the CLI does that you can&#x27;t get out of it as JSON.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;By exposing a proper API, I was also able to build an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.omnigroup.com&#x2F;omnifocus&quot;&gt;OmniFocus&lt;&#x2F;a&gt; extension so that I can just click &quot;start task&quot; in the app to start something. I was also able to build an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xbarapp.com&#x2F;&quot;&gt;xbar&lt;&#x2F;a&gt; extension that shows my current task (or break) in the status bar. I get reports with a purpose-made command in the CLI that produces Markdown for me to copy into &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&#x2F;&quot;&gt;Obsidian&lt;&#x2F;a&gt;. But again, it&#x27;s just talking to the API so I have the flexibility to produce information about my workdays in any format I want.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m pretty happy with Montage so far, and I also have plenty of ideas for more things it should do! For example, it doesn&#x27;t have any opinion on how long you should work before being reminded to take a break. I&#x27;m planning to build that in as a configurable thing in &quot;vex&quot; (the subcommand that talks to me with TTS.) I&#x27;d also like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.raycast.com&#x2F;&quot;&gt;Raycast&lt;&#x2F;a&gt; and Obsidian extensions. At some point it may also make sense to build a GUI or an app, but for now I&#x27;m not too bothered by occasionally having to open up the SQLite database for some manual changes.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, like it says in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;montage&#x2F;src&#x2F;branch&#x2F;main&#x2F;README.md&quot;&gt;the README&lt;&#x2F;a&gt;, go ahead and give this a shot if it seems interesting to you and you&#x27;re OK with being called &quot;Brian&quot; by a computer every 25 minutes!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>moving genrules to library rules</title>
		<published>2023-08-14T00:00:00+00:00</published>
		<updated>2023-08-14T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/moving-genrules-to-library-rules/" type="text/html"/>
		<id>https://bytes.zone/posts/moving-genrules-to-library-rules/</id>
		<content type="html">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;buck2-basics&#x2F;&quot;&gt;buck2 basics&lt;&#x2F;a&gt;, we made two rules:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;some-target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;target-file&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;echo &amp;#39;Hello, World!&amp;#39; &amp;gt; $OUT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;yelling-target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;target-file&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; $(location :some-target) &amp;gt; $OUT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s lift these to named rules so that we have a nicer API and don&#x27;t mix the commands with the parameters. Let&#x27;s make a couple rules to &lt;code&gt;say&lt;&#x2F;code&gt; and &lt;code&gt;yell&lt;&#x2F;code&gt; (to implement &lt;code&gt;some-target&lt;&#x2F;code&gt; and &lt;code&gt;yelling-target&lt;&#x2F;code&gt;, respectively.) Open a new file &lt;code&gt;talking.bzl&lt;&#x2F;code&gt; and put this in it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; _say_impl&lt;&#x2F;span&gt;&lt;span&gt;(ctx:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;context&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; [DefaultInfo()]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;say&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;    impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; _say_impl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;    attrs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: attrs.string(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;out&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: attrs.string(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;out&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This won&#x27;t do anything yet, but it&#x27;s about the minimum possible rule that lets us do this back in &lt;code&gt;BUCK&lt;&#x2F;code&gt;. I&#x27;ve rendered this as a diff so you can see the before and after:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+load(&amp;quot;:talking.bzl&amp;quot;, &amp;quot;say&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  name = &amp;quot;some-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  out = &amp;quot;target-file&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  cmd = &amp;quot;echo &amp;#39;Hello, World!&amp;#39; &amp;gt; $OUT&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+say(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  name = &amp;quot;some-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  message = &amp;quot;Hello, World!&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   name = &amp;quot;yelling-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   out = &amp;quot;target-file&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   cmd = &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; $(location :some-target) &amp;gt; $OUT&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we have &lt;code&gt;:some-target&lt;&#x2F;code&gt; redefined as a call to &lt;code&gt;say&lt;&#x2F;code&gt;, so let&#x27;s implement it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; def _say_impl(ctx: &amp;quot;context&amp;quot;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    out = ctx.actions.declare_output(ctx.attrs.out)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    ctx.actions.write(out, ctx.attrs.message)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-    return [DefaultInfo()]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    return [DefaultInfo(default_output = out)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; say = rule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     impl = _say_impl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     attrs = {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         &amp;quot;message&amp;quot;: attrs.string(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;         &amp;quot;out&amp;quot;: attrs.string(default = &amp;quot;out&amp;quot;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;OK! Here&#x27;s what&#x27;s happening there:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we&#x27;re declaring an output file named whatever we passed in in &lt;code&gt;ctx.attrs.out&lt;&#x2F;code&gt;. We didn&#x27;t specify that in &lt;code&gt;BUCK&lt;&#x2F;code&gt; so it&#x27;s the default value, which we called &quot;out&quot;. Importantly, whenever we declare an output we have to &quot;bind&quot; it by using it in an action before the end of the implementation.&lt;&#x2F;li&gt;
&lt;li&gt;So, next, we write the message to that out file. That binds the output.&lt;&#x2F;li&gt;
&lt;li&gt;Then finally, we say that that output is our default output.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;At this point, if you run &lt;code&gt;buck2 build &#x2F;&#x2F;:yelling-target&lt;&#x2F;code&gt; it&#x27;ll produce the same output. We changed how we&#x27;re building the file, but the build graph as a whole produces the same thing. I really like this property; it means I can improve parts of the build graph in isolation without worrying too much about upstream or downstream dependencies.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, let&#x27;s do the same thing to &lt;code&gt;:yelling-target&lt;&#x2F;code&gt; now by making a &lt;code&gt;yell&lt;&#x2F;code&gt; rule:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;def&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; _yell_impl&lt;&#x2F;span&gt;&lt;span&gt;(ctx:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;context&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ctx.actions.declare_output(ctx.attrs.out)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ctx.actions.run(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;            &amp;quot;bash&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;            &amp;quot;-c&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;            &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; $1 &amp;gt; $2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;            &amp;quot;--&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ctx.attrs.src,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            out.as_output(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;        category&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;yell&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; [DefaultInfo(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;default_output&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; out)]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;yell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  impl&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; _yell_impl,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  attrs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;src&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: attrs.source(),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;out&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: attrs.string(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;default&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;out&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have some more stuff happening here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;re calling a &lt;code&gt;bash&lt;&#x2F;code&gt; script to get the shell redirection working correctly. I write a lot of little bash scripts like this when making Buck rules. There are probably better ways to do this, but it works fine for what we&#x27;re doing here.&lt;&#x2F;li&gt;
&lt;li&gt;When we pass &lt;code&gt;out&lt;&#x2F;code&gt; to the args of our script, we call &lt;code&gt;.as_output()&lt;&#x2F;code&gt; on it. That lets Buck know that we&#x27;re binding the output in that command.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;re requiring &lt;code&gt;attrs.source()&lt;&#x2F;code&gt; in &lt;code&gt;src&lt;&#x2F;code&gt;, which tells Buck that we&#x27;re expecting some already-existing input file.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Now in &lt;code&gt;BUCK&lt;&#x2F;code&gt;, we can modify our rules like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-load(&amp;quot;:talking.bzl&amp;quot;, &amp;quot;say&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+load(&amp;quot;:talking.bzl&amp;quot;, &amp;quot;say&amp;quot;, &amp;quot;yell&amp;quot;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; say(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   name = &amp;quot;some-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   message = &amp;quot;Hello, World!&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  name = &amp;quot;yelling-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  out = &amp;quot;target-file&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-  cmd = &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; $(location :some-target) &amp;gt; $OUT&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FDAEB7;background-color: #86181D;&quot;&gt;-)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+yell(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  name = &amp;quot;yelling-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  src = &amp;quot;:some-target&amp;quot;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since &lt;code&gt;src&lt;&#x2F;code&gt; is a &lt;code&gt;source&lt;&#x2F;code&gt;, we provide the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buck2.build&#x2F;docs&#x2F;concepts&#x2F;target_pattern&#x2F;&quot;&gt;target pattern&lt;&#x2F;a&gt; of the input file we want. That means our build graph is unchanged once again!&lt;&#x2F;p&gt;
&lt;p&gt;And we&#x27;re done! Now we can call &lt;code&gt;buck2 build &#x2F;&#x2F;:yelling-target&lt;&#x2F;code&gt;, same as before. Tada!&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, that&#x27;s all I have for Buck stuff—like I said in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;buck2-basics&#x2F;&quot;&gt;buck2 basics&lt;&#x2F;a&gt;, I&#x27;ll be watching the project to see when I can use it again!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>buck2 basics</title>
		<published>2023-08-07T00:00:00+00:00</published>
		<updated>2023-08-07T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/buck2-basics/" type="text/html"/>
		<id>https://bytes.zone/posts/buck2-basics/</id>
		<content type="html">&lt;p&gt;I just finished an experiment with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buck2.build&quot;&gt;Buck 2&lt;&#x2F;a&gt; at work. We didn&#x27;t end up using it, but it was more for organizational reasons than technical ones. I&#x27;ll be watching the project to see if it would make sense to use in the future!&lt;&#x2F;p&gt;
&lt;p&gt;One thing that held me back during the early part of the experiment was not having enough learning material around the basics. That makes sense: it&#x27;s a pretty new project (at least in this iteration) and there are a &lt;em&gt;lot&lt;&#x2F;em&gt; of concepts to learn.&lt;&#x2F;p&gt;
&lt;p&gt;So instead of teaching things from first principles, I want to run head-first into getting something simple set up. We won&#x27;t have any more functionality than we could get with some simple &lt;code&gt;make&lt;&#x2F;code&gt; rules, but since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;learning-requires-effort&#x2F;&quot;&gt;the person who does the work does the learning&lt;&#x2F;a&gt; I hope this will be helpful! That said, keep in mind that I&#x27;m no expert—just some person who learned enough to get stuff done!&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;I&#x27;m going to assume you have buck2 available on your &lt;code&gt;PATH&lt;&#x2F;code&gt;. If you don&#x27;t, download it from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;facebook&#x2F;buck2&#x2F;releases&#x2F;tag&#x2F;latest&quot;&gt;the &lt;code&gt;latest&lt;&#x2F;code&gt; tag on the facebook&#x2F;buck2 repo&lt;&#x2F;a&gt;. Once you do that, you can run &lt;code&gt;buck2 init --git&lt;&#x2F;code&gt; to get a basic project set up for the rest of this post.&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
&lt;h2 id=&quot;the-simplest-possible-rule&quot;&gt;The Simplest Possible Rule&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ll start off with the simplest possible thing: a file that says &quot;Hello, World!&quot; We&#x27;ll use a &lt;code&gt;genrule&lt;&#x2F;code&gt; for this, which lets you say what shell commands to run to produce some file. In the root &lt;code&gt;BUCK&lt;&#x2F;code&gt; file, write this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;some-target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;target-file&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;echo &amp;#39;Hello, World!&amp;#39; &amp;gt; $OUT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That configures a target named &lt;code&gt;some-target&lt;&#x2F;code&gt; that you can build with &lt;code&gt;buck2 build &#x2F;&#x2F;:some-target&lt;&#x2F;code&gt;. If you add &lt;code&gt;--show-output&lt;&#x2F;code&gt; to that, it&#x27;ll tell you where the file is, and if you &lt;code&gt;cat buck-out&#x2F;v2&#x2F;gen&#x2F;root&#x2F;213ed1b7ab869379&#x2F;__some-target__&#x2F;out&#x2F;target-file&lt;&#x2F;code&gt; (or whatever path Buck gives you) it out it&#x27;ll say &quot;Hello, World!&quot; First build done. Yay!&lt;&#x2F;p&gt;
&lt;p&gt;So what do all the parts in that rule mean?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;genrule(…)&lt;&#x2F;code&gt; is the rule you&#x27;re calling. I think &quot;gen&quot; here is short for &quot;generic&quot; (or maybe &quot;generate&quot;?) Anyway, it&#x27;s the simplest thing that the build system can possibly do.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;name&lt;&#x2F;code&gt; defines how you&#x27;ll refer to this target from other rules or from the CLI. See &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buck2.build&#x2F;docs&#x2F;concepts&#x2F;target_pattern&#x2F;&quot;&gt;the target pattern docs&lt;&#x2F;a&gt;, which also explain why you refer to this as &lt;code&gt;&#x2F;&#x2F;:some-target&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;out&lt;&#x2F;code&gt; is the file that will be produced by the rule&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cmd&lt;&#x2F;code&gt; is the command that will be run to produce the file. &lt;code&gt;$OUT&lt;&#x2F;code&gt; looks like an environment variable reference, but as far as I can tell Buck is actually interpolating it into the string before calling the command. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buck2.build&#x2F;docs&#x2F;api&#x2F;rules&#x2F;#genrule&quot;&gt;The docs for &lt;code&gt;genrule&lt;&#x2F;code&gt; have some examples of other variables you can use&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;more&quot;&gt;More!&lt;&#x2F;h2&gt;
&lt;p&gt;Next, let&#x27;s chain two rules together. Here&#x27;s another rule in the same file:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;genrule(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;yelling-target&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;target-file&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;  cmd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;tr &amp;#39;[:lower:]&amp;#39; &amp;#39;[:upper:]&amp;#39; &amp;lt; $(location :some-target) &amp;gt; $OUT&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The new thing here is &lt;code&gt;$(location ...)&lt;&#x2F;code&gt;, which interpolates the location of some the output of the named target into the command line.&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;I had a hard time finding out if there was anything else like &lt;code&gt;location&lt;&#x2F;code&gt; that I could use. It turns out that the call there is actually a lookup for the &quot;location&quot; attribute on the target inside Buck&#x27;s build graph. That means different rules will produce different things! But generally speaking, &lt;code&gt;location&lt;&#x2F;code&gt; will work for files in general and &lt;code&gt;executable&lt;&#x2F;code&gt; will work for files that you need to run.&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
&lt;p&gt;Now if you build everything and go look at the output (using &lt;code&gt;--show-output&lt;&#x2F;code&gt;), you will get a new file that says &quot;HELLO, WORLD!&quot;. If you change the original rule, the new one will get built too.&lt;&#x2F;p&gt;
&lt;p&gt;So with these two rules (plus the thing in &lt;code&gt;toolchain&lt;&#x2F;code&gt;), you can:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;code&gt;buck2 build &#x2F;&#x2F;...&lt;&#x2F;code&gt; to build both targets&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;buck2 build &#x2F;&#x2F;:some-target&lt;&#x2F;code&gt; to build only the &quot;Hello, World!&quot; file.&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;buck2 build &#x2F;&#x2F;:yelling-target&lt;&#x2F;code&gt; to build the &quot;HELLO, WORLD!&quot; file, which will build &lt;code&gt;:some-target&lt;&#x2F;code&gt; as well, if necessary.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Try changing things around (for example by changing the message) and seeing what happens. You can also run &lt;code&gt;buck clean&lt;&#x2F;code&gt; to remove the targets to get a fresh build.&lt;&#x2F;p&gt;
&lt;p&gt;Next time, we&#x27;ll talk about moving these from &lt;code&gt;genrule&lt;&#x2F;code&gt;s into a library that we can import and call.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Modeling Git Internals in Alloy, Part 3: Operations on Blobs and Trees</title>
		<published>2023-04-24T00:00:00+00:00</published>
		<updated>2023-04-24T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-git-internals-in-alloy-part-3-operations-on-blobs-and-trees/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-git-internals-in-alloy-part-3-operations-on-blobs-and-trees/</id>
		<content type="html">&lt;p&gt;In the last two posts, we&#x27;ve modeled Git&#x27;s internal data structures in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;. First, we handled &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-1-blobs-and-trees&#x2F;&quot;&gt;blobs and trees&lt;&#x2F;a&gt;, then &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-2-commits-and-tags&#x2F;&quot;&gt;commits and tags&lt;&#x2F;a&gt;. During all that, we introduced some &lt;code&gt;fact&lt;&#x2F;code&gt;s—things that Alloy will accept as true, regardless of whether those assumptions hold in real life. Today, we&#x27;ll model some Git operations to see if those facts actually hold! In particular, we&#x27;ll look at:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hash-object&lt;&#x2F;code&gt; to get a &lt;code&gt;Blob&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;update-index&lt;&#x2F;code&gt; and &lt;code&gt;write-tree&lt;&#x2F;code&gt; to get a &lt;code&gt;Tree&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To do that, we&#x27;re going to have to introduce changes over time to our model. In Alloy, this means two things:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Marking the parts of our model that can change.&lt;&#x2F;li&gt;
&lt;li&gt;Defining transitions between one state and the next.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Once we do that, we can make assertions about time, for example that something is always or never true, or becomes true eventually given some other condition.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s begin by redefining &lt;code&gt;Blob&lt;&#x2F;code&gt; to be able to change over time. To do that, we just prepend &lt;code&gt;var&lt;&#x2F;code&gt; to its definition:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If we run this, we get new goodies in the instance viewer:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;blob-over-time-in-the-instance-viewer.png&quot; alt=&quot;The Alloy instance viewer, a macOS window with a row of buttons at the top like &amp;quot;New Init&amp;quot; or &amp;quot;New Fork&amp;quot;. Below the controls is a timeline that shows distinct states. In this instance, there is only one state repeated twice.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On the left, we have time step 0—the initial state of the instance. On the right, we have the next step. Alloy will always show at least two time steps, and in this case Alloy also shows us that the last step repeats forever (by showing a looping arrow over the second step.)&lt;&#x2F;p&gt;
&lt;p&gt;Where we had “New” before to get a new instance, we now click “New Init”—that&#x27;ll get us another starting state for the model. We can also scrub back and forth on the timeline by clicking the left and right arrow buttons. If you click “New Fork”, Alloy will make something new happen starting from the second visible step (the one on the right.) I&#x27;d encourage you to load up Alloy and play around with this some—it&#x27;ll look totally random right now, but that&#x27;s just because we&#x27;re not controlling how &lt;code&gt;Blob&lt;&#x2F;code&gt; changes at all right now, so Alloy allows it to change in arbitrary ways.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-processes&quot;&gt;Adding Processes&lt;&#x2F;h2&gt;
&lt;p&gt;This model isn&#x27;t super useful yet, since we don&#x27;t actually say &lt;em&gt;how&lt;&#x2F;em&gt; blobs can change over time. Let&#x27;s add some structure so we can define that!&lt;&#x2F;p&gt;
&lt;p&gt;To start with, we need to define an initial state. Seems fine to start with the same thing Git does: an empty repo with no objects.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;d normally use a predicate to check if a condition holds, and that&#x27;s the same here except with the addition of time. Said another way, at any time that &lt;code&gt;init&lt;&#x2F;code&gt; is true, there will be no &lt;code&gt;Blob&lt;&#x2F;code&gt;s. We&#x27;ll place this &lt;code&gt;init&lt;&#x2F;code&gt; at a precise moment in time (namely, the first time step) very soon here.&lt;&#x2F;p&gt;
&lt;p&gt;From there, one reasonable thing to do is to do nothing. We&#x27;ll need a step like this for most models, since Alloy needs to be able to say “… and then nothing ever happens again for the rest of time.” This is traditionally called a stutter step:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; stutter&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We get a new bit of syntax here: saying &lt;code&gt;&#x27;&lt;&#x2F;code&gt; after something means you&#x27;re referring to that thing in the next time step. So &lt;code&gt;Blob&#x27; = Blob&lt;&#x2F;code&gt; (which I pronounce “blob prime equals blob”) means that &lt;code&gt;Blob&lt;&#x2F;code&gt; will stay exactly the same whenever the &lt;code&gt;stutter&lt;&#x2F;code&gt; predicate is true.&lt;&#x2F;p&gt;
&lt;p&gt;Now let&#x27;s combine these! Right now, we can keep the system the same across time by saying “init is true at the beginning, and then stutter is true forever.” We&#x27;d do that with another predicate that traces those events through time:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always stutter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This introduces another new piece of syntax: &lt;code&gt;always&lt;&#x2F;code&gt; means “from this point going forward, this condition is always true.” In other words, we&#x27;re saying “at the current time step, &lt;code&gt;init&lt;&#x2F;code&gt; is true. Going forward, &lt;code&gt;stutter&lt;&#x2F;code&gt; is always true.” Effectively, that means that we start with no blobs and never add any.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we tell Alloy that it should assume that the &lt;code&gt;traces&lt;&#x2F;code&gt; predicate is always true:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fact { traces }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now if we evaluate the model, we see that it has exactly zero blobs forever:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;no-blobs-in-the-instance-viewer.png&quot; alt=&quot;The Alloy instance viewer showing a totally uninhabited instance that does not change over time.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If we click “New Init” now, we get the usual “there are no more instances” message:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;no-more-satisfying-instances.png&quot; alt=&quot;a dialog box saying &amp;quot;There are no more satisfying instances. Note: due to symmetry breaking and other optimizations, some equivalent solutions may have been omitted.&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;hashing-objects&quot;&gt;Hashing Objects&lt;&#x2F;h2&gt;
&lt;p&gt;Now we have a blank canvas and can start adding operations. Let&#x27;s start as we did in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-1-blobs-and-trees&#x2F;&quot;&gt;part 1&lt;&#x2F;a&gt; with &lt;code&gt;git hash-object&lt;&#x2F;code&gt;. As a refresher, this command takes some content, hashes it, and stores it according to the hash. You can invoke it like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ echo &amp;#39;Hello, Alloy!&amp;#39; | git hash-object -w --stdin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;39528abd81b13b2731d47f86206351a61f1e6484&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In Alloy, that looks like adding a new predicate:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; hashObject&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Since we only care about the presence of the blobs and not their content, it&#x27;s sufficient to say, “there is one (and only one) blob in the next state that doesn&#x27;t appear in the previous state.”&lt;&#x2F;p&gt;
&lt;p&gt;Next, we&#x27;ll tell &lt;code&gt;traces&lt;&#x2F;code&gt; that &lt;code&gt;hashObject&lt;&#x2F;code&gt; is one of the things that can be true at any time step:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    stutter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; hashObject&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Just to reiterate what this means, we&#x27;re now saying “&lt;code&gt;init&lt;&#x2F;code&gt; is true at the start. Forever after, either &lt;code&gt;stutter&lt;&#x2F;code&gt; or &lt;code&gt;hashObject&lt;&#x2F;code&gt; is true.”&lt;&#x2F;p&gt;
&lt;p&gt;If it helps, you can kind of think of this as saying “&lt;code&gt;init&lt;&#x2F;code&gt; is true at the start; afterward, either &lt;code&gt;stutter&lt;&#x2F;code&gt; or &lt;code&gt;hashObject&lt;&#x2F;code&gt; &lt;em&gt;can happen&lt;&#x2F;em&gt;.” However, this is not strictly accurate: Alloy has no concept of events happening, only conditions which happen to be true at any given time.&lt;&#x2F;p&gt;
&lt;p&gt;If you execute this spec, you can now see blobs coming into existence between time steps (you may need to click “New Fork” to get this to happen.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;one-blob-in-the-instance-viewer.png&quot; alt=&quot;The Alloy instance viewer showing the creation of a single new blob.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;adding-to-the-index&quot;&gt;Adding to the Index&lt;&#x2F;h2&gt;
&lt;p&gt;To finish up, we&#x27;re going to create trees. As a reminder, this is two operations:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Adding a blob to the index at a certain path&lt;&#x2F;li&gt;
&lt;li&gt;Creating the tree from the index&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;On the command line, this looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git update-index --add --cacheinfo 100644 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  39528abd81b13b2731d47f86206351a61f1e6484 hello-alloy.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git write-tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3ee29075f260c5eebd8b9480b6464a7612668dde&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To model this in Alloy, we&#x27;ll need to model the index as well as trees. First, the index:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Repo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(This is &lt;code&gt;one sig&lt;&#x2F;code&gt; because there can only be one repo in our model. We&#x27;ve assumed there was one implicitly—we wouldn&#x27;t have been able to add blobs otherwise—it&#x27;s just explicit now.)&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll also need to add trees:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Like &lt;code&gt;Blob&lt;&#x2F;code&gt;, &lt;code&gt;Tree&lt;&#x2F;code&gt; is &lt;code&gt;var&lt;&#x2F;code&gt;: we can create new trees from an index. But unlike &lt;code&gt;Blob&lt;&#x2F;code&gt;, &lt;code&gt;Tree&lt;&#x2F;code&gt; has children. These are also marked &lt;code&gt;var&lt;&#x2F;code&gt;, despite an individual tree&#x27;s contents never changing, because we need to be able to change the mapping of trees to blobs over time.&lt;&#x2F;p&gt;
&lt;p&gt;Before we can add any events on these, we&#x27;ll need to define what they look like initially, and say that they never change in the stutter step:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; pred init {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   no Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  no Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  no children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; pred stutter {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   Blob&amp;#39; = Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  Tree&amp;#39; = Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  children&amp;#39; = children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  index&amp;#39; = index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We could have omitted &lt;code&gt;no children&lt;&#x2F;code&gt; in &lt;code&gt;init&lt;&#x2F;code&gt; because there really can&#x27;t be any trees or blobs present in &lt;code&gt;children&lt;&#x2F;code&gt; if there are no trees or blobs in the whole system, but I find it&#x27;s better to be explicit.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll also need to add similar lines as a frame condition in &lt;code&gt;hashObject&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; pred hashObject {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   one Blob&amp;#39; - Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  Tree&amp;#39; = Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  children&amp;#39; = children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+  index&amp;#39; = index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We need to do this because of the behavior we observed at first: if we don&#x27;t control the next state of every varying thing in our model, Alloy allows it to change in arbitrary ways. This can be annoying, but so far I&#x27;ve found it&#x27;s something I can get over in order to get the useful stuff Alloy can do.&lt;&#x2F;p&gt;
&lt;p&gt;But now that we have that done, we can add blobs and trees to the index:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; updateIndex&lt;&#x2F;span&gt;&lt;span&gt;[bt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;bt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Our action here is saying “the next version of &lt;code&gt;stage&lt;&#x2F;code&gt; is the old version plus the addition of &lt;code&gt;bt&lt;&#x2F;code&gt;.” We haven&#x27;t seen &lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt; before in this post; it means “all of &lt;code&gt;Repo&lt;&#x2F;code&gt; maps to all of &lt;code&gt;bt&lt;&#x2F;code&gt;.” It so happens that we have only one thing in each of those sets, so this is effectively saying “add the mapping of repo-to-bt to the index.”&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;If you want a firmer technical term, &lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt; gives you the &lt;em&gt;Cartesian product&lt;&#x2F;em&gt; of the two sides. That means &lt;code&gt;{a, b}-&amp;gt;{1, 2}&lt;&#x2F;code&gt; would be &lt;code&gt;{a-&amp;gt;1, a-&amp;gt;2, b-&amp;gt;1, b-&amp;gt;2}&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;aside&gt;
&lt;p&gt;Now that we have an index, let&#x27;s take care of &lt;code&gt;write-tree&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; writeTree&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Tree {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A couple of details here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;re dancing around a little by saying “I want one tree in &lt;code&gt;Tree&#x27;&lt;&#x2F;code&gt; that doesn&#x27;t exist in &lt;code&gt;Tree&lt;&#x2F;code&gt;, and then that &lt;code&gt;Tree&#x27;&lt;&#x2F;code&gt; is only changed by that new tree.” I feel this is a little awkward but it&#x27;s the best way I can find to get exactly one new thing. If you&#x27;re an Alloy expert reading this and know a better way to do this, I&#x27;d really appreciate it if you could let me know!&lt;&#x2F;li&gt;
&lt;li&gt;Since &lt;code&gt;-&amp;gt;&lt;&#x2F;code&gt; maps &lt;em&gt;all&lt;&#x2F;em&gt; of the left- and right-hand sides, this time we&#x27;re saying “our new tree has all the things currently in the index.”&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;With that taken care of, we need to unstage the items afterward. One final action:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; resetIndex&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; index&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, finally, we can add these predicates to the list of conditions that Alloy will consider at every time step:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;diff&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; pred traces {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     stutter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;     or hashObject&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    or (one bt: Blob + Tree | updateIndex[bt])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    or writeTree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #85E89D;background-color: #144620;&quot;&gt;+    or resetIndex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;At this point, I&#x27;d usually show some instances from the visualizer and ask if we thought they were reasonable, but that gets harder to do as you add more ways for the system to change. Instead, we&#x27;re going to jump right into…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;making-assertions&quot;&gt;Making Assertions&lt;&#x2F;h2&gt;
&lt;p&gt;Remember how we had this &lt;code&gt;fact&lt;&#x2F;code&gt; back in the static version of the model?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;trees cannot refer to themselves&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We added this because it didn&#x27;t look like it was in the design of Git for trees to refer to themselves (that is, appear in their own children, grandchildren, etc.) Well, now that we&#x27;re modeling operations instead of the data structures alone, we can ask Alloy to think about all the state transitions in the system and tell us whether that&#x27;s allowed:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;check TreesCannotReferToThemselves {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (no t: Tree | t in t.^children)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Remembering that &lt;code&gt;always&lt;&#x2F;code&gt; means “true at this time and forever after”, we&#x27;re asking it to check if there&#x27;s any series of time steps where a tree can appear in its own &lt;code&gt;children&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Alloy can&#x27;t find a counterexample for this—yay! Here&#x27;s what that looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Executing &amp;quot;Check TreesCannotReferToThemselves&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   Solver=minisatprover(jni) Steps=1..10 Bitwidth=4 MaxSeq=4 SkolemDepth=1 Symmetry=20 Mode=batch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   1..10 steps. 27665 vars. 1790 primary vars. 67627 clauses. 275ms.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   No counterexample found. Assertion may be valid. 45ms.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   . contains 9 top-level formulas. 10ms.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s also check that the content of trees cannot change with any of our operations. This is as much a unit test of our modeling as it is a property of Git:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; TreesAreImmutable&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; always t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And again, no counterexample found.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, let&#x27;s do something a little trickier. We said it should be OK for files to have the same children—we assume that they&#x27;ll have different filenames. We can ask Alloy to generate this as a counterexample, just to make sure it&#x27;s possible. To do this, we make the opposite assertion (that two distinct trees can&#x27;t have the same children):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; NoDuplicateTrees&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;no&lt;&#x2F;span&gt;&lt;span&gt; disjoint t1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; t2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We get a counterexample, but not the one we wanted: it turns out that in our model you can call &lt;code&gt;write-tree&lt;&#x2F;code&gt; over and over and get a new tree each time. If you do that in a real repo you&#x27;ll just the same tree over and over.&lt;&#x2F;p&gt;
&lt;p&gt;We could deal with this in a few ways:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We could add a guard condition to &lt;code&gt;writeTree&lt;&#x2F;code&gt; so that it cannot be true if more than one empty tree would be produced. &lt;strong&gt;BUT&lt;&#x2F;strong&gt; in real life, Git allows you to call &lt;code&gt;write-tree&lt;&#x2F;code&gt; at any time.&lt;&#x2F;li&gt;
&lt;li&gt;We could add a precondition to &lt;code&gt;traces&lt;&#x2F;code&gt; to do the same, &lt;strong&gt;BUT&lt;&#x2F;strong&gt; that models someone just never does the wrong thing, so I&#x27;d rather assume that anything can be called at any time.&lt;&#x2F;li&gt;
&lt;li&gt;We could add a &lt;code&gt;fact&lt;&#x2F;code&gt; about empty trees, &lt;strong&gt;but&lt;&#x2F;strong&gt; every fact we have introduces assumptions to our model.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In this case, I think adding a &lt;code&gt;fact&lt;&#x2F;code&gt; is worth it, since we don&#x27;t represent the behavior of empty trees accurately now and introducing a fact helps with accuracy. Here&#x27;s how we do it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;there can only be &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt; empty tree&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;lone&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;| no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This hinges on &lt;code&gt;lone&lt;&#x2F;code&gt; enforcing that there will be zero or one &lt;code&gt;Tree&lt;&#x2F;code&gt;s matching this description. We could also say &lt;code&gt;no disjoint t1, t2: Tree | no t1.children and no t2.children&lt;&#x2F;code&gt;, but that&#x27;s a bit of a mouthful.&lt;&#x2F;p&gt;
&lt;p&gt;With the addition of the fact, though, I only see the kinds of traces in the instance viewer that I was expecting. For example:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Start off with nothing:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;git-trace-0.png&quot; alt=&quot;An instance with only the repo.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;hash an object:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;git-trace-1.png&quot; alt=&quot;An instance with the repo and a single blob.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;stage the object:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;git-trace-2.png&quot; alt=&quot;The previous instance, but with the blob staged.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;write a tree:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;git-trace-3.png&quot; alt=&quot;The previous instance, but with a tree pointing to the blob. The tree is labeled &amp;quot;no duplicate trees t2&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;without resetting, write another tree:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;git-trace-4.png&quot; alt=&quot;The previous instance, with an additional tree pointing to the blob. The tree is labeled &amp;quot;no duplicate trees t1&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I think we&#x27;re good at this point! Just to recap, today we&#x27;ve modeled:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;blobs and &lt;code&gt;hash-object&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;the plumbing we need to make trees—a repo, the index, staging, and resetting&lt;&#x2F;li&gt;
&lt;li&gt;trees and &lt;code&gt;write-tree&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Next time we&#x27;ll go a bit further by adding commits. Stay tuned!&lt;&#x2F;p&gt;
&lt;p&gt;Before I leave you, though, here&#x27;s the complete model from this post. It&#x27;s quite a bit longer than other ones we&#x27;ve worked with, but it&#x27;s also doing way more:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; hashObject&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;var&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;there can only be &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt; empty tree&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;lone&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;| no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Repo&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  var index&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; updateIndex&lt;&#x2F;span&gt;&lt;span&gt;[bt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree] {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;bt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; writeTree&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Tree {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Repo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; resetIndex&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; index&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; frame&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; init&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; stutter&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Blob&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  Tree&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  index&amp;#39; &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; index&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;pred&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; traces&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  init&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    stutter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; hashObject&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt; bt&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; updateIndex&lt;&#x2F;span&gt;&lt;span&gt;[bt])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; writeTree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    or&lt;&#x2F;span&gt;&lt;span&gt; resetIndex&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; { traces }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; TreesCannotReferToThemselves&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; TreesAreImmutable&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; always t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children&amp;#39;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; NoDuplicateTrees&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  always (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;no&lt;&#x2F;span&gt;&lt;span&gt; disjoint t1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; t2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
	</entry>
	<entry xml:lang="en">
		<title>Modeling Git Internals in Alloy, Part 2: Commits and Tags</title>
		<published>2023-04-10T00:00:00+00:00</published>
		<updated>2023-04-10T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-git-internals-in-alloy-part-2-commits-and-tags/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-git-internals-in-alloy-part-2-commits-and-tags/</id>
		<content type="html">&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-git-internals-in-alloy-part-1-blobs-and-trees&#x2F;&quot;&gt;Last week&lt;&#x2F;a&gt;, we started modeling Git&#x27;s internals in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;. We added blobs (to store content) and trees (to organize it into a filesystem.) We ended up with this model:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;abstract sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;trees cannot refer to themselves&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;… which produces instances that look like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;normal-trees-and-blobs.png&quot; alt=&quot;an Alloy instance showing a tree containing another tree and a blob. The child tree contains a second blob.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Today, we&#x27;re going to add commits and tags to this model!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;commits&quot;&gt;Commits&lt;&#x2F;h2&gt;
&lt;p&gt;Going back to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;book.git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Internals-Git-Objects&quot;&gt;the &lt;em&gt;Git Internals - Git Objects&lt;&#x2F;em&gt; chapter of the Git book&lt;&#x2F;a&gt;, we can take a tree hash we produced in the last post and make a commit with &lt;code&gt;git commit-tree&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git commit-tree 3ee29075 -m &amp;#39;Commit message&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8cc0d4f4ddfde6efa9a8fced667d4d51574a36ec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can add more commits (and history) by repeating this command, but specifying a parent ID for each subsequent commit.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git commit-tree 3ee29075 -m &amp;#39;Second commit&amp;#39; -p 8cc0d4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;bc8d9d27a206d0e933be3e445c82cbef09da54d1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git commit-tree 3ee29075 -m &amp;#39;Third commit&amp;#39; -p bc8d9d&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;844bcca25118c27b0322aacd49edb73d8fac827f&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then we can view the lineage of the most recent commit with &lt;code&gt;git log&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git log 844bcc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;commit 844bcca25118c27b0322aacd49edb73d8fac827f&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Author: Brian Hicks &amp;lt;brian@brianthicks.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Date:   Fri Mar 3 12:36:14 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Third commit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;commit bc8d9d27a206d0e933be3e445c82cbef09da54d1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Author: Brian Hicks &amp;lt;brian@brianthicks.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Date:   Fri Mar 3 12:35:49 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Second commit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;commit 8cc0d4f4ddfde6efa9a8fced667d4d51574a36ec&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Author: Brian Hicks &amp;lt;brian@brianthicks.com&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Date:   Fri Mar 3 12:32:37 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    Commit message&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But… would it work to commit a blob hash, or does it only work with tree? The book doesn&#x27;t say, so I tried, and it looks like the hash you pass in as the first argument to git commit-tree must be a tree. If you try to make a commit based on a blob, git won&#x27;t let you:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git cat-file -p 3ee29075&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;100644 blob 39528abd81b13b2731d47f86206351a61f1e6484    hello-alloy.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;100644 blob 9b4b40c2bca67e781930105fa190b9b90235cfe5    hello-blob.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git cat-file -p 39528a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Hello, Alloy!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git commit-tree 39528a -m &amp;#39;Can you commit a blob?&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fatal: 39528abd81b13b2731d47f86206351a61f1e6484 is not a valid &amp;#39;tree&amp;#39; object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So it looks like a commit has to have a tree, a message, and zero or more parents (you can have more than one; this is how merge commits work.) All this is confirmed by &lt;code&gt;man git-commit-tree&lt;&#x2F;code&gt;! We&#x27;ll leave messages out of our model because they don&#x27;t matter for any properties we might care about, but otherwise we&#x27;ll add this to our model:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  parent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;finding-mismatches-between-git-s-model-and-ours&quot;&gt;Finding mismatches between Git&#x27;s model and ours&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s look at the instances Alloy produces and see if we think any of that feels off. To start, we get relatively normal-looking instances, such as two commits with the same tree:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;two-commits-pointing-to-the-same-tree.png&quot; alt=&quot;An Alloy instance showing two commits referencing the same tree.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But we also get some wilder instances. For example, it looks like our model allows trees to have commits as children:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;tree-with-commit-child.png&quot; alt=&quot;An Alloy instance showing a tree with a commit as a child.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not sure whether that&#x27;d be allowed, but it&#x27;s easy to verify by asking Git to add a commit to the staging area:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git update-index --add --cacheinfo 100644 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  8cc0d4 commits-are-stageable.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;fatal: git update-index: --cacheinfo cannot add 8cc0d4&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Nope, doesn&#x27;t work. That&#x27;s fine. We&#x27;ll just update our definition of &lt;code&gt;Tree&lt;&#x2F;code&gt; to say that they can&#x27;t have commits as children. Since we&#x27;re dealing with sets here, we can write “all objects besides commits” as &lt;code&gt;Object - Commit&lt;&#x2F;code&gt;, which makes the new definition of &lt;code&gt;Tree&lt;&#x2F;code&gt; look like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Object &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; Commit&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s not all the weirdness taken care of, though: we also get commits which are their own parents, or cycles of commits who are each other&#x27;s parents:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;commits-who-are-their-own-parents.png&quot; alt=&quot;An Alloy instance showing a commit which has itself as a parent.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Like last time, this is &lt;em&gt;technically&lt;&#x2F;em&gt; possible: if you can find just the right content for the commit messages and trees, you could conceivably get a commit to refer to itself. Like before, though, this is likely to break git in some awful ways (segfaults!) If we were modeling Git to try to find bugs or security vulnerabilities, I&#x27;d say we should allow this. But, as before, we&#x27;re trying to learn how this is &lt;em&gt;supposed&lt;&#x2F;em&gt; to work, so let&#x27;s disallow it in the same way we disallowed trees being their own parent:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;commits can&amp;#39;t be their own parent&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Commit &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; c &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;tags&quot;&gt;Tags&lt;&#x2F;h2&gt;
&lt;p&gt;With commits done, we have only one more object type to model: the tag. Tags are like commits, but instead of pointing to a tree and parent they point to a commit, and you can move them later (as opposed to everything else we&#x27;ve seen so far, which is immutable.) Here&#x27;s how we&#x27;d model that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tag&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running the model like this shows that we&#x27;ve implicitly allowed trees to contain tags (because now &lt;code&gt;Object - Commit&lt;&#x2F;code&gt; includes &lt;code&gt;Tag&lt;&#x2F;code&gt;) which we didn&#x27;t mean. We &lt;em&gt;could&lt;&#x2F;em&gt; say &lt;code&gt;Object - Commit - Tag&lt;&#x2F;code&gt;, but at this point I think it&#x27;d be better to rephrase &lt;code&gt;Tree.children&lt;&#x2F;code&gt; to contain only what we want:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can get tags on commits. Yay!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;tagged-commit.png&quot; alt=&quot;An Alloy instance showing a tag attached to a commit.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve now reached the end of the first part of our Git-modeling journey: we have all the objects! (There are also refs, though, which work like tags but aren&#x27;t stored with the git objects. You can read more about those in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;book.git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Internals-Git-References&quot;&gt;the Git Internals - Git References chapter of the Git book&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the model we&#x27;re finishing with:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;abstract sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;trees cannot refer to themselves&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  parent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;commits can&amp;#39;t be their own parent&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Commit &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; c &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tag&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;From here, our next step is to model the operations we can take on this model to check if the properties we wrote earlier actually hold when we use Git&#x27;s commands. Stay tuned!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Modeling Git Internals in Alloy, Part 1: Blobs and Trees</title>
		<published>2023-04-03T00:00:00+00:00</published>
		<updated>2023-04-03T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-git-internals-in-alloy-part-1-blobs-and-trees/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-git-internals-in-alloy-part-1-blobs-and-trees/</id>
		<content type="html">&lt;p&gt;Today we&#x27;re going to learn some &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; by modeling &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git-scm.com&#x2F;&quot;&gt;Git&lt;&#x2F;a&gt; objects.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-alloy&quot;&gt;What&#x27;s Alloy?&lt;&#x2F;h2&gt;
&lt;p&gt;I wrote this post for people who are meeting Alloy for the first time. If that&#x27;s you, here&#x27;s a quick intro: Alloy is a piece of software (with its own domain-specific programming language) used for making and checking models of other software. You use it to do &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Formal_methods#Lightweight_formal_methods&quot;&gt;lightweight formal methods&lt;&#x2F;a&gt;&quot;, focusing on approachable language and practical applications over full specification and formal proofs.&lt;&#x2F;p&gt;
&lt;p&gt;Alloy helps you think through problems by showing you both what&#x27;s possible and implied in the models you give it. It can model whatever you care to express in its language, although some things lend themselves to modeling better than others. Some things I&#x27;ve had good luck with: data structures, database schemas, and UI states.&lt;&#x2F;p&gt;
&lt;p&gt;Alloy can exhaustively check conditions in models &lt;em&gt;under a certain size&lt;&#x2F;em&gt;. This gives us a tradeoff: you don&#x27;t get a full proof that your condition holds (although you can get close enough for most purposes) but it can execute your specs much faster than other tools in the formal methods space. This is almost always fine. Just be cautious about saying things like &quot;I &lt;em&gt;proved&lt;&#x2F;em&gt; this using Alloy&quot;—it&#x27;s not that kind of tool.&lt;&#x2F;p&gt;
&lt;p&gt;You can download Alloy at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.org&quot;&gt;alloytools.org&lt;&#x2F;a&gt;. If you&#x27;re interested in learning more after reading this, I&#x27;ve also written &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-database-tables-in-alloy&#x2F;&quot;&gt;modeling database tables in Alloy&lt;&#x2F;a&gt; as well as a &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;few&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;the-value-of-a-model-is-more-making-than-having&#x2F;&quot;&gt;other&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;fields-as-sets&#x2F;&quot;&gt;posts&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;git-s-internals&quot;&gt;Git&#x27;s Internals&lt;&#x2F;h2&gt;
&lt;p&gt;Now, to Git. Git stores all the code and commits in your repo in a content-addressable store. That means that if you know the hash of something, you can retrieve it from the store. This allows Git to do cool things like syncing and deduplication, but it&#x27;s also the source of some of the weirder parts of Git&#x27;s behavior from a beginner&#x27;s perspective. Once I learned about the internals, I found it a lot easier to reason about what it was doing. If you&#x27;re learning about them for the first time now, I hope you have a similar experience!&lt;&#x2F;p&gt;
&lt;p&gt;I based these models off of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;book.git-scm.com&#x2F;book&#x2F;en&#x2F;v2&#x2F;Git-Internals-Git-Objects&quot;&gt;the &lt;em&gt;Git Internals - Git Objects&lt;&#x2F;em&gt; chapter of the Git book&lt;&#x2F;a&gt;. I won&#x27;t go into nearly as much detail about how Git works as they do there; this post will be more about modeling. If learning both the modeling tool and the domain at once is too much, go read that chapter and come back.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;blobs&quot;&gt;Blobs&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s look at blobs (used for basic content storage) first. We can tell &lt;code&gt;git&lt;&#x2F;code&gt; to create one like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ echo &amp;#39;Hello, blob!&amp;#39; | git hash-object -w --stdin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;9b4b40c2bca67e781930105fa190b9b90235cfe5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Git gives us a hash of the content on stdout. To retrieve it again, we just ask:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git cat-file -p 9b4b40c2bca67e781930105fa190b9b90235cfe5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Hello, blob!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Like I mentioned above, these blob objects are content-addressed. In practice, that means they can&#x27;t be duplicated. If you run the command to store &lt;code&gt;Hello, blob!&lt;&#x2F;code&gt; twice, Git will give you the hash again but not change anything on disk (since the blob is already stored.) We can get a &lt;em&gt;new&lt;&#x2F;em&gt; blob, along with a new hash, by changing the content:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ echo &amp;#39;Hello, Alloy!&amp;#39; | git hash-object -w --stdin&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;39528abd81b13b2731d47f86206351a61f1e6484&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(Git produces these hashes consistently across different installations, by the way. Try running them in some repo on your computer. You should get the same hashes!)&lt;&#x2F;p&gt;
&lt;p&gt;Since this is basically the same behavior as sets, we can model the whole thing in Alloy like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This introduces a &lt;code&gt;sig&lt;&#x2F;code&gt;, our basic unit of modeling. I find it easiest to think of &lt;code&gt;sig&lt;&#x2F;code&gt;s as sets: we can have zero &lt;code&gt;Blob&lt;&#x2F;code&gt;s, or one, or many, but never more than one of the same &lt;code&gt;Blob&lt;&#x2F;code&gt; in the set.&lt;&#x2F;p&gt;
&lt;p&gt;We can ask Alloy to execute this model, giving us examples of &lt;code&gt;Blob&lt;&#x2F;code&gt;. If we do so, then click &quot;Show&quot;, it gives us a window like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;alloy-show-one-blob.png&quot; alt=&quot;the Alloy evaluator showing a single blob instance, a box labeled blob&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can click &quot;New&quot; a bunch of times here. Alloy will start off with no blobs, then show one, then two, three, and four. At four, if you click &quot;New&quot; again Alloy will tell you that &quot;There are no more satisfying instances.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;no-more-satisfying-instances.png&quot; alt=&quot;a dialog box saying &amp;quot;There are no more satisfying instances. Note: due to symmetry breaking and other optimizations, some equivalent solutions may have been omitted.&amp;quot;&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This means that we&#x27;ve seen all the possible ways for there to be blobs under the current configuration. Alloy limits us to four of each &lt;code&gt;sig&lt;&#x2F;code&gt; by default. We can raise this if we need, but in my experience Alloy can find most interesting things under this limit.&lt;&#x2F;p&gt;
&lt;p&gt;At any rate, no matter how many blobs we have, we still haven&#x27;t modeled enough to show useful things about Git. Next up in the Git book: trees!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trees&quot;&gt;Trees&lt;&#x2F;h2&gt;
&lt;p&gt;Trees work basically like directories in a filesystem hierarchy. Their hash is based on the hash of all the content they contain, plus the filenames of that content. We can create one by associating blobs with filenames in the index (kinda like &lt;code&gt;git add foo.txt&lt;&#x2F;code&gt; but with the object hash instead of reading from the project source.) It looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git update-index --add --cacheinfo 100644 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  9b4b40c2bca67e781930105fa190b9b90235cfe5 hello-blob.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git update-index --add --cacheinfo 100644 \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  39528abd81b13b2731d47f86206351a61f1e6484 hello-alloy.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git write-tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3ee29075f260c5eebd8b9480b6464a7612668dde&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us a tree object containing the hashes we wrote before. We &lt;em&gt;might&lt;&#x2F;em&gt; try and model trees like this in Alloy:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Blob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But that won&#x27;t work: trees can also contain trees! We can demonstrate using &lt;code&gt;git read-tree&lt;&#x2F;code&gt; (it works like &lt;code&gt;update-index --add&lt;&#x2F;code&gt;, but puts the content of the tree under the given prefix.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git reset --hard # to clear out the staged files&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git read-tree --prefix=greetings 3ee29075f260c5eebd8b9480b6464a7612668dde&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git write-tree&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;a0776403e71047444987c67405757a1dbeb0f263&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git cat-file -p a0776403e71047444987c67405757a1dbeb0f263&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;040000 tree 3ee29075f260c5eebd8b9480b6464a7612668dde    greetings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We need to change our modeling to allow for this. Luckily for us, it turns out that both blobs and trees belong to the category of &quot;Git objects.&quot; Alloy makes this easy to model:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;abstract sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A &lt;code&gt;sig&lt;&#x2F;code&gt; being &lt;code&gt;abstract&lt;&#x2F;code&gt; means that there aren&#x27;t ever going to be any things that are &lt;em&gt;only&lt;&#x2F;em&gt; &lt;code&gt;Object&lt;&#x2F;code&gt;, but that &lt;code&gt;Object&lt;&#x2F;code&gt; can serve as a superset for anything extending it (you can think of it like an abstract class in an object-oriented language.)&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Blob&lt;&#x2F;code&gt; and &lt;code&gt;Tree&lt;&#x2F;code&gt; are now defined as &lt;em&gt;extending&lt;&#x2F;em&gt; &lt;code&gt;Object&lt;&#x2F;code&gt;. This means that they are non-overlapping subsets of &lt;code&gt;Object&lt;&#x2F;code&gt;. In other words, nothing is both a &lt;code&gt;Blob&lt;&#x2F;code&gt; and a &lt;code&gt;Tree&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we give &lt;code&gt;Tree&lt;&#x2F;code&gt; some &lt;code&gt;children&lt;&#x2F;code&gt;. Defining it with &lt;code&gt;set&lt;&#x2F;code&gt; means that we could have zero, one, or many children (which is possible in &lt;code&gt;git&lt;&#x2F;code&gt;; I checked on an empty repo.) If we want one or more, we could say &lt;code&gt;some&lt;&#x2F;code&gt;. There&#x27;s also &lt;code&gt;one&lt;&#x2F;code&gt; (which is what it sounds like) and &lt;code&gt;lone&lt;&#x2F;code&gt; (which is zero or one exactly.) These are all called &quot;multiplicity operators.&quot; You don&#x27;t have to specify one, although I always do because I forget what the default is and it&#x27;s nicer to be explicit anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s execute this model and have a look at some of the instances. For example, we can get totally normal-looking trees which contain other trees and blobs:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;normal-trees-and-blobs.png&quot; alt=&quot;an Alloy instance showing a tree containing another tree and a blob. The child tree contains a second blob.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;However, we&#x27;ve accidentally allowed some other stuff like…&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recursion-dun-dun-dunnnnn&quot;&gt;Recursion! (dun dun dunnnnn)&lt;&#x2F;h2&gt;
&lt;p&gt;Our current model allows the data structures we want, but it also allows stuff that doesn&#x27;t make sense in Git. For example, trees can contain themselves, and contain the trees that contain them. Here&#x27;s an example with all that happening at once:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;trees-containing-themselves-and-each-other.png&quot; alt=&quot;an Alloy instance showing two trees which both have each other and themselves as children.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This shouldn&#x27;t be possible, theoretically. Remember that this is a content-addressed storage system, so you need to know something&#x27;s hash to refer to it. That means for something to refer to itself, you&#x27;d have to know the hash in advance, and the hash of the thing you create would change depending on that hash. Since hash algorithms aren&#x27;t perfect, you can get two values that produce the same hash (a collision) but you&#x27;d have to get really lucky (or unlucky, depending on your perspective.)&lt;&#x2F;p&gt;
&lt;p&gt;I was curious about whether this was actually even possible or if Git would reject it, so I did some digging. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;git&#x2F;comments&#x2F;6kkb3k&#x2F;a_tree_that_references_itself&#x2F;&quot;&gt;Someone on Reddit&lt;&#x2F;a&gt; had the same question and changed the source code to be dramatically more likely to have a collision (by changing the code to only use the first byte of the SHA-1 hash.) They got a segfault, which tells me that this isn&#x27;t something that the Git authors designed for.&lt;&#x2F;p&gt;
&lt;aside&gt;
&lt;p&gt;&lt;strong&gt;Sidebar: what are we modeling, again?&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We hit a fork in the road here in practical modeling terms: we can either allow or disallow this case in our model. This happens to me a lot when using Alloy; it&#x27;s good at bringing up weird cases I hadn&#x27;t previously considered. In this case, I think you can make a good argument for either direction:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If we allow recursion in trees, we&#x27;re keeping our eyes open to the fact that SHA-1 is not completely collision-free, or that some day it may be broken in a way that makes it trivial to find collisions.&lt;&#x2F;li&gt;
&lt;li&gt;If we disallow recursion in trees, we simplify our conceptual model significantly. We&#x27;re not nearly done with the data model (we still have commits, refs, tags, etc to add) and allowing every single little edge case will make it harder to understand the system as a whole.
In this case we&#x27;re modeling to learn about Git&#x27;s internals, so I think disallowing this case makes more sense. Git clearly relies on the fact that it&#x27;s very hard to cause a cycle here. If I was modeling Git objects to (for example) find bugs or potential security issues, I think I&#x27;d make the opposite decision.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;aside&gt;
&lt;p&gt;We&#x27;ll use a &lt;code&gt;fact&lt;&#x2F;code&gt; to disallow this case. When we declare something as &lt;code&gt;fact&lt;&#x2F;code&gt;, Alloy will not allow any instances which would make the fact false. We should use these carefully—as in other kinds of reasoning, more axioms mean making a weaker argument (even though it&#x27;s hard to not use any at all.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;trees cannot refer to themselves&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;^&lt;&#x2F;code&gt; operator is new to us—it follows the &quot;children&quot; relationship one or more times. That means that we&#x27;re saying &quot;there is no &lt;code&gt;Tree&lt;&#x2F;code&gt; that is it&#x27;s own child, or that child&#x27;s child, or that child&#x27;s child&#x27;s child, and so on.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Once we do this, Alloy cannot find any more instances where trees refer to themselves, but it can still find plenty where trees contain other trees or blobs. Success!&lt;&#x2F;p&gt;
&lt;p&gt;We &lt;em&gt;can&lt;&#x2F;em&gt;, however, still get two trees pointing to the same blob. If we assume that these two trees are using the same filename for the blob, this shouldn&#x27;t be possible, because they&#x27;d have the same hash.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;two-trees-pointing-to-the-same-blob.png&quot; alt=&quot;an Alloy instance showing two trees containing the same child blob.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;So we find ourselves at another fork in the road: we could either model naming files within trees or not. Like last time, you can make a convincing argument either way:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If we add names, we&#x27;ll be sticking closer to the way the actual system works. Trees exist as the equivalent of a directory in a filesystem, so it&#x27;s a little weird to ignore names totally.&lt;&#x2F;li&gt;
&lt;li&gt;If we don&#x27;t add names, we&#x27;ll keep the model simpler. However, we can assume that any time we see two trees with the same contents, at least child has a different name. Since a directory entry is a string, and there are effectively an infinite amount of strings, this seems like a safe assumption.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Again, I have to ask &lt;em&gt;what we&#x27;re modeling&lt;&#x2F;em&gt;. If, right now, we were modeling the way that &lt;code&gt;git checkout&lt;&#x2F;code&gt; turns a tree into files and directories in the filesystem, we might want to add names. But since we&#x27;re trying to understand Git&#x27;s internal structure, it probably makes sense to leave them out (it makes our &lt;code&gt;fact&lt;&#x2F;code&gt; above much nicer, for one.) I usually add a comment documenting this, though, and I will in our final model.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;that-s-all-for-now&quot;&gt;That&#x27;s all… for now&lt;&#x2F;h2&gt;
&lt;p&gt;Today we&#x27;ve learned: how to make a &lt;code&gt;sig&lt;&#x2F;code&gt;, how to connect two &lt;code&gt;sig&lt;&#x2F;code&gt;s together, and how to force Alloy to disallow certain cases. We&#x27;ve also (maybe) learned a little bit about blobs and trees. The full, final model is below:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;abstract sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Blob&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tree&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Object&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; We&amp;#39;re not modeling the mapping from name to object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; for simplicity&amp;#39;s sake. If you see two trees with the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; same children, assume it&amp;#39;s fine because the children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; have different file names.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Object&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;trees cannot refer to themselves&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tree &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Over the next couple of posts, we&#x27;ll build on this by:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;adding more Git object! Commits, references, and tags.&lt;&#x2F;li&gt;
&lt;li&gt;modeling how commands like &lt;code&gt;git hash-object -w&lt;&#x2F;code&gt;, &lt;code&gt;git update-index&lt;&#x2F;code&gt;, and &lt;code&gt;git write-tree&lt;&#x2F;code&gt; modify the object database to verify that statements like &quot;trees can&#x27;t refer to themselves&quot; are actually true given the operations we can run.&lt;&#x2F;li&gt;
&lt;li&gt;modeling how everyday Git commands (the &quot;porcelain&quot;; things like &lt;code&gt;git add&lt;&#x2F;code&gt; and &lt;code&gt;git commit&lt;&#x2F;code&gt;) map onto these lower-level operations.&lt;&#x2F;li&gt;
&lt;li&gt;and, if we&#x27;re very lucky, we can model how &lt;code&gt;git&lt;&#x2F;code&gt; syncs this all around when you run &lt;code&gt;git push&lt;&#x2F;code&gt; or &lt;code&gt;git pull&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Stay tuned! If you&#x27;d like to be notified when I publish any of those posts, you can subscribe below.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>aligning Markdown tables in Helix</title>
		<published>2023-03-13T00:00:00+00:00</published>
		<updated>2023-03-13T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/aligning-markdown-tables-in-helix/" type="text/html"/>
		<id>https://bytes.zone/posts/aligning-markdown-tables-in-helix/</id>
		<content type="html">&lt;p&gt;One of the things I like most about using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;helix-editor.com&#x2F;&quot;&gt;Helix&lt;&#x2F;a&gt; (and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kakoune.org&#x2F;&quot;&gt;Kakoune&lt;&#x2F;a&gt;, when I used it before) is the multiple cursor support. I use them whenever I can! Today, I want to share how Helix solves a common task: formatting a Markdown table.&lt;&#x2F;p&gt;
&lt;p&gt;When I first write Markdown tables, they tend to look like this (with some data from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;datasets&#x2F;rtatman&#x2F;lego-database?select=parts.csv&quot;&gt;this LEGO Database on Kaggle&lt;&#x2F;a&gt;.) Ragged-right formatting and just enough syntax for a parser to tell the headers from the rows:&lt;&#x2F;p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| ID | Name | Category ID |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|-|-|-|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11092 | Gorilla Fist | 27 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11212 | Plate 3 x 3 | 14 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11209 | Tyre 21 x 9.9 | 29 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11640pr0003 | ELECTRIC GUITAR SHAFT Ø3.2 NO. 3 | 27 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This looks fine when rendered, but it&#x27;s somewhat annoying in the source. In other editors I&#x27;ve used, you either need to do the alignment by hand (tedious!) or use some plugin. In Helix and Kakoune, doing this is fairly simple: you just need to combine some editing primitives.&lt;&#x2F;p&gt;
&lt;p&gt;Bottom-line-up-front, the key sequence is &lt;code&gt;mips\|&amp;lt;ret&amp;gt;&amp;amp;&lt;&#x2F;code&gt;. But that looks just like line noise, so let&#x27;s break it down:&lt;&#x2F;p&gt;
&lt;p&gt;First, I hit &lt;code&gt;mip&lt;&#x2F;code&gt; to select the whole paragraph, then select pipes (&lt;code&gt;s\|&amp;lt;ret&amp;gt;&lt;&#x2F;code&gt;.) That gives me one selection per pipe character:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;one-selection-per-character-in-a-markdown-table.png&quot; alt=&quot;screenshot of Helix, in which each pipe character from the code sample above is selected by a rectangular cursor.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Then I hit &lt;code&gt;&amp;amp;&lt;&#x2F;code&gt;, the “align selections” operator, which gets me the final alignment:&lt;&#x2F;p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| ID          | Name                             | Category ID |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|-            |-                                 |-            |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11092       | Gorilla Fist                     | 27          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11212       | Plate 3 x 3                      | 14          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11209       | Tyre 21 x 9.9                    | 29          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11640pr0003 | ELECTRIC GUITAR SHAFT Ø3.2 NO. 3 | 27          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This still renders just fine, but it&#x27;s a little nicer reading experience to have the line separating the headers from the body rows full of hyphens. That one&#x27;s easy too: after dismissing the selections with &lt;code&gt;,&lt;&#x2F;code&gt;, you can navigate to the row, hit &lt;code&gt;x&lt;&#x2F;code&gt; to select the whole line, then &lt;code&gt;s&lt;&#x2F;code&gt; to select characters, enter space as the section, and &lt;code&gt;r-&lt;&#x2F;code&gt; to replace all the selections with hyphens. Now it&#x27;s a perfectly nice Markdown table that&#x27;s very pleasant to read in the source:&lt;&#x2F;p&gt;
&lt;!-- prettier-ignore --&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| ID          | Name                             | Category ID |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|-------------|----------------------------------|-------------|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11092       | Gorilla Fist                     | 27          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11212       | Plate 3 x 3                      | 14          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11209       | Tyre 21 x 9.9                    | 29          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| 11640pr0003 | ELECTRIC GUITAR SHAFT Ø3.2 NO. 3 | 27          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;</content>
	</entry>
	<entry xml:lang="en">
		<title>how to add weak fairness on actions</title>
		<published>2023-03-06T00:00:00+00:00</published>
		<updated>2023-03-06T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/how-to-add-weak-fairness-on-actions/" type="text/html"/>
		<id>https://bytes.zone/posts/how-to-add-weak-fairness-on-actions/</id>
		<content type="html">&lt;p&gt;In &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;, you can add weak fairness to actions (that is, ensure that they&#x27;ll always eventually fire if they&#x27;re able) by saying:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(eventually always p) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; (always eventually q)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In English, this means: &quot;if &lt;code&gt;p&lt;&#x2F;code&gt; is eventually true, then &lt;code&gt;q&lt;&#x2F;code&gt; will eventually be true at all points afterwards.&quot; In other words, &lt;code&gt;p&lt;&#x2F;code&gt; is the condition under which &lt;code&gt;q&lt;&#x2F;code&gt; can happen.&lt;&#x2F;p&gt;
&lt;p&gt;Note that you should be careful about existential (&lt;code&gt;some&lt;&#x2F;code&gt;) vs universal (&lt;code&gt;all&lt;&#x2F;code&gt;) quantifiers with this kind of statement. For example:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Thing {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (eventually always &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;enabled&lt;&#x2F;span&gt;&lt;span&gt;[t]) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;implies&lt;&#x2F;span&gt;&lt;span&gt; (always eventually &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;do&lt;&#x2F;span&gt;&lt;span&gt;[t])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Saying &lt;code&gt;all&lt;&#x2F;code&gt; here means that every time the condition &lt;code&gt;enabled&lt;&#x2F;code&gt; is true the action &lt;code&gt;do&lt;&#x2F;code&gt; is eventually taken. If this said &lt;code&gt;some t: Thing&lt;&#x2F;code&gt; instead, that would mean that this condition would only be true some of the time.&lt;&#x2F;p&gt;
&lt;p&gt;Source: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.discourse.group&#x2F;t&#x2F;how-do-you-say-once-this-is-true-its-always-true&#x2F;294&quot;&gt;Alcino Cunha&#x27;s answer to my question about this on the Alloy discourse instance&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>give aliases when inverting relations</title>
		<published>2023-02-27T00:00:00+00:00</published>
		<updated>2023-02-27T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/give-aliases-when-inverting-relations/" type="text/html"/>
		<id>https://bytes.zone/posts/give-aliases-when-inverting-relations/</id>
		<content type="html">&lt;p&gt;Say you&#x27;re using &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; to model some tree structure. For example, a file system with directories and files:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;abstract sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  parent&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: lone&lt;&#x2F;span&gt;&lt;span&gt; Node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; File&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Directory&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; extends&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Node&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;a node cannot be its own parent&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Node &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; n &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;there is only &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt; root node&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  one&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Node &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;| no&lt;&#x2F;span&gt;&lt;span&gt; n&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This gives us nice file system objects that always terminate in a single node and without any recursive shenanigans, like this one:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;filesystem-two-dirs-two-files.png&quot; alt=&quot;an Alloy diagram showing two directories, each containing one file. One directory also contains the other.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;However, the model currently implicitly allows this that we don&#x27;t want to include based on our mental model of the filesystem. For example, files that have other files as their parent:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;filesystem-parenting-files.png&quot; alt=&quot;an Alloy diagram showing a file containing to other files&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That doesn&#x27;t work: a directory can have children, but a file needs to have contents instead (e.g., images, text, etc.) So let&#x27;s disallow that! We&#x27;ll do that by adding a new fact like “files shouldn&#x27;t have any children.” We &lt;em&gt;could&lt;&#x2F;em&gt; do that by saying this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;files shouldn&amp;#39;t have any children&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; File&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.~&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(In case you haven&#x27;t encountered the &lt;code&gt;~&lt;&#x2F;code&gt; operator before, it just flips the relation. That makes it child-to-parent instead of parent-to-child. More about this in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;fields-as-sets&#x2F;&quot;&gt;fields as sets&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;Alloy can work fine with this, but in my opinion, using &lt;code&gt;~&lt;&#x2F;code&gt; like this makes the intent of the spec less clear. I always have to think about what the &lt;code&gt;~&lt;&#x2F;code&gt; is changing, even if I wrote the code not ten minutes before! Fortunately, we can clarify this by making an alias with a &lt;code&gt;fun&lt;&#x2F;code&gt; and then define our &lt;code&gt;fact&lt;&#x2F;code&gt; in those terms instead:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; children&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Node &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Node {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  ~&lt;&#x2F;span&gt;&lt;span&gt;parent&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;files shouldn&amp;#39;t have any children&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; File&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This seems much clearer to me; the assertion we&#x27;re making reads almost exactly how we&#x27;d write it in English. I like that a lot! It makes communicating about the spec easier, both for me and for anyone else who ends up reading it.&lt;&#x2F;p&gt;
&lt;p&gt;In general, I&#x27;m finding that I really like to use &lt;code&gt;fun&lt;&#x2F;code&gt;s to clarify my intent, or to put a durable label on some form of node. Doing this requires a little mindset shift from writing &lt;code&gt;pred&lt;&#x2F;code&gt;s or &lt;code&gt;fact&lt;&#x2F;code&gt;s, since &lt;code&gt;fun&lt;&#x2F;code&gt;s have to yield a subset of data instead of a boolean, but in my experience it results in higher-quality communication!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>what is the randomart image for?</title>
		<published>2023-02-20T00:00:00+00:00</published>
		<updated>2023-02-20T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/what-is-the-randomart-image-for/" type="text/html"/>
		<id>https://bytes.zone/posts/what-is-the-randomart-image-for/</id>
		<content type="html">&lt;p&gt;When you generate an SSH key (like I did when looking at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;signing-commits-with-ssh-keys&#x2F;&quot;&gt;signing commits with SSH keys&lt;&#x2F;a&gt;), you get a “randomart image” from &lt;code&gt;ssh-keygen&lt;&#x2F;code&gt;. These things:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+--[ED25519 256]--+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|..o              |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|.+               |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .E              |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|.. .     +o o    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .+.    S+=+     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .... o.*+o*     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     oo*.o+oo.   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     o o=+.oo+   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     .o.oo==B++  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That looks nice, and I appreciate art in my terminal, but what is it for? So, let&#x27;s see… first, I looked in &lt;code&gt;man ssh-keygen&lt;&#x2F;code&gt;, because that&#x27;s the context in which I see these, but it doesn&#x27;t have any hits for &lt;code&gt;randomart&lt;&#x2F;code&gt; or &lt;code&gt;art&lt;&#x2F;code&gt;. But on a hunch I tried &lt;code&gt;man ssh&lt;&#x2F;code&gt;, and that page has some info! Here&#x27;s what it says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because of the difficulty of comparing host keys just by looking at fingerprint strings, there is also support to compare host keys visually, using random art. By setting the VisualHostKey option to “yes”, a small ASCII graphic gets displayed on every login to a server, no matter if the session itself is interactive or not. By learning the pattern a known server produces, a user can easily find out that the host key has changed when a completely different pattern is displayed. Because these patterns are not unambiguous however, a pattern that looks similar to the pattern remembered only gives a good probability that the host key is the same, not guaranteed proof.&lt;&#x2F;p&gt;
&lt;p&gt;To get a listing of the fingerprints along with their random art for all known hosts, the following command line can be used:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ ssh-keygen -lv -f ~&#x2F;.ssh&#x2F;known_hosts&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ok, that makes a lot of sense! There have been plenty of times when I&#x27;ve logged on to a new server, and it asks me if I want to trust key such-and-such. I mostly just say “yeah, that&#x27;s fine” without paying attention, so I see how the art could help me remember what&#x27;s right!&lt;&#x2F;p&gt;
&lt;p&gt;If I run the command given in the manual page, I get this for GitHub:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;256 SHA256:+DiY3wvvV6TuJJhbpZisF&#x2F;zLDA0zPMSvHdkr4UvCOqU github.com (ED25519)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+--[ED25519 256]--+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|                 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     .           |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|      o          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     o o o  .    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     .B S oo     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     =+^ =...    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|    oo#o@.o.     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|    E+.&amp;amp;.=o      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|    ooo.X=.      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2048 SHA256:nThbg6kXUpJWGl7E1IGOCspRomTxdCARLviKw6E5SY8 github.com (RSA)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---[RSA 2048]----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| =+o...+=o..     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|o++... *o .      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|*.o.  *o.        |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|oo.  ..o.= .     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|.+o. .. S =      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|*=+ .  o = .     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|OE .  . o        |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| o     .         |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|                 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;256 SHA256:p2QAMXNIC1TJYWeIOttrVc98&#x2F;R1BUFWu3&#x2F;LiyKgUfQM github.com (ECDSA)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---[ECDSA 256]---+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .o=X*+      .o.=|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|  .o=O         o |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .  . .   E   . .|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|o     .. . .   o |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| +   . +S o.o . .|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|. . .  o++.... o.|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   o    o.   ...+|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|  o    .   o .oo.|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| .      ... o....|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The manual also mentioned that you can set &lt;code&gt;VisualHostKey&lt;&#x2F;code&gt; to &lt;code&gt;yes&lt;&#x2F;code&gt; in your SSH client config to get that information every time. If I do that, I can see that my SSH client is using the ED25519 key:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; ssh git@github.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF&#x2F;zLDA0zPMSvHdkr4UvCOqU&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;+--[ED25519&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 256]--+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|                 |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;     .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;           |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;      o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;          |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;     o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; o o  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;     .B&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; S oo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;     =+^ =...&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    oo#o@.o.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    E+.&lt;&#x2F;span&gt;&lt;span&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;=o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    ooo.X&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;PTY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; allocation request failed on channel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Hi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; BrianHicks! You&amp;#39;ve successfully authenticated, but GitHub does not provide shell access.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;Connection to github.com closed.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It shows a different image when I &lt;code&gt;git push&lt;&#x2F;code&gt; to my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;dotfiles.nix&quot;&gt;dotfiles repo&lt;&#x2F;a&gt;, which also makes sense—they&#x27;re different servers, so they have different keys. I left this on for a while and see if I started to be able to recognize the randomart images in the way the manual implies I&#x27;d be able to, and it turns out it worked! For example, I&#x27;ve started seeing the GitHub fingerprint (above) as something like the Statue of Liberty if it were a cat (don&#x27;t ask why; that&#x27;s just how my brain sees it), and the signature for &lt;code&gt;git.bytes.zone&lt;&#x2F;code&gt; (below) as the Vagrant logo. How interesting!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+---[RSA 2048]----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|.+=+.         **o|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|...+ .       *.o+|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|o + o     . = =. |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;| +   . + . E . + |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|  o  .+ S =   + .|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|   o .o+ . o o o |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|    ..o.+   o .  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|     o...    .   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;|      oo         |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All in all, I think I&#x27;d recommend doing this. Even if the keys never change, it&#x27;s pleasing to see the art show up in your terminal. And if they do, I now have one more tool to know if something&#x27;s wrong.&lt;&#x2F;p&gt;
&lt;p&gt;One final note: I mentioned this in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.recurse.com&#x2F;&quot;&gt;Recurse Center&lt;&#x2F;a&gt; Zulip and someone mentioned that they had recently come across &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.dirk-loss.de&#x2F;sshvis&#x2F;drunken_bishop.pdf&quot;&gt;the paper that describes the algorithm for making these&lt;&#x2F;a&gt;. Now we know!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>fields as sets</title>
		<published>2023-02-13T00:00:00+00:00</published>
		<updated>2023-02-13T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/fields-as-sets/" type="text/html"/>
		<id>https://bytes.zone/posts/fields-as-sets/</id>
		<content type="html">&lt;p&gt;Today, let&#x27;s look at how relationships between &lt;code&gt;sig&lt;&#x2F;code&gt;s in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;learning-alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; work.&lt;&#x2F;p&gt;
&lt;p&gt;Say, for example, you&#x27;re modeling database tables (&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-database-tables-in-alloy&#x2F;&quot;&gt;as I have previously&lt;&#x2F;a&gt;) and want to represent the relationship between people and their favorite flavor of ice cream:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Person&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  favoriteIceCream&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: lone&lt;&#x2F;span&gt;&lt;span&gt; Flavor&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Flavor&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You can ask Alloy for examples of this, and it will show you a bunch of different ways &lt;code&gt;Person&lt;&#x2F;code&gt; and &lt;code&gt;Flavor&lt;&#x2F;code&gt; can be related. For example, here&#x27;s an instance where there are three distinct people who each like a different flavor:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;three-people-three-flavors.png&quot; alt=&quot;an Alloy instance describing three people, each with a distinct favorite ice cream flavor. The people and flavors are not connected otherwise.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;favoriteIceCream&lt;&#x2F;code&gt; relationship here can be used in a bunch of interesting ways. For example, if we want to assert (contrary to our use of &lt;code&gt;lone&lt;&#x2F;code&gt;) that everybody has a favorite ice cream flavor, we&#x27;d do it like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;check NobodyDoesntLikeIceCream {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  all p: Person | some p.favoriteIceCream&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alloy finds a counterexample for this (since we used &lt;code&gt;lone&lt;&#x2F;code&gt;, which means some people don&#x27;t have a favorite flavor of ice cream), but it demonstrates the first usage of the relation: looking up fields as if they were methods or attribute accesses in an object-oriented language. &lt;code&gt;x.fieldName&lt;&#x2F;code&gt; will always work basically like you might have already intuited.&lt;&#x2F;p&gt;
&lt;p&gt;Relations can do more than this, though, since they&#x27;re actually secretly sets of tuples. If you load up the instance above in Alloy and examine it in the table view, you&#x27;ll see that it&#x27;s defined something like this:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;code&gt;this&#x2F;Person&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;th&gt;&lt;code&gt;favoriteIceCream&lt;&#x2F;code&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Person$0&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Flavor$2&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Person$1&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Flavor$1&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Person$2&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;code&gt;Flavor$2&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This looks suspiciously like a table in a database, right? Well, good news: it basically works that way too!&lt;&#x2F;p&gt;
&lt;p&gt;What &lt;code&gt;.&lt;&#x2F;code&gt; does under the hood is essentially equivalent to a SQL join: it selects rows matching a pattern on the left and gives you access to the equivalent rows on the right.&lt;&#x2F;p&gt;
&lt;p&gt;The cool thing about &lt;code&gt;.&lt;&#x2F;code&gt; is that it can do this with a set in either position. Even though it &lt;em&gt;looks like&lt;&#x2F;em&gt; you&#x27;re using a single value with &lt;code&gt;p.favoriteIceCream&lt;&#x2F;code&gt;, &lt;code&gt;p&lt;&#x2F;code&gt; is actually a subset of people—it just happens to be one with only one member.&lt;&#x2F;p&gt;
&lt;p&gt;So what other sets can you do this to? Well, if we add a &lt;code&gt;children&lt;&#x2F;code&gt; field like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sig Person {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  children: set Person,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  favoriteIceCream: lone Flavor,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We could look at some parent &lt;code&gt;p&lt;&#x2F;code&gt; and get the ice cream flavors they&#x27;d need to buy for their children with &lt;code&gt;p.children.favoriteIceCream&lt;&#x2F;code&gt;, or their grandchildren with &lt;code&gt;p.children.children.favoriteIceCream&lt;&#x2F;code&gt;. Chaining works in the way you&#x27;d expect, except with set semantics instead of having to do loops or list comprehensions.&lt;&#x2F;p&gt;
&lt;p&gt;We could also get the set of reachable ice cream flavors by using the whole set of &lt;code&gt;Person&lt;&#x2F;code&gt; on the left: &lt;code&gt;Person.favoriteIceCream&lt;&#x2F;code&gt;. We could also get the sad, lonely flavors that nobody loves with &lt;code&gt;Flavor - Person.favoriteIceCream&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s not all you can do, though. The &lt;code&gt;~&lt;&#x2F;code&gt; operator flips a relation around: if &lt;code&gt;favoriteIceCream&lt;&#x2F;code&gt; is a mapping from &lt;code&gt;Person&lt;&#x2F;code&gt; to &lt;code&gt;Flavor&lt;&#x2F;code&gt;, &lt;code&gt;~favoriteIceCream&lt;&#x2F;code&gt; is one from &lt;code&gt;Flavor&lt;&#x2F;code&gt; to &lt;code&gt;Person&lt;&#x2F;code&gt;. We can then do the same tricks as above. For example, we can find out who doesn&#x27;t like ice cream at all by doing &lt;code&gt;Person - Flavor.~favoriteIceCream&lt;&#x2F;code&gt; (since that will be all the people, minus the people who have a favorite ice cream set.)&lt;&#x2F;p&gt;
&lt;p&gt;For me, realizing that basically everything is a set, and that &lt;code&gt;~&lt;&#x2F;code&gt; and &lt;code&gt;.&lt;&#x2F;code&gt; worked set-wise unlocked a lot of uses for Alloy that I hadn&#x27;t considered before. I hope reading this helped you, too!&lt;&#x2F;p&gt;
&lt;p&gt;By the way, we haven&#x27;t even gone over all the interesting things you can do: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloy.readthedocs.io&quot;&gt;Hillel Wayne&#x27;s documentation site&lt;&#x2F;a&gt; has a big section on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloy.readthedocs.io&#x2F;en&#x2F;latest&#x2F;language&#x2F;sets-and-relations.html#sets-and-relations&quot;&gt;sets and relations&lt;&#x2F;a&gt; that&#x27;s worth a read if you&#x27;d like to learn more.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>signing commits with SSH keys</title>
		<published>2023-02-06T00:00:00+00:00</published>
		<updated>2023-02-06T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/signing-commits-with-ssh-keys/" type="text/html"/>
		<id>https://bytes.zone/posts/signing-commits-with-ssh-keys/</id>
		<content type="html">&lt;p&gt;I sign all my git commits. “Why” is not super important here (but basically: I participate in some open source organizations for whom supply chain security is important.) Right now, I want to focus on nice ways to make the signatures.&lt;&#x2F;p&gt;
&lt;p&gt;To start with, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;authentication&#x2F;managing-commit-signature-verification&#x2F;signing-commits&quot;&gt;here are some GitHub docs&lt;&#x2F;a&gt; on signing commits with different kinds of keys. I currently use GPG keys, but they&#x27;re kind of annoying to manage (at least for me.) A better option for me might be to sign my commits with SSH keys. But, what are the consequences of doing that?&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m gonna run some experiments to figure that out. To start with, I use two git forges: GitHub and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gitea.io&#x2F;en-us&#x2F;&quot;&gt;Gitea&lt;&#x2F;a&gt; (although I plan to migrate to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;forgejo.org&#x2F;&quot;&gt;Forgejo&lt;&#x2F;a&gt; once it&#x27;s in a stable version of nixpkgs.) I want to make sure that I can get verified commits on both.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m &lt;em&gt;most&lt;&#x2F;em&gt; interested in what happens when I revoke keys. Generally speaking, I generate one SSH key per machine I use for development. That way, no key ever has to leave the machine it was generated on. So what happens if I retire a machine, along with its key? Will all my commits turn into unsigned commits? GitHub&#x27;s documentation implies that it might be fine, but I don&#x27;t know.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-stuff-out&quot;&gt;Testing Stuff Out&lt;&#x2F;h2&gt;
&lt;p&gt;Well, then: let&#x27;s try! I&#x27;m gonna create a new repo and key:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git init test-ssh-key-signing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Initialized&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; empty Git repository in test-ssh-key-signing&#x2F;.git&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; cd test-ssh-key-signing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; ssh-keygen&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; ed25519&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -f&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; test-key.ed25519&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Generating&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; public&#x2F;private ed25519 key pair.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; passphrase&lt;&#x2F;span&gt;&lt;span&gt; (empty&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; for no passphrase&lt;&#x2F;span&gt;&lt;span&gt;):&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Enter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; same passphrase again:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; identification has been saved in test-key.ed25519&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Your&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; public key has been saved in test-key.ed25519.pub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;The&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; key fingerprint is:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;SHA256:mevdSg3mn8+unpYYaB7+Sw2D6k4&#x2F;VmViamKCEHHma0M&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; brianhicks@sequoia.local&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;The&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; key&amp;#39;s randomart image is:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;+--[ED25519 256]--+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|..o              |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|.+               |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;| .E              |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|.. .     +o o    |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;| .+.    S+=+     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;| .... o.*+o*     |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|     oo*.o+oo.   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|     o o=+.oo+   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;|     .o.oo==B++  |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;+----[SHA256]-----+&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First, I&#x27;ll make a commit that&#x27;s signed with my normal GPG key (which I have set globally):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --allow-empty -S -m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;this commit has been signed with my regular GPG key&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then I&#x27;ll reconfigure the repo to sign using the local key and make another commit:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git config gpg.format ssh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git config user.signingKey &amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;)&#x2F;test-key.ed25519&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --allow-empty -S -m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;this commit has been signed with the test SSH key&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can check the signatures on the commits. Unfortunately, it looks like &lt;code&gt;git&lt;&#x2F;code&gt; needs some additional configuration to verify the signature with the SSH key:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git log&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --show-signature&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;error:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; gpg.ssh.allowedSignersFile needs to be configured and exist for ssh signature verification&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 60c4afaae4bb3a2013648a26a99b994e868ee4ae&lt;&#x2F;span&gt;&lt;span&gt; (HEAD -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;No&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; signature&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Author:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Brian Hicks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;brian@brianthicks.co&lt;&#x2F;span&gt;&lt;span&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Date:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;   Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:37&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; commit has been signed with the test SSH key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 04627098cc096584dac81c43ad70b40851880c18&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Signature made Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; CST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                using RSA key 66BAD9732604D23EF4A55B75C4F324B9CAAB0D50&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                issuer &amp;quot;brian@brianthicks.com&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Good signature from &amp;quot;Brian Hicks &amp;lt;brian@brianthicks.com&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; [ultimate]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Author:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Brian Hicks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;brian@brianthicks.co&lt;&#x2F;span&gt;&lt;span&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Date:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;   Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; commit has been signed with my regular GPG key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Very well, let&#x27;s set an allowed signers file up. I found &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gitlab.com&#x2F;ee&#x2F;user&#x2F;project&#x2F;repository&#x2F;ssh_signed_commits&#x2F;#verify-commits&quot;&gt;some instructions on GitLab to set up the allowed signers file&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; echo &amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; config&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --get&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; user.email) namespaces=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; $(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; test-key.ed25519.pub)&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; allowed_signers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git config gpg.ssh.allowedSignersFile &amp;quot;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;)&#x2F;allowed_signers&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now I can get both signatures verified:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; git log&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --show-signature&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 60c4afaae4bb3a2013648a26a99b994e868ee4ae&lt;&#x2F;span&gt;&lt;span&gt; (HEAD -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; main&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Good&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;git&amp;quot; signature for brian@brianthicks.com with ED25519 key SHA256:mevdSg3mn8+unpYYaB7+Sw2D6k4&#x2F;VmViamKCEHHma0M&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Author:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Brian Hicks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;brian@brianthicks.co&lt;&#x2F;span&gt;&lt;span&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Date:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;   Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:37&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; commit has been signed with the test SSH key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;commit&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 04627098cc096584dac81c43ad70b40851880c18&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Signature made Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; CST&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                using RSA key 66BAD9732604D23EF4A55B75C4F324B9CAAB0D50&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;                issuer &amp;quot;brian@brianthicks.com&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;gpg:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Good signature from &amp;quot;Brian Hicks &amp;lt;brian@brianthicks.com&amp;gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; [ultimate]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Author:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Brian Hicks&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;brian@brianthicks.co&lt;&#x2F;span&gt;&lt;span&gt;m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Date:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;   Thu Jan&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 19&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; 11:02:12&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 2023 -0600&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    this&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; commit has been signed with my regular GPG key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Cool. Now let&#x27;s test how forges deal with rotating these keys.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;github&quot;&gt;GitHub&lt;&#x2F;h2&gt;
&lt;p&gt;GitHub says that SSH key signing is less involved but lacks features, namely that an SSH signing key can&#x27;t be revoked. Fair enough; there&#x27;s no mechanism for that. They &lt;em&gt;don&#x27;t&lt;&#x2F;em&gt; say what happens when you roll keys, though, so let&#x27;s try it.&lt;&#x2F;p&gt;
&lt;p&gt;First, I&#x27;ll push my test repo to GitHub:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; gh repo create&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --private --source&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --push&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Created repository BrianHicks&#x2F;test-ssh-key-signing on GitHub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Added remote https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;test-ssh-key-signing.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Enumerating&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; objects: 3, done.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Counting&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; objects: 100%&lt;&#x2F;span&gt;&lt;span&gt; (3&#x2F;3), done.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Delta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; compression using up to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; threads&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Compressing&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; objects: 100%&lt;&#x2F;span&gt;&lt;span&gt; (2&#x2F;2), done.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Writing&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; objects: 100%&lt;&#x2F;span&gt;&lt;span&gt; (3&#x2F;3), 1.25 KiB &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; 1.25&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; MiB&#x2F;s, done.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Total&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt; (delta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;), reused 0 (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;delta&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;), pack-reused 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;To&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;test-ssh-key-signing.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; [new&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; branch]      HEAD&lt;&#x2F;span&gt;&lt;span&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;branch&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;#39;main&amp;#39; set up to track &amp;#39;origin&#x2F;main&amp;#39;.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;✓&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Pushed commits to https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;test-ssh-key-signing.git&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I haven&#x27;t uploaded the new key, but I have enabled &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;authentication&#x2F;managing-commit-signature-verification&#x2F;displaying-verification-statuses-for-all-of-your-commits&quot;&gt;vigilant mode&lt;&#x2F;a&gt;, so their web UI shows the commit as unverified:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;unverified-commits-on-GitHub-in-the-test-ssh-key-signing-repo.png&quot; alt=&quot;the GitHub UI, showing the commit history with one verified commit signed by GPG and one unverified commit signed by SSH&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;However, if I go to my settings and add the SSH key as a signing key, it shows as verified!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;verified-commits-on-GitHub-in-the-test-ssh-key-signing-repo.png&quot; alt=&quot;the GitHub UI, showing both commits verified&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;If I remove the signing key, the commit status goes back to unverified, which makes sense.&lt;&#x2F;p&gt;
&lt;p&gt;Since I uploaded this key as a signing key instead of an authentication key, it does not let me log in:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; ssh&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;IdentitiesOnly=yes&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; test-key.ed25519 git@github.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;git@github.com:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Permission denied&lt;&#x2F;span&gt;&lt;span&gt; (publickey).&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Good! I&#x27;d be uncomfortable allowing authentication from machines with decommissioned keys, but I&#x27;m at least OK-ish with saying “the commits that I made with this are still valid forever”, as long as I can be reasonably confident that the key material is completely destroyed, so no new commits can be made. If I wanted to say &quot;any commits with signatures after this date should not be considered verified&quot;, I could use a GPG key (but you can maybe still mess around with that, since you can change the date of a commit?)&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, signing commits in this way seems like it satisfies my security requirements, so it might be possible. I still need to look at Gitea, though!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gitea-forgejo&quot;&gt;Gitea&#x2F;Forgejo&lt;&#x2F;h2&gt;
&lt;p&gt;For Gitea&#x2F;Forgejo, there&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.gitea.io&#x2F;en-us&#x2F;signing&#x2F;&quot;&gt;some documentation by Gitea&lt;&#x2F;a&gt;. It only mentions GPG keys. I wonder if it&#x27;d “just work” for SSH-based signing, though? Let&#x27;s find out!&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t have the CLI installed for Gitea, so I created a private repo in my account and pushed to it. Afterward, the commit shows up as unverified, as expected:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;unverified-commits-on-gitea-in-the-test-ssh-key-signing-repo.png&quot; alt=&quot;the Gitea UI, showing the commit history with one verified commit signed by GPG and one unverified commit signed by SSH&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I added the key to my account as a regular authentication key, but that didn&#x27;t change anything. However, I noticed a new “Verify” button in the web UI. Once I verified my SSH key (by signing a string with some instructions provided on the page) the commit showed up as verified:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;verified-commits-on-gitea-in-the-test-ssh-key-signing-repo.png&quot; alt=&quot;the Gitea UI, showing both commits verified&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This creates a problem, though: it doesn&#x27;t look like I can accept the key for signing but not authentication. For example, if I try to authenticate to my git host with the key it lets me:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; ssh&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -o&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;IdentitiesOnly=yes&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; test-key.ed25519 git@git.bytes.zone&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;PTY&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; allocation request failed on channel&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;Hi&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; there, brian! You&amp;#39;ve successfully authenticated with the key named brianhicks@..., but Gitea does not provide shell access.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;If this is unexpected, please log in with password and setup Gitea under another user.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;Connection to git.bytes.zone closed.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is maybe worth asking either the Gitea or Forgejo development teams about. Of course, I could just be thinking incorrectly about the security model here: it could be the case that they don&#x27;t want to allow this because you can&#x27;t revoke SSH keys.&lt;&#x2F;p&gt;
&lt;p&gt;Either way, it seems like if I rotate my keys, I either have to allow authentication access to old keys forever, which I really don&#x27;t want to do. Looks like I&#x27;ll be sticking with my GPG key for signing commits for now!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>What does expect 1 mean in Alloy?</title>
		<published>2023-01-30T00:00:00+00:00</published>
		<updated>2023-01-30T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/what-does-expect-1-mean-in-alloy/" type="text/html"/>
		<id>https://bytes.zone/posts/what-does-expect-1-mean-in-alloy/</id>
		<content type="html">&lt;p&gt;When you open up &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;, the default &lt;code&gt;run&lt;&#x2F;code&gt; in the menu is “Run Default for 4 but 4 int, 4 seq expect 1.” I know what “for 4” means (up to 4 of every &lt;code&gt;sig&lt;&#x2F;code&gt;) and &lt;code&gt;but 4 int&lt;&#x2F;code&gt; means (4 bits worth of integers) but I don&#x27;t know what “expect 1” means!&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s look in &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;alloytools.org&#x2F;spec.html&quot;&gt;the spec&lt;&#x2F;a&gt;… nothing! It shows the “run X” and “for Y” and “but Z SigName” but not “expect.” 🤔&lt;&#x2F;p&gt;
&lt;p&gt;OK, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.discourse.group&#x2F;t&#x2F;what-does-expect-1-mean&#x2F;308&quot;&gt;on to the Alloy forums, then&lt;&#x2F;a&gt;. Daniel Jackson says it allows you to say how many results you expect. You used to be able to expect an arbitrary number, but that didn&#x27;t turn out to be useful, and now you can just say “1” (for some results) or “0” (for no results.) It&#x27;s useful for testing regressions when developing Alloy itself.&lt;&#x2F;p&gt;
&lt;p&gt;I was able to use &lt;code&gt;expect 0&lt;&#x2F;code&gt; to produce a spec that fails, as expected:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  some&lt;&#x2F;span&gt;&lt;span&gt; u&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: univ |&lt;&#x2F;span&gt;&lt;span&gt; u &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;not in univ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;run&lt;&#x2F;span&gt;&lt;span&gt; {} &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; expect&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;(In English: “I want you to check that the set of all sets does not contain itself.” This is not possible in Alloy&#x27;s set semantics.)&lt;&#x2F;p&gt;
&lt;p&gt;When evaluating this, Alloy says, “No instance found. Predicate may be inconsistent, as expected.” Note the “inconsistent”, meaning “you&#x27;ve created a conflict in your expectations.” If I switch it to &lt;code&gt;expect 1&lt;&#x2F;code&gt;, it says “Predicate may be inconsistent, contrary to expectation.”&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>advice you might as well take</title>
		<published>2023-01-22T00:00:00+00:00</published>
		<updated>2023-01-22T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/advice-you-might-as-well-take/" type="text/html"/>
		<id>https://bytes.zone/posts/advice-you-might-as-well-take/</id>
		<content type="html">&lt;p&gt;I&#x27;ve read some nice articles recently which I can sum up as “advice you might as well take.” This is stuff that&#x27;s good to consider at the beginning of a project, or when you&#x27;re about to add a feature to some existing software.&lt;&#x2F;p&gt;
&lt;p&gt;These articles run counter to YAGNI (You Ain&#x27;t Gonna Need It), the software design principle that says you should only ever add things you&#x27;ll be using right away. Many even call this out in their titles! One even addresses this by coining an alternative term, PAGNI (Probably Are Gonna Need It), so let&#x27;s start with that one:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pagnis-probably-are-gonna-need-its&quot;&gt;PAGNIs: Probably Are Gonna Need Its&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2021&#x2F;Jul&#x2F;1&#x2F;pagnis&#x2F;&quot;&gt;PAGNIs - Probably Are Gonna Need Its&lt;&#x2F;a&gt; by Simon Wilson. Makes a good rule for when to allow complexity in advance:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;When should you over-ride YAGNI? When the cost of adding something later is so dramatically expensive compared with the cost of adding it early on that it’s worth taking the risk. On when you know from experience that an initial investment will pay off many times over.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;His PAGNIs: make kill-switches for mobile apps to force upgrades or contain security issues, have automated deployments and continuous integration, build pagination, and keep detailed API logs, including POST bodies.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;preemptive-pluralization-is-probably-not-evil&quot;&gt;Preemptive Pluralization is Probably Not Evil&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.swyx.io&#x2F;preemptive-pluralization&quot;&gt;Preemptive Pluralization is Probably Not Evil&lt;&#x2F;a&gt; by swyx. Summed up by this quote:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Before you write any code — ask if you could ever possibly want multiple kinds of the thing you are coding.&lt;&#x2F;strong&gt; If yes, just do it. Now, not later.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Basically, if:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;it&#x27;s a lot of effort to go from being able to handle zero things to being able to handle one thing&lt;&#x2F;li&gt;
&lt;li&gt;AND, it&#x27;s a similar amount of effort to go from one to many&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Then it probably makes sense to go directly from zero to many if you think that&#x27;ll ever be needed.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s also &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;wiki.c2.com&#x2F;?ZeroOneInfinityRule&quot;&gt;some interesting discussion on the C2 wiki&lt;&#x2F;a&gt;, including some times when it&#x27;s good to avoid this.&lt;&#x2F;p&gt;
&lt;p&gt;Out of these pieces of advice, this is the one I&#x27;ve used the most. However, I&#x27;d recommend modeling out your system with something like &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; first and seeing what that gets you!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-might-as-well-timestamp-it&quot;&gt;You Might as Well Timestamp It&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;changelog.com&#x2F;posts&#x2F;you-might-as-well-timestamp-it&quot;&gt;You might as well timestamp it&lt;&#x2F;a&gt; by Jerod Santo. Summary: if you store boolean fields as nullable timestamps, then you have basically the same semantics but get the ability to say when the value went to “true” for free.&lt;&#x2F;p&gt;
&lt;p&gt;You do use a little more disk space, but not much, so you&#x27;ll have to decide whether that&#x27;s worth it. (At least in PostgreSQL, booleans are 1 byte while a timestamp is 8.) However, I would imagine that&#x27;s fine for most uses.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;probably-are-gonna-need-it-application-security-edition&quot;&gt;Probably Are Gonna Need It: Application Security Edition&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jacobian.org&#x2F;2021&#x2F;jul&#x2F;8&#x2F;appsec-pagnis&#x2F;&quot;&gt;Probably Are Gonna Need It - Application Security Edition&lt;&#x2F;a&gt; by Jacob Kaplan-Moss. Inspired by Simon Wilson&#x27;s PAGNI post, but focused on security. There&#x27;s too much good stuff in here to summarize, but some highlights for me were to always consider the “abusive ex” persona when you&#x27;re designing your app and his thoughts on security admin interfaces.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;summing-up&quot;&gt;Summing Up&lt;&#x2F;h2&gt;
&lt;p&gt;These articles show a ton of cases where it&#x27;s worth breaking YAGNI. I appreciate this because, at a higher level, I think that oversimplifying our mental model of the software we&#x27;re building can risk failure just as much as overcomplexity can. Even worse, by oversimplifying we can harm the people who use our software by failing to consider their safety as we&#x27;re building (e.g. the “abusive ex” persona.)&lt;&#x2F;p&gt;
&lt;p&gt;I want to close with a quote I really love from Hillel Wayne&#x27;s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buttondown.email&#x2F;hillelwayne&#x2F;archive&#x2F;reject-simplicity-embrace-complexity&#x2F;&quot;&gt;Reject Simplicity, Embrace Complexity&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Simplicity is good. We should write simple code. But complexity is &lt;em&gt;unavoidable&lt;&#x2F;em&gt;. We do a disservice to ourselves by pretending that any software can be simple &lt;em&gt;if we just try hard enough&lt;&#x2F;em&gt;. Instead, we should study the factors that lead to complex software. That way we can learn how to recognize, predict, and manage complexity in our systems. And then we can seek simplicity within that context. It won’t give us simple software, but it will help us write &lt;em&gt;simpler&lt;&#x2F;em&gt; software. Nuance is better than mantras.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>pagerank for my Obsidian notes</title>
		<published>2023-01-17T00:00:00+00:00</published>
		<updated>2023-01-17T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/pagerank-for-my-obsidian-notes/" type="text/html"/>
		<id>https://bytes.zone/posts/pagerank-for-my-obsidian-notes/</id>
		<content type="html">&lt;p&gt;I recently learned a little more about how the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;PageRank&quot;&gt;Pagerank&lt;&#x2F;a&gt; algorithm works. I thought it was cool, and it made me wonder what the ranks for my &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&quot;&gt;Obsidian&lt;&#x2F;a&gt; vault looked like, so I built a little tool. It&#x27;s fairly simple: it just parses &lt;code&gt;[[links]]&lt;&#x2F;code&gt; out of the vault with a regex and then uses the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;crates.io&#x2F;crates&#x2F;simple-pagerank&quot;&gt;simple-pagerank&lt;&#x2F;a&gt; crate for Rust to calculate the ranking.&lt;&#x2F;p&gt;
&lt;p&gt;For my vault, the most centrally connected node is my employer. It&#x27;s the top node by quite a lot (with a score of 4.9), followed by the town where I live (2.0), then members of my family (around 2) and notes on projects I&#x27;m doing and tools I use (around 1.) This makes a lot of sense to me; I primarily use Obsidian as a way to keep a work log and write about things related to that, &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;digital-gardening-in-obsidian&#x2F;&quot;&gt;as I wrote about previously&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d like to try this for yourself, you can get the code at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;obsidian-pagerank&quot;&gt;bytes.zone&#x2F;brian&#x2F;obsidian-pagerank&lt;&#x2F;a&gt;, although you&#x27;ll need to have either &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&quot;&gt;Nix&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&#x2F;&quot;&gt;Rust&lt;&#x2F;a&gt; installed in advance. If you decide to try it, let me know if you find anything interesting!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>The Value of a Model is More Making than Having</title>
		<published>2023-01-09T00:00:00+00:00</published>
		<updated>2023-01-09T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/the-value-of-a-model-is-more-making-than-having/" type="text/html"/>
		<id>https://bytes.zone/posts/the-value-of-a-model-is-more-making-than-having/</id>
		<content type="html">&lt;p&gt;After making a bunch of models in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt;, I&#x27;m curious if formal modeling is more useful as a thing to do (a process) or a thing to create (an artifact.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;making-process&quot;&gt;Making &#x2F; Process&lt;&#x2F;h2&gt;
&lt;p&gt;On the modeling-as-a-process side, I&#x27;ve had good luck taking the time to carefully model a system and asking domain experts how the model is wrong. In those cases, I tend to get one of two responses:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;“Oh, we take care of that case with X” (which means I need to change my model)&lt;&#x2F;li&gt;
&lt;li&gt;“Uh-oh, we hadn&#x27;t thought of that” (which means we might need to fix bugs or make other changes to the system in question)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For example, I recently modeled an upcoming feature my team would be working on and found several problems (in this case, actions we hadn&#x27;t disallowed that would violate our assumptions about what data would be shown on the page.) I brought those to the folks in charge of the spec, and it turned out they had considered several—but not all—of them and that they had already decided that the consequences were acceptable. I&#x27;m still glad I did it, though, since it gave me a much better sense of what we were building and the consequences of our approach.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve also found it useful to sit down with other people and have a conversation &lt;em&gt;using&lt;&#x2F;em&gt; formal modeling; the model acts as a conversation partner to keep us from assuming different things about the world and point out problems in our thinking. This has even been useful when one or more of the humans involved hasn&#x27;t known about formal modeling in advance because I can explain stuff on the fly. For example, as I wrote &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;previously&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;For example, at work we were building a feature with inline commenting (like Google Docs.) We started by modeling Google Docs&#x27; comments and it turned out that each comment had something like 32 unique states. We realized this was going to be way too much for our use case, and simplified it down to 4. We probably could have had this realization without using Alloy, but it made the conversation much simpler—the designer and I sat down and looked at the visualizations, then kept saying &quot;but for us, states X and Y would be the same…&quot; until we arrived at a simpler model. It was really nice, and only took about an hour and a half of modeling to come up with a solution that satisfied all our requirements!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This is basically “&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Formal_methods#Lightweight_formal_methods&quot;&gt;lightweight formal methods&lt;&#x2F;a&gt;”—we&#x27;re not out to solve the Byzantine generals problem or whatever, but just to make sure there aren&#x27;t any big issues we hadn&#x27;t thought of or flaws we can&#x27;t live with.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;having-artifact&quot;&gt;Having &#x2F; Artifact&lt;&#x2F;h2&gt;
&lt;p&gt;On the models-as-artifacts side, I&#x27;ve had some success, but less than modeling-as-a-process.&lt;&#x2F;p&gt;
&lt;p&gt;To start, I&#x27;ve tried to turn Alloy and TLA+ models into documentation. Alloy&#x27;s ability to generate examples and its literate markdown support is amazing, but both still require the reader to have some basic familiarity with Alloy&#x27;s syntax and how to interpret the visualizations.&lt;&#x2F;p&gt;
&lt;p&gt;This can work really well for tutorials and explorations, though: &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;modeling-database-tables-in-alloy&#x2F;&quot;&gt;modeling database tables in Alloy&lt;&#x2F;a&gt; was originally written as a literate Alloy file (and still lives in my notes in a form that Alloy could pick up and run.) At work, we now have a few documentation files explaining different features and referencing a common tutorial I wrote for background. It works alright, although I&#x27;m not sure how many people have read them (but I have sent links to folks who wanted a refresher on the features, which seems to have worked.)&lt;&#x2F;p&gt;
&lt;p&gt;Beyond documentation of fairly static features, the model and the actual system almost always diverge. People add features and fix bugs in ways that violate the assumptions or assertions of the model, at which point the model is making strong claims that don&#x27;t match up with real life.&lt;&#x2F;p&gt;
&lt;p&gt;The weird thing, though, is that &lt;strong&gt;this is something you want&lt;&#x2F;strong&gt;. It&#x27;s not useful to specify your model all the way down to the level of your code. It can be distracting to have random states changing that don&#x27;t matter for the thing you&#x27;re trying to assert about the model. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Map%E2%80%93territory_relation&quot;&gt;The map is not the territory&lt;&#x2F;a&gt;, after all! For example, your real-life HTTP service always has to deal with network problems, but your model might not need to. It depends on what you&#x27;re trying to figure out about the system, and that might change over time as you make models of different parts of the system!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-which-is-it&quot;&gt;So which is it?&lt;&#x2F;h2&gt;
&lt;p&gt;Based on my experiences with formal modeling tools, I think the process of creating a model is more valuable than the thing you produce. I&#x27;ve seen fewer drawbacks to approaching modeling as a process or as a communication tool without worrying about preserving the model for the long term.&lt;&#x2F;p&gt;
&lt;p&gt;However, my opinion here may change as I practice more. I&#x27;ve already produced a few models-as-documentation for stabler and longer-lived parts of systems at work, and those have been helpful to introduce people to the concepts involved in an isolated way. Maybe trying to find more ways that models have long-term value will change my mind here!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thank you to Ezzeri Esa and Miccah Castorina at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;recurse.com&quot;&gt;the Recurse Center&lt;&#x2F;a&gt; for reviewing a draft of this post!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Modeling Database Tables in Alloy</title>
		<published>2023-01-02T00:00:00+00:00</published>
		<updated>2023-01-02T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/modeling-database-tables-in-alloy/" type="text/html"/>
		<id>https://bytes.zone/posts/modeling-database-tables-in-alloy/</id>
		<content type="html">&lt;p&gt;Formal methods tools like Alloy are not just for proving correctness properties in distributed systems (or whatever) but can be a useful tool for mere mortals like me! &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;alloy&#x2F;&quot;&gt;As I mentioned previously&lt;&#x2F;a&gt;, I&#x27;ve been using Alloy to help me avoid data modeling mistakes before they&#x27;re encoded in hard-to-change places, like database schemas. In this post, I&#x27;ll show my thought process as I use Alloy to model the schema for a project for my makerspace, including some of the mistakes that Alloy helped me avoid.&lt;&#x2F;p&gt;
&lt;p&gt;I joined the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.inventorforgemakerspace.org&#x2F;&quot;&gt;Inventor Forge Makerspace&lt;&#x2F;a&gt; recently to get access to some better tools for &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;home-recycling&#x2F;&quot;&gt;home recycling&lt;&#x2F;a&gt;. There are some cool tools there (Lasers! CNC machines! Plasma cutters!) that need both special training and have high demand among the folks who use the space. Right now, we use a shared Google calendar to schedule time on different machines. Unfortunately, I don&#x27;t have a personal Google account, so it&#x27;s harder to schedule than I&#x27;d like! There are a couple of other people in this situation too, so I&#x27;m in the process of building a tool to track training and scheduling.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll start by modeling &lt;code&gt;Member&lt;&#x2F;code&gt; (someone who can join the makerspace) and a &lt;code&gt;Tool&lt;&#x2F;code&gt; (something that we need training and scheduling for.) We&#x27;ll use Alloy&#x27;s basic building block of &lt;code&gt;sig&lt;&#x2F;code&gt;s for this. If you haven&#x27;t been exposed to Alloy before, you can think of &lt;code&gt;sig&lt;&#x2F;code&gt;s as sets that can contain values (like rows in a database table.) So you can think of &lt;code&gt;Member&lt;&#x2F;code&gt; as the set of all members of the makerspace, which can contain values like &lt;code&gt;Member0&lt;&#x2F;code&gt; and &lt;code&gt;Member1&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Member&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Tool&lt;&#x2F;span&gt;&lt;span&gt; {}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We can ask Alloy to generate examples for this small model. They won&#x27;t be super useful at this point—we haven&#x27;t modeled any interactions between &lt;code&gt;Member&lt;&#x2F;code&gt; and &lt;code&gt;Tool&lt;&#x2F;code&gt;—but they let us get a sense of what Alloy can provide us. For example, here&#x27;s an instance with two members and two tools:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;two-members-two-tools.png&quot; alt=&quot;two members and two tools, not connected in any way&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We can ask Alloy for any number of instances, and we&#x27;ll be looking at these frequently to find the kinds of edge cases we&#x27;re interested in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;connecting-members-and-tools&quot;&gt;Connecting Members and Tools&lt;&#x2F;h2&gt;
&lt;p&gt;To get authorization to use a tool at my makerspace, you need to contact a &quot;tool champion&quot; who is responsible for doing safety training. (They&#x27;re not just &quot;trainers&quot; because they also maintain, fix, and upgrade the tools.) In our model, we&#x27;ll first connect &lt;code&gt;Member&lt;&#x2F;code&gt; and &lt;code&gt;Tool&lt;&#x2F;code&gt; by adding a new &lt;code&gt;sig Champion&lt;&#x2F;code&gt;, which has a relationship with both.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Champion&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  tool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Tool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;When we&#x27;re turning this model into a real database, &lt;code&gt;Champion&lt;&#x2F;code&gt; will become a &lt;code&gt;champions&lt;&#x2F;code&gt; table, with foreign keys to &lt;code&gt;tools&lt;&#x2F;code&gt; (&lt;code&gt;tool_id&lt;&#x2F;code&gt;) and &lt;code&gt;members&lt;&#x2F;code&gt; (&lt;code&gt;member_id&lt;&#x2F;code&gt;.)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;can-you-be-the-champion-for-more-than-one-tool-at-once&quot;&gt;Can you be the champion for more than one tool at once?&lt;&#x2F;h3&gt;
&lt;p&gt;Now that we have some relationships, we can poke around in the instances Alloy generates to see if we can find anything that feels off. For example, Alloy shows that we&#x27;re allowing duplicate champions for the same member and tool:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;one-member-four-championships.png&quot; alt=&quot;one instance of member and one of tool connected by four separate champion instances&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This could cause us problems later. Imagine what could happen if we also had a boolean field named &lt;code&gt;active&lt;&#x2F;code&gt; and a champion resigned. We could end up with one row that says they&#x27;re active and another that says they&#x27;re inactive. We&#x27;d have to define some conflict semantics in the application, making sure to only change the one we care most about, or all of them, or whatever. But we can get around all that by disallowing these duplicate rows!&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, this is fairly simple to achieve in a database schema by adding a unique index on &lt;code&gt;champions.tool_id&lt;&#x2F;code&gt; and &lt;code&gt;champions.member_id&lt;&#x2F;code&gt;. We can do something similar in our Alloy model by introducing a &lt;code&gt;fact&lt;&#x2F;code&gt; (a logical statement that Alloy will assume is always correct.)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;a unique index on champions prevents duplicate member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt;tool combinations&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Tool&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Member &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;| lone&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Champion &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;member &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; m&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In English, this is saying &quot;for every combination of &lt;code&gt;Tool&lt;&#x2F;code&gt; and &lt;code&gt;Member&lt;&#x2F;code&gt;, there&#x27;s at most one &lt;code&gt;Champion&lt;&#x2F;code&gt; that connects them.&quot; (The mnemonic for &lt;code&gt;lone&lt;&#x2F;code&gt; is &quot;less than or equal to one&quot;. In this case, you can think of it as &quot;if there&#x27;s one, there&#x27;s &lt;em&gt;only&lt;&#x2F;em&gt; one.&quot;)&lt;&#x2F;p&gt;
&lt;p&gt;Just to emphasize, Alloy will take our word that this fact is always true in our system instead of checking it for us! Because of this, I like to include some justification in the description for how this is true in the system I&#x27;m modeling. In this case, that means explaining the mechanism for how we&#x27;re going to make sure this property holds: the unique index.&lt;&#x2F;p&gt;
&lt;p&gt;To make sure we&#x27;ve got this completely covered, we&#x27;ll also assert that we solved the problem. In this case, we&#x27;ll assert that the case we found earlier can&#x27;t come up because of our new fact:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; NoDuplicateChampions&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; disjoint c1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; c2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Champion &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; c1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; c2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt; c1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;member &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; c2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;member&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We need &lt;code&gt;disjoint&lt;&#x2F;code&gt; here because, otherwise, Alloy could choose the same &lt;code&gt;Champion&lt;&#x2F;code&gt; as both &lt;code&gt;c1&lt;&#x2F;code&gt; and &lt;code&gt;c2&lt;&#x2F;code&gt;, in which case the assertion would be trivially disprovable—two references to the same champion would naturally have the same tool and member.&lt;&#x2F;p&gt;
&lt;p&gt;Alloy does not find any counterexamples to this check, so we&#x27;re safe to continue.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;trainings&quot;&gt;Trainings&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we have champions members to join the space, tools for them to use, and champions to train them… how do we get access to the tools? Trainings! Someone can have access to a tool that they&#x27;ve trained for, so we can represent it as another &lt;code&gt;sig&lt;&#x2F;code&gt;&#x2F;table:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Training&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Champion&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  trainee&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Member&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll add another validation, as well: that the trainer and trainee can&#x27;t be the same person. We may be able to do some index trickery to get this to work, but I think it&#x27;s more likely that we&#x27;ll have to validate this at the app level.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;the application code validates that trainer &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;!=&lt;&#x2F;span&gt;&lt;span&gt; trainee&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Training &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;member &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainee&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Including this justification makes me wonder what the application should do if someone inserts a row that violates this fact into &lt;code&gt;trainings&lt;&#x2F;code&gt; directly, but I&#x27;m going to leave that for the moment.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;examining-more-instances&quot;&gt;Examining More Instances&lt;&#x2F;h3&gt;
&lt;p&gt;Let&#x27;s look at the model instances to see if there&#x27;s anything worth fixing now. Fortunately, there&#x27;s not a lot: we&#x27;ve already ruled out quite a few problems with our modeling so far. That means we&#x27;ll get mostly &quot;normal&quot; instances. For example, here&#x27;s what it looks like when a champion has trained someone on a tool:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;champion-training-member.png&quot; alt=&quot;a complete training, showing a tool, the champion for that tool, another member, and the training instance for that member&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;A champion can also train two members on the same tool. Also fine:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;champion-training-two-members.png&quot; alt=&quot;two complete trainings, showing a tool, the champion for that tool, and two members, each with training from the champion&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;But Alloy also gives us this, in which the same member is trained multiple times:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;training-twice.png&quot; alt=&quot;two training instances for the same member. The champion and tool are the same for both.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Thinking through our domain, this &lt;em&gt;might&lt;&#x2F;em&gt; be ok. Because the training is for safety, it&#x27;s not uncommon for someone to request to have a refresher on a tool if it&#x27;s been a while since they&#x27;ve used it. This is especially common with the woodworking tools, where there are a lot of sharp things near your hands. However, is it OK in our database schema, or would we rather disallow duplicates and (for example) update the date of the training if someone re-trains? I&#x27;ll have to talk to the person in charge of our current record-keeping system to find out!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;reservations&quot;&gt;Reservations&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve already found a couple of interesting questions in our modeling; let&#x27;s see if we can find more by modeling reservations on machines (finally, we get to the reason I&#x27;m doing this in the first place!)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Reservation&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  start&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  end&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Int&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Training&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;} {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  -- a CHECK enforces ordering in the database&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  start &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We&#x27;ll use &lt;code&gt;Int&lt;&#x2F;code&gt;s to specify the start and end times of the reservation and specify that they have to be ordered. One weird corner I&#x27;m noting about this design is that since I&#x27;ve used &lt;code&gt;Training&lt;&#x2F;code&gt; as the authorization (just to say that you can&#x27;t get time on a tool you haven&#x27;t been trained on) you have to access the tool in a roundabout way: given a &lt;code&gt;Reservation&lt;&#x2F;code&gt; named &lt;code&gt;r&lt;&#x2F;code&gt;, it&#x27;s &lt;code&gt;r.auth.trainer.tool&lt;&#x2F;code&gt;. Could be worse, but that&#x27;s gonna be a lot of joins in the database. There are a couple of alternatives here:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;we could reference &lt;code&gt;Tool&lt;&#x2F;code&gt; directly from &lt;code&gt;Reservation&lt;&#x2F;code&gt;, but then we could then construct a &lt;code&gt;Reservation&lt;&#x2F;code&gt; for a tool that a member hasn&#x27;t been trained on using a &lt;code&gt;Training&lt;&#x2F;code&gt; for another tool. We&#x27;d have to enforce that in the app, and I&#x27;d prefer not to—the database should take care of data integrity, where possible.&lt;&#x2F;li&gt;
&lt;li&gt;we could embed a &lt;code&gt;Tool&lt;&#x2F;code&gt; in &lt;code&gt;Training&lt;&#x2F;code&gt; as part of the foreign key to &lt;code&gt;Champion&lt;&#x2F;code&gt;, then do the same in &lt;code&gt;Reservation&lt;&#x2F;code&gt;. This would be a bunch of coordination work, but might be nicer: it&#x27;d mean that we couldn&#x27;t change &lt;code&gt;Champion.tool&lt;&#x2F;code&gt; without figuring out how to update &lt;code&gt;Training&lt;&#x2F;code&gt; and &lt;code&gt;Reservation&lt;&#x2F;code&gt; at the same time. It&#x27;d take more coordination, but it&#x27;d give our data model some nice additional robustness.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I&#x27;m partial to adding the foreign keys from option 2, but for the sake of completing our model (and this post) let&#x27;s move on.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;preventing-conflicting-reservations&quot;&gt;Preventing Conflicting Reservations&lt;&#x2F;h3&gt;
&lt;p&gt;To finish the &lt;code&gt;reservations&lt;&#x2F;code&gt; table, we should rule out overlapping reservations for the same tool. To start with, we can assert (incorrectly) that reservations already can&#x27;t overlap to get Alloy to give us a counterexample:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fun&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; reservedTimes&lt;&#x2F;span&gt;&lt;span&gt;[r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Reservation]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Int {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;start&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.*&lt;&#x2F;span&gt;&lt;span&gt;next &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;end&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.^&lt;&#x2F;span&gt;&lt;span&gt;prev&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; NoConflictingReservations&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; disjoint r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Reservation &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      implies no&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; reservedTimes&lt;&#x2F;span&gt;&lt;span&gt;[r1] &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; reservedTimes&lt;&#x2F;span&gt;&lt;span&gt;[r2]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The way to read this is &quot;given two different &lt;code&gt;Reservation&lt;&#x2F;code&gt;s with the same tool, there should be no overlap in their scheduling.&quot; The &lt;code&gt;implies&lt;&#x2F;code&gt; there works like a conditional statement in an imperative language: in this case, you can read it as &quot;if X then Y&quot; instead of &quot;X implies Y,&quot; so &quot;if the tools are the same, the times can&#x27;t overlap.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;code&gt;next&lt;&#x2F;code&gt; and &lt;code&gt;prev&lt;&#x2F;code&gt; relations on &lt;code&gt;start&lt;&#x2F;code&gt; and &lt;code&gt;end&lt;&#x2F;code&gt; are new as well—they come from Alloy&#x27;s ordered set support and implicitly exist on ordered sets like &lt;code&gt;Int&lt;&#x2F;code&gt;. They let you do things like calling &lt;code&gt;1.next&lt;&#x2F;code&gt; to get &lt;code&gt;2&lt;&#x2F;code&gt;. You can use the recursion operators here (another new concept to us,) where &lt;code&gt;*&lt;&#x2F;code&gt; will repeat a lookup zero or more times, and &lt;code&gt;^&lt;&#x2F;code&gt; will repeat a lookup one or more times. That means that calling &lt;code&gt;r.start.*next&lt;&#x2F;code&gt; gives us &lt;code&gt;r.start&lt;&#x2F;code&gt; plus all the times after it, and &lt;code&gt;r.end.^prev&lt;&#x2F;code&gt; gives us all times before the end, not including &lt;code&gt;r.end&lt;&#x2F;code&gt; (since one reservation should be able to start exactly when another ends.)&lt;&#x2F;p&gt;
&lt;p&gt;If we find the set intersection of those two lookups (the middle of a Venn diagram), we get a range, so saying &lt;code&gt;r.start.*next &amp;amp; r.end.^prev&lt;&#x2F;code&gt; gets us the range of reserved times for &lt;code&gt;r&lt;&#x2F;code&gt;. We can use this (in &lt;code&gt;reservedTimes&lt;&#x2F;code&gt;) to make assertions that there&#x27;s no overlap between the two reservations.&lt;&#x2F;p&gt;
&lt;p&gt;As expected, Alloy finds a violation of this check more-or-less instantly:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;conflicting-reservation.png&quot; alt=&quot;two reservations instances for the same member on the same tool. One starts at time -6 and ends at time 4. The other starts at time 0 and ends at time 6.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this case, the same member is making both reservations. I&#x27;m not convinced that that&#x27;s OK, but we&#x27;ll leave it alone for now and focus on the fact that &lt;code&gt;r2&lt;&#x2F;code&gt; starts at time &lt;code&gt;0&lt;&#x2F;code&gt; before &lt;code&gt;r1&lt;&#x2F;code&gt; ends at &lt;code&gt;4&lt;&#x2F;code&gt;. It looks like we can &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.postgresql.org&#x2F;docs&#x2F;15&#x2F;rangetypes.html#id-1.5.7.25.16.2&quot;&gt;use a GiST index in PostgreSQL to exclude overlapping ranges&lt;&#x2F;a&gt;. In fact, the last example on that page shows how you can avoid conflicts for a meeting room, which is similar to our example!&lt;&#x2F;p&gt;
&lt;p&gt;So, for now, let&#x27;s introduce a new fact with the constraint that would be introduced by adding such an index:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;a GiST index prevents overlapping ranges&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; disjoint r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Reservation &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tool &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;and&lt;&#x2F;span&gt;&lt;span&gt; r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;start &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;start)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      implies&lt;&#x2F;span&gt;&lt;span&gt; r1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;end &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;lt;=&lt;&#x2F;span&gt;&lt;span&gt; r2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;start&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We only have to make one assertion here (that &lt;code&gt;r1&lt;&#x2F;code&gt; ends before &lt;code&gt;r2&lt;&#x2F;code&gt; starts) because Alloy will check every possible combination of &lt;code&gt;Reservation&lt;&#x2F;code&gt; (that&#x27;s what &lt;code&gt;all&lt;&#x2F;code&gt; does.) That means that every reservation will eventually be checked as both &lt;code&gt;r1&lt;&#x2F;code&gt; and &lt;code&gt;r2&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, Alloy now says that it can&#x27;t find any counterexamples, which is what we want.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;browsing-instances-again&quot;&gt;Browsing Instances Again&lt;&#x2F;h2&gt;
&lt;p&gt;I &lt;em&gt;think&lt;&#x2F;em&gt; we&#x27;re done now, but I&#x27;m just going to browse a couple more examples to make sure. (I do this a lot.) First, we get a completely normal reservation:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;normal-reservation.png&quot; alt=&quot;a complete reservation from time 5 to time 6 using a single training, non-champion member, champion, and tool&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;No conflicts and the structure looks OK to me! Next, we have one reservation starting as soon as the previous one ends:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;starting-and-ending-at-the-same-time.png&quot; alt=&quot;two complete reservations for the same tool, using the same training, member, champion, and tool. One starts at the same time the other ends.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;This one&#x27;s a little funny since someone is using two different training authorizations. This is the kind of example I&#x27;ll have to bring up when I talk with the person who keeps the records now. Having a diagram like this can help explain the case I&#x27;m thinking about to someone else, so I&#x27;m glad I found it. I&#x27;m also going to ask whether it&#x27;s OK for one member to have two consecutive reservations—I don&#x27;t know!&lt;&#x2F;p&gt;
&lt;p&gt;I also notice we&#x27;re not getting any instances where the champion can schedule a reservation, but that&#x27;s something that should be able to happen. We can get Alloy to show us if it&#x27;s possible or not by asserting it can&#x27;t happen and then looking at the counterexample:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; ChampionCantSchedule&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  no&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Champion&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;span&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Reservation &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;auth&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;trainee &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;member&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Alloy comes up with this. A champion can be trained by another champion, at which point they can make reservations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;champion-cant-schedule.png&quot; alt=&quot;a reservation on a tool for a champion. The champion has been trained by another champion, allowing the reservation to exist through that training.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That seems reasonable, but how do you bootstrap it? Maybe &lt;code&gt;Champion&lt;&#x2F;code&gt; actually needs to be a subset of &lt;code&gt;Training&lt;&#x2F;code&gt; (that is, a boolean field on the &lt;code&gt;training&lt;&#x2F;code&gt; table?) Or maybe we should relax the requirement that a champion can&#x27;t train themselves? This is an edge case that I might not have thought of otherwise!&lt;&#x2F;p&gt;
&lt;p&gt;But let&#x27;s leave this here for now. With only a page or so of Alloy code, we&#x27;ve chased out a bunch of potential bugs in our model and found some interesting jumping-off points for questions while I continue to develop the model into an actual application. This has been pretty typical of my experience using Alloy in this way: in all but the simplest cases, it finds questions that I need to answer with very little effort on my part.&lt;&#x2F;p&gt;
&lt;p&gt;Hopefully, at this point, you&#x27;re interested and want to learn more about Alloy. Here are some good links:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.org&#x2F;&quot;&gt;The Alloy project site&lt;&#x2F;a&gt; (where you can get the software and some older learning material)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.hillelwayne.com&#x2F;post&#x2F;alloy6&#x2F;&quot;&gt;Hillel Wayne&#x27;s introduction to Alloy 6&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloy.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;his reference docs mostly covering Alloy 5&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;haslab.github.io&#x2F;formal-software-design&#x2F;&quot;&gt;A draft book about designing software in Alloy 6&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In general, most learning material does not cover Alloy 6, which came out about a year ago as of this writing. The links above are the only things I&#x27;ve found that do, although there may be more by the time you&#x27;re reading this!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks to Hazem at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;recurse.com&quot;&gt;Recurse Center&lt;&#x2F;a&gt; and Tessa Kelly at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;noredink.com&quot;&gt;NoRedInk&lt;&#x2F;a&gt; for reviewing drafts of this post!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Digital Gardening in Obsidian</title>
		<published>2022-12-26T00:00:00+00:00</published>
		<updated>2022-12-26T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/digital-gardening-in-obsidian/" type="text/html"/>
		<id>https://bytes.zone/posts/digital-gardening-in-obsidian/</id>
		<content type="html">&lt;p&gt;I have been using &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;obsidian.md&quot;&gt;Obsidian&lt;&#x2F;a&gt; for all my notes for something like 6 months now. That&#x27;s really &lt;em&gt;everything&lt;&#x2F;em&gt;, from temporary measurements to journaling to serious documentation work. I&#x27;ve tried Obsidian a couple of times before and bounced off of it, so I decided to try to do things differently this time. Instead of starting out by figuring out a system for where everything lives in advance, I&#x27;ve just thrown almost everything in the default folder and sorted it out with linking and search.&lt;&#x2F;p&gt;
&lt;p&gt;Some structure has emerged over time, which I&#x27;m fairly happy with, but it&#x27;s more to do with practices than things that would make it easier for someone else to read my vault. These practices basically boil down to &quot;digital gardening&quot;—they allow me to keep things neat and tidy and observe growth without being overwhelmed by it. Here&#x27;s some of what I do!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rollup-journaling&quot;&gt;Rollup Journaling&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been keeping a log in my daily notes. These logs can contain answers to journal prompts like &quot;what have I been avoiding?&quot; but most of the time it&#x27;s just a roughly accurate timestamped log of what I&#x27;ve been up to in a given day.&lt;&#x2F;p&gt;
&lt;p&gt;Every Monday I summarize these daily notes into a weekly digest. These have more of a journaling flavor to them, since one of the goals is to help me discover patterns, but the template is still pretty basic: a header for highlights, one for lowlights, and one for things I want to change in the next week.&lt;&#x2F;p&gt;
&lt;p&gt;If the particular Monday I&#x27;m doing this on also starts a new month, I&#x27;ll also do a summary of the weeks in the previous month following the same basic pattern. Same for the last 3 months of monthly notes, if it happens to be the first Monday of April, July, October, or January. I haven&#x27;t been doing this long enough to do a yearly rollup yet, but I may do a 2022 yearly rollup note in January.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been really happy with this! The big benefit so far I&#x27;ve seen is being able to see back in time with more clarity. There have been times this year where I&#x27;ve felt stuck—like I&#x27;m not making progress and haven&#x27;t for a long time—but looking back in my notes I can see that I actually have done some pretty major things in the past year. This has had a nice side benefit that my end-of-year review at work was really easy to write: I just looked back at my quarterly reviews, as well as a few monthly ones, and pulled out some good stuff. It doesn&#x27;t completely get rid of recency bias, but it does mean that I&#x27;m not &lt;em&gt;only&lt;&#x2F;em&gt; focusing on stuff that happened in the last month or two.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t know if &quot;rollup journaling&quot; is the best name for this, but it&#x27;s one that&#x27;s stuck with me for some reason. I got the idea for doing this a couple years (and tries at Obsidian) ago based on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=AzrEDnIye14&quot;&gt;Britney Braxton&#x27;s talk at JuneteenthConf 2020, &lt;em&gt;Journaling as a Dev&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;spaced-repetition&quot;&gt;Spaced Repetition&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been using the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;st3v3nmw&#x2F;obsidian-spaced-repetition&quot;&gt;community spaced repetition plugin&lt;&#x2F;a&gt; to periodically review notes. It&#x27;s a bit of a hack—this plugin is clearly not meant to be used like this—but it works.&lt;&#x2F;p&gt;
&lt;p&gt;I add a &lt;code&gt;#review&lt;&#x2F;code&gt; tag to every note that I want to periodically come back to and improve or tweak (this is actually most of my notes.) I then ask the spaced repetition plugin if there&#x27;s anything for me to look at. If I make any changes, I&#x27;ll mark the review as &quot;hard&quot; so the plugin will show it to me earlier. If I don&#x27;t think it needs any changes, I&#x27;ll mark it as &quot;easy&quot; to send it further into the future.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not terribly disciplined about doing this every day, but it has a nice benefit of improving notes that need to improve while sending ones that don&#x27;t into the far future for me to get reminded of periodically.&lt;&#x2F;p&gt;
&lt;p&gt;I think I originally got the idea for this from an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;notes.andymatuschak.org&#x2F;z36iMKLe4CDAXdtLSJD4Z6qPPFUS8ZXymUk3i&quot;&gt;Andy Matuschak note&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pages-that-don-t-exist-but-should&quot;&gt;Pages that Don&#x27;t Exist, but Should&lt;&#x2F;h2&gt;
&lt;p&gt;When writing notes, I link freely, even if the page I&#x27;m linking to doesn&#x27;t exist yet. Then, every couple of days, I&#x27;ll have a look at my &quot;pages that don&#x27;t exist but should&quot; note, which has a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blacksmithgu.github.io&#x2F;obsidian-dataview&#x2F;&quot;&gt;dataview&lt;&#x2F;a&gt; query that shows me all those nonexistent pages, ranked by how many incoming links they would have if they did exist.&lt;&#x2F;p&gt;
&lt;p&gt;For example, if I look at that page right now, I can see that I haven&#x27;t made a note for Phoenix Liveview—I&#x27;ve been looking into it for a side project and have made notes on a couple of conference talks where I&#x27;ve linked to it. There&#x27;s also a missing note for some features of the product I work on, since I&#x27;ve mentioned them in daily notes.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m fairly happy with this approach. It&#x27;s a bit easier than looking for greyed-out nodes in the graph view, and over time it&#x27;s lead to some interesting practices. For example, I started linking the names of my coworkers when I paired or collaborated with them on something, which eventually lead to keeping notes on what other teams around the company are responsible for. I&#x27;m not sure I would have started that (quite useful) practice if I hadn&#x27;t been paying attention to what &quot;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.wikiwand.com&#x2F;en&#x2F;Desire_path&quot;&gt;desire paths&lt;&#x2F;a&gt;&quot; I was creating in my vault.&lt;&#x2F;p&gt;
&lt;p&gt;In case it&#x27;s useful to someone else, here&#x27;s the dataview script I embed for this. There&#x27;s probably a better way to get this information, but it&#x27;s been working well as-is. This version filters out daily notes and doesn&#x27;t consider media files, but you may want to remove those lines if being prompted to create daily notes far in the past or future sounds useful to you:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; phantoms&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; dv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;pages&lt;&#x2F;span&gt;&lt;span&gt;()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;flatMap&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; p.file.outlinks.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;link&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; [link, p.file]))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;groupBy&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; p[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;])&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; g.rows.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;endsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;.png&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;endsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;.jpeg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;endsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;.jpg&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;endsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;.mov&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;^&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DBEDFF;&quot;&gt;-Q&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;^&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DBEDFF;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DBEDFF;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      !&lt;&#x2F;span&gt;&lt;span&gt;g.key.path.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;^&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DBEDFF;&quot;&gt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\d\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; { key: g.key, rows: g.rows.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;r&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; r[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]) };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  })&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;filter&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; dv.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;page&lt;&#x2F;span&gt;&lt;span&gt;(g.key)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; undefined&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  .&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; g.rows.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;desc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dv.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;table&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;Page to Create&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;Linked From&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;],&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  phantoms.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; [g.key, g.rows.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;((&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;p&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; p.link)]),&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;other-dataview-shenanigans&quot;&gt;Other Dataview Shenanigans&lt;&#x2F;h2&gt;
&lt;p&gt;Other than the stuff mentioned above, I have a handful of useful dataview pages. For example, I&#x27;m looking into replacing an older car we own right now. The page for that project has a dataview query summing up research I&#x27;ve done into cars I might want to buy to replace it. I also have a view of everything tagged &lt;code&gt;#possible-blog-post&lt;&#x2F;code&gt; sorted by when I think I might want to publish it. None of these are huge, but they do a good job of gathering information from around my vault that I probably want to see all at once.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;anyway&quot;&gt;Anyway&lt;&#x2F;h2&gt;
&lt;p&gt;That&#x27;s how I organize my Obsidian vault. I have a few more plugins I use (e.g. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;liamcain&#x2F;obsidian-calendar-plugin&quot;&gt;a calendar widget&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;liamcain&#x2F;obsidian-periodic-notes&quot;&gt;improved daily notes&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;SilentVoid13&#x2F;Templater&quot;&gt;templater&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;esm7&#x2F;obsidian-map-view&quot;&gt;a map view&lt;&#x2F;a&gt; for when I was looking at stuff to do on vacation) but that&#x27;s the core of it. I hope you can take some of this and get use out of it for yourself!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Scope in the TH element</title>
		<published>2022-12-19T00:00:00+00:00</published>
		<updated>2022-12-19T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/scope-in-the-th-element/" type="text/html"/>
		<id>https://bytes.zone/posts/scope-in-the-th-element/</id>
		<content type="html">&lt;p&gt;The &lt;code&gt;&amp;lt;th&amp;gt;&lt;&#x2F;code&gt; (table head) element in HTML accepts a &lt;code&gt;scope&lt;&#x2F;code&gt; attribute. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;HTML&#x2F;Element&#x2F;th#attr-scope&quot;&gt;According to MDN&lt;&#x2F;a&gt;, it can be one of these:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;row&lt;&#x2F;code&gt; and &lt;code&gt;col&lt;&#x2F;code&gt;, which means that the header relates to all the cells of the group (row or column, respectively) in which it&#x27;s found&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;rowgroup&lt;&#x2F;code&gt; and &lt;code&gt;colgroup&lt;&#x2F;code&gt;, which specifies a group of rows or columns. I&#x27;m not sure how to make a group like this, though!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;th scope=&quot;row&quot;&amp;gt;&lt;&#x2F;code&gt; can be really useful for accessibility. Say you have a table like this:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Name&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Text-to-speech enabled&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: center&quot;&gt;Edit student&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Valencia Flowers&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;yes&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Romeo Harrison&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;no&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: center&quot;&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;If the &quot;name&quot; cells are &lt;code&gt;&amp;lt;th scope=&quot;row&quot;&amp;gt;&lt;&#x2F;code&gt;, screen readers can contextualize &quot;yes&quot; or &quot;no&quot; and the edit button when navigating between rows in a non-name column. For example, one might say something like &quot;text-to-speech enabled, Valencia Flowers, row, yes.&quot;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Projects</title>
		<published>2022-12-14T00:00:00+00:00</published>
		<updated>2022-12-14T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/projects/" type="text/html"/>
		<id>https://bytes.zone/posts/projects/</id>
		<content type="html">&lt;p&gt;Just a little note: inspired by &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;sive.rs&#x2F;nowff&quot;&gt;&#x2F;now pages&lt;&#x2F;a&gt;, I&#x27;ve put a bunch of current and past projects up on the home page at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bytes.zone&quot;&gt;bytes.zone&lt;&#x2F;a&gt;, listed by date at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;projects&#x2F;&quot;&gt;bytes.zone&#x2F;projects&lt;&#x2F;a&gt;.
I&#x27;ll update them occasionally, adding blog posts for RSS readers as relevant.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re interested in what I&#x27;ve been up to lately, there it is!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Alloy</title>
		<published>2022-12-12T00:00:00+00:00</published>
		<updated>2022-12-12T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/alloy/" type="text/html"/>
		<id>https://bytes.zone/posts/alloy/</id>
		<content type="html">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;alloytools.org&#x2F;&quot;&gt;Alloy&lt;&#x2F;a&gt; is a tool for modeling software. It&#x27;s best at representing things like databases or data structures, but in my experience it does fine with things like UI states or processes as well.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve found Alloy most helpful in exploring the relationship between different parts of a system. Unlike other tools in the formal methods space, Alloy has a visualizer. For me, this is a huge advantage: not only does it make it easier for me to think about my models, but it also makes communicating about specs with people who are not familiar with Alloy much nicer. Having something you can talk about or mark up enables better communication, as opposed to having to read states and relationships out of a table.&lt;&#x2F;p&gt;
&lt;p&gt;To illustrate that, here&#x27;s an oversimplified version of email. I&#x27;ve told Alloy that there are many accounts on a server, and that accounts can send emails back and forth. This should even work across servers, as long as the servers can deliver to each other. The code looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Email&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Account&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Account&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Account&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: one&lt;&#x2F;span&gt;&lt;span&gt; Server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;sig&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Server&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  deliversTo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;: set&lt;&#x2F;span&gt;&lt;span&gt; Server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;To start off, Alloy can generate a &quot;metamodel&quot; (that is, the relationships between things it knows about.) It basically looks like you might expect given the code:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;email-metamodel.png&quot; alt=&quot;a metamodel visualization generated by Alloy, with Email pointing &amp;quot;to&amp;quot; and &amp;quot;from&amp;quot; arrows to Account, Account pointing a &amp;quot;server&amp;quot; error to Server, and Server pointing &amp;quot;deliversTo&amp;quot; arrow to itself.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;With no code changes, it can also generate as many instances of the model as you care to ask it for. Here&#x27;s one, where an email is being sent from one server to another:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;deliverable-email.png&quot; alt=&quot;an example generated by Alloy, with an email being delivered from an account on one server to an account on another&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve found it useful to set up relationships like this, then ask the tool to generate examples. If something looks off or out of the ordinary, I can go back and have a think (maybe with other people) about whether or not that thing should be allowed. For example, I notice that one email server above can&#x27;t see the other, and the other can&#x27;t deliver to its own accounts. Is that reasonable? Depending on the situation you&#x27;re modeling, it may or may not be!&lt;&#x2F;p&gt;
&lt;p&gt;You can use Alloy to make assertions about your models as well. In the example above, for example, you could assert that there couldn&#x27;t be any undeliverable email (that is, any email going between servers without a &lt;code&gt;deliversTo&lt;&#x2F;code&gt; relationship.) If you&#x27;ve allowed that possibility somehow, Alloy will tell you why and generate a counterexample. Here&#x27;s how we&#x27;d do that:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;check&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; NoUndeliverableEmail&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  all&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Email &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;|&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;to&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;server &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.*&lt;&#x2F;span&gt;&lt;span&gt;deliversTo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In English, this means &quot;for an email to be deliverable, the server in &lt;code&gt;from&lt;&#x2F;code&gt; must be able to reach the server in &lt;code&gt;to&lt;&#x2F;code&gt; through the &lt;code&gt;deliversTo&lt;&#x2F;code&gt; relation, 0 or more times. And, by the way, every Email is deliverable.&quot; Of course, we haven&#x27;t guaranteed that assertion in any way, so Alloy comes up with a counterexample:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;undeliverable-email.png&quot; alt=&quot;an example generated by Alloy, with an account trying to send email to a server that its server cannot deliver to&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;In this, &lt;code&gt;Server1&lt;&#x2F;code&gt; can deliver everywhere, &lt;code&gt;Server0&lt;&#x2F;code&gt; can deliver to &lt;code&gt;Server2&lt;&#x2F;code&gt;, and &lt;code&gt;Server2&lt;&#x2F;code&gt; can&#x27;t deliver anywhere. Unfortunately for everyone involved, &lt;code&gt;Account0&lt;&#x2F;code&gt; on &lt;code&gt;Server2&lt;&#x2F;code&gt; wants to send an email to their buddy on &lt;code&gt;Server1&lt;&#x2F;code&gt;. Guess you&#x27;re outta luck, pal!&lt;&#x2F;p&gt;
&lt;p&gt;We can try to fix this, of course, by saying that all of these &lt;code&gt;deliversTo&lt;&#x2F;code&gt; relations are symmetric. We&#x27;d just establish some fact that Alloy will take as given:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;alloy&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fact&lt;&#x2F;span&gt;&lt;span&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;all&lt;&#x2F;span&gt;&lt;span&gt; delivery relationships are symmetric&amp;quot; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  deliversTo &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;= ~&lt;&#x2F;span&gt;&lt;span&gt;deliversTo&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This works because &lt;code&gt;deliversTo&lt;&#x2F;code&gt; is actually a set of tuples; the &lt;code&gt;server.deliversTo&lt;&#x2F;code&gt; is doing a lookup into it! The &lt;code&gt;~&lt;&#x2F;code&gt; operator reverses all the tuples, so we&#x27;re asserting that the forward version is equal to the backwards one—symmetric!&lt;&#x2F;p&gt;
&lt;p&gt;But in the face of this stunning victory, Alloy replies &quot;ah, yes, but what if the servers are on completely separate networks?&quot;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;separate-networks.png&quot; alt=&quot;an example generated by Alloy showing two completely independent networks of email servers with one account trying to send an email across them. There are no connections between the networks, so this email is undeliverable.&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;We can keep going down this rabbit hole of generating examples and making assertions until we&#x27;re satisfied that the system has few enough problems to implement!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-is-this-useful&quot;&gt;Why is this Useful?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve used Alloy in a lot of situations now where I would previously have just been guessing at edge cases in the model. It&#x27;s been really useful to show me exactly what problems exist in my model and helping me communicate with other people about why we should care about them.&lt;&#x2F;p&gt;
&lt;p&gt;Alloy helps simplify models, as well! For example, at work we were building a feature with inline commenting (like Google Docs.) We started by modeling Google Docs&#x27; comments and it turned out that each comment had something like 32 unique states. We realized this was going to be way too much for our use case, and simplified it down to 4. We probably could have had this realization without using Alloy, but it made the conversation much simpler—the designer and I sat down and looked at the visualizations, then kept saying &quot;but for us, states X and Y would be the same…&quot; until we arrived at a simpler model. It was really nice, and only took about an hour and a half of modeling to come up with a solution that satisfied all our requirements!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;yeah-but&quot;&gt;Yeah, but…&lt;&#x2F;h2&gt;
&lt;p&gt;So all that&#x27;s super exciting, and I love using Alloy, but it&#x27;d be silly to pretend that it&#x27;s perfect. Here&#x27;s some examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s very easy to give Alloy contradicting instructions that rule out some case you care about. Sometimes this even means that Alloy can&#x27;t come up with any examples at all! It can be really difficult to debug this when you&#x27;re getting started. (But if you change the solver to &quot;MiniSat with Unsat Core&quot; you can at least get a hint at what might be conflicting.)&lt;&#x2F;li&gt;
&lt;li&gt;Alloy only checks a finite size of the models you generate (4 of everything by default.) This means that it won&#x27;t find bugs that require there to be a huge number of something. However, almost everything I&#x27;ve done in Alloy gives useful feedback with the default limits—it checks all states comprehensively within the given size—and it&#x27;s easy (bordering on trivial) to raise the limit if you suspect there would be a bug if you had more instances. Just know going in that you&#x27;re not going to get proof for an infinitely sized model. This property does mean that Alloy is very, very fast, though!&lt;&#x2F;li&gt;
&lt;li&gt;As a beginner, it&#x27;s hard to know how to learn. The usual learning path is to buy and work through &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;alloytools.org&#x2F;book.html&quot;&gt;&lt;em&gt;Software Abstractions&lt;&#x2F;em&gt; by Alloy&#x27;s author, Daniel Jackson&lt;&#x2F;a&gt;. However, Alloy has evolved over time and now has important features—like temporal logic—that are not covered in the book. To make matters worse, documentation outside of the normal learning path often means reading &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;alloytools.org&#x2F;spec.html&quot;&gt;the spec&lt;&#x2F;a&gt;. However, once you know a bit about Alloy, and even while you&#x27;re learning, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloy.readthedocs.io&#x2F;en&#x2F;latest&#x2F;&quot;&gt;Hillel Wayne&#x27;s Alloy reference&lt;&#x2F;a&gt; is great.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Another one of Alloy&#x27;s weakness &lt;em&gt;used to be&lt;&#x2F;em&gt; time—that is, it was hard to model things that could change—but the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;alloytools.org&#x2F;alloy6.html&quot;&gt;release of Alloy 6&lt;&#x2F;a&gt; added temporal logic and made this situation a lot more tenable. I still haven&#x27;t completely come to grips with the new features, but I see concepts in the spec that I first learned about in TLA+—although I appreciate that Alloy uses English-language keywords instead of symbol operators like &lt;code&gt;[]&lt;&#x2F;code&gt; (&lt;code&gt;always&lt;&#x2F;code&gt; in Alloy) or &lt;code&gt;&amp;lt;&amp;gt;&lt;&#x2F;code&gt; (&lt;code&gt;eventually&lt;&#x2F;code&gt; in Alloy.)&lt;&#x2F;p&gt;
&lt;p&gt;All told, I recommend learning Alloy! I&#x27;ve found it tremendously helpful and I think it&#x27;s too bad that it&#x27;s not better-known by industry programmers.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>tree-grepper</title>
		<published>2021-08-31T00:00:00+00:00</published>
		<updated>2021-08-31T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/tree-grepper/" type="text/html"/>
		<id>https://bytes.zone/posts/tree-grepper/</id>
		<content type="html">&lt;p&gt;A while ago I wanted to build an import graph from all the frontend code at NoRedInk to build some developer tools.
The code I wrote ended up working fine, but it was also pretty messy… tons of big regular expressions to make sure I got all the corner cases and whitespace allowed in the syntax.
I thought there probably was a better way, especially since I had just learned about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tree-sitter.github.io&#x2F;tree-sitter&#x2F;&quot;&gt;tree-sitter&lt;&#x2F;a&gt;.
I also wanted to learn some Rust, so… well, it sounded like a fun little project!&lt;&#x2F;p&gt;
&lt;p&gt;Fast forward about a year and I just released &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;tree-grepper&quot;&gt;&lt;code&gt;tree-grepper&lt;&#x2F;code&gt; 2.0.0&lt;&#x2F;a&gt;!
It lets you search very quickly across large projects full of diverse filetypes, using tree-sitter grammars and search queries.&lt;&#x2F;p&gt;
&lt;p&gt;The big benefit here is that it&#x27;s easy to expand: tree-sitter is getting really popular, what with language servers and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;neovim.io&#x2F;doc&#x2F;treesitter&#x2F;&quot;&gt;Neovim extensions&lt;&#x2F;a&gt; and everything, so it&#x27;s pretty likely that someone has already built a parser that we can just add!
Currently tree-grepper lets you search Elm, Haskell, JavaScript, Ruby, Rust, and TypeScript, but it&#x27;s pretty easy to add more, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;tree-grepper#supported-languages&quot;&gt;the README has a step-by-step guide&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Tree-grepper is focused only on search: it doesn&#x27;t do linting (like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;semgrep.dev&quot;&gt;semgrep&lt;&#x2F;a&gt;) or AST-based refactoring (like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;comby.dev&#x2F;&quot;&gt;comby&lt;&#x2F;a&gt;.)
You can get structured match data out of it, but any further processing is another tool&#x27;s responsibility.
This let me cut down on scope significantly, and optimize for searching alone.
Hooray for the Unix philosophy!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;extracting-an-import-graph&quot;&gt;Extracting An Import Graph&lt;&#x2F;h2&gt;
&lt;p&gt;Let me show off what it can do a little bit by implementing the parsing task I set out to do originally!&lt;&#x2F;p&gt;
&lt;p&gt;Tree-sitter implements an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;tree-sitter.github.io&#x2F;tree-sitter&#x2F;using-parsers#pattern-matching-with-queries&quot;&gt;s-expression query API&lt;&#x2F;a&gt; which tree-grepper exposes directly.
We&#x27;ll use that to query for all the imports in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;noredink&#x2F;noredink-ui&quot;&gt;NoRedInk&#x2F;noredink-ui&lt;&#x2F;a&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(import_clause)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:18:1:query:import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Sort exposing&lt;&#x2F;span&gt;&lt;span&gt; (Sorter)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:3:1:query:import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Accessibility.Styled as Html exposing&lt;&#x2F;span&gt;&lt;span&gt; (Html,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; img, text&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:4:1:query:import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Browser exposing&lt;&#x2F;span&gt;&lt;span&gt; (Document,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; UrlRequest&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;..&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:5:1:query:import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Browser.Dom&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:6:1:query:import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; Browser.Navigation exposing&lt;&#x2F;span&gt;&lt;span&gt; (Key)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What we&#x27;re doing here is asking &lt;code&gt;tree-grepper&lt;&#x2F;code&gt; to give us all the &lt;code&gt;import_clause&lt;&#x2F;code&gt; nodes it finds in &lt;code&gt;elm&lt;&#x2F;code&gt; files (I&#x27;ll tell you how to find out how this is called an &lt;code&gt;import_clause&lt;&#x2F;code&gt; later.)
Each match gets printed as a line of text along with the exact position in the source file.&lt;&#x2F;p&gt;
&lt;p&gt;We don&#x27;t want the entire match, though, just the module name (the part after &lt;code&gt;import&lt;&#x2F;code&gt; but before &lt;code&gt;as&lt;&#x2F;code&gt; or &lt;code&gt;exposing&lt;&#x2F;code&gt;).
Let&#x27;s try and get just the module name:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(import_clause (upper_case_qid)@import)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:18:7:import:Sort&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:3:8:import:Accessibility.Styled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:4:8:import:Browser&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:5:8:import:Browser.Dom&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:6:8:import:Browser.Navigation&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now we&#x27;re asking for &lt;code&gt;upper_case_qid&lt;&#x2F;code&gt; nodes inside &lt;code&gt;import_clause&lt;&#x2F;code&gt;s.
If we tag the nodes we care about (by naming them after an &lt;code&gt;@&lt;&#x2F;code&gt;), &lt;code&gt;tree-grepper&lt;&#x2F;code&gt; will only output the parts we tagged.&lt;&#x2F;p&gt;
&lt;p&gt;So far, so good!
But how about &lt;code&gt;module Foo exposing (..)&lt;&#x2F;code&gt; at the tops of files, too?
Easy: just add another &lt;code&gt;--query&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(module_declaration (upper_case_qid)@module)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(import_clause (upper_case_qid)@import)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:1:8:module:Category&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:19:8:import:Sort&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:1:8:module:Main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:3:8:import:Accessibility.Styled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:4:8:import:Browser&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This follows the same pattern: we want a named child of another named node in the file, and &lt;code&gt;tree-grepper&lt;&#x2F;code&gt; manages walking the tree to give it to us.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;javascript-imports&quot;&gt;JavaScript Imports&lt;&#x2F;h2&gt;
&lt;p&gt;What about JavaScript &lt;code&gt;import&lt;&#x2F;code&gt; clauses?
We can mix different languages as easily as we mix different queries, but let&#x27;s do one at a time for simplicity&#x27;s sake:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; javascript &amp;#39;(import_statement (string (string_fragment)@import) .)&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;lib&#x2F;TextArea&#x2F;V4.js:1:31:import:..&#x2F;CustomElement&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the end of a &lt;code&gt;import * as Foo from &quot;Bar&quot;&lt;&#x2F;code&gt; clause.
The &lt;code&gt;.&lt;&#x2F;code&gt; at the end is an anchor: it tells tree-sitter that we care that the thing we&#x27;re matching is the last child of its parent node.
You can also put a &lt;code&gt;.&lt;&#x2F;code&gt; right after the node name to match on the first node only, or on both sides to enforce that you are matching the only node.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re also matching a string fragment out of the string we require.
This lets us remove any quoting characters so we get &lt;code&gt;..&#x2F;CustomElement&lt;&#x2F;code&gt; out instead of &lt;code&gt;&#x27;..&#x2F;CustomElement&#x27;&lt;&#x2F;code&gt;.
We could also put anchors here to make sure we&#x27;re not getting any interpolated strings, but I haven&#x27;t needed to do that yet so we&#x27;re not here either.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;javascript-requires&quot;&gt;JavaScript Requires&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, let&#x27;s get &lt;code&gt;require&lt;&#x2F;code&gt; calls:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; javascript &amp;#39;(call_expression (identifier)@_fn (arguments . (string (string_fragment)@require) .) (#eq? @_fn require))&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;manifest.js:1:8:require:..&#x2F;lib&#x2F;index.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;script&#x2F;percy-tests.js:1:29:require:@percy&#x2F;script&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;script&#x2F;axe-puppeteer.js:4:27:require:puppeteer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;script&#x2F;axe-puppeteer.js:5:25:require:axe-core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;script&#x2F;axe-puppeteer.js:6:37:require:url&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is quite the query!
Let&#x27;s break it up over multiple lines to talk about it:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;common-lisp&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(call_expression&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (identifier)@_fn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (arguments &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt; (string_fragment)@require) &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  (#eq? @_fn &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;require&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;(call_expression (identifier) (arguments))&lt;&#x2F;code&gt; is a function or method call, in our case &lt;code&gt;require(&quot;url&quot;)&lt;&#x2F;code&gt;.
However, without anchors or specifying the arguments we&#x27;re not saying anything about the contents, just that it&#x27;s a call.
In this case, we care that the arguments are only a single string, so we specify that as &lt;code&gt;(arguments . (string (string_fragment)@require) .)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, we don&#x27;t want &lt;em&gt;just any&lt;&#x2F;em&gt; function with a single string argument; we only want &lt;code&gt;require&lt;&#x2F;code&gt; statements.
Tree-sitter exposes a couple of matcher functions (&lt;code&gt;#eq?&lt;&#x2F;code&gt; for string equality and &lt;code&gt;#match?&lt;&#x2F;code&gt; for regular expressions) to select the things we want here.
To use them, we name the node we care about (here &lt;code&gt;@_fn&lt;&#x2F;code&gt; with a leading underscore to tell tree-grepper to drop it from the output,) then give the match to &lt;code&gt;#eq?&lt;&#x2F;code&gt; along with a bare word (&lt;code&gt;require&lt;&#x2F;code&gt;) to check for equality.
Now we only match nodes that look like &lt;code&gt;require(&#x27;axe-core&#x27;)&lt;&#x2F;code&gt;, but only pull out the inner &lt;code&gt;axe-core&lt;&#x2F;code&gt; string that we care about!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;putting-it-all-together&quot;&gt;Putting It All Together&lt;&#x2F;h2&gt;
&lt;p&gt;Of course, we can do this all at once, smashing all our queries together in one giant &lt;code&gt;tree-grepper&lt;&#x2F;code&gt; invocation.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(import_clause (upper_case_qid)@import)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; elm &amp;#39;(module_declaration (upper_case_qid)@module)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; javascript &amp;#39;(import_statement (string (string_fragment)@import) .)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; javascript &amp;#39;(call_expression (identifier)@_fn (arguments . (string (string_fragment)@require) .) (#eq? @_fn require))&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; head&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -n 5&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:1:8:module:Category&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Category.elm:19:8:import:Sort&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:1:8:module:Main&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:3:8:import:Accessibility.Styled&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;.&#x2F;styleguide-app&#x2F;Main.elm:4:8:import:Browser&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That works for any amount of queries you&#x27;d care to throw at it!
In fact, it&#x27;s more efficient to run this way since it only has to walk the filesystem and parse files once!&lt;&#x2F;p&gt;
&lt;p&gt;You can also get more information by specifying the format: the &lt;code&gt;json&lt;&#x2F;code&gt; and &lt;code&gt;pretty-json&lt;&#x2F;code&gt; formats have match end locations as well as starts, and they include the node names returned.
You can use that to get an overview of all the node names in a grammar:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; echo &amp;#39;console.log(&amp;quot;Hello, World!&amp;quot;)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; hello.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tree-grepper&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --query&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; javascript &amp;#39;(_)&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --format&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; pretty-json hello.js&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    &amp;quot;file&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;hello.js&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    &amp;quot;file_type&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;javascript&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;    &amp;quot;matches&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;kind&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;program&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;query&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;text&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;console.log(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;Hello, World!&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;)\n&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;start&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;          &amp;quot;row&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: 1,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;          &amp;quot;column&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;        &amp;quot;end&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;          &amp;quot;row&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: 2,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;          &amp;quot;column&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      ... snip ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I&#x27;ve actually had to shorten that significantly because it&#x27;s so long!
If you&#x27;d like to see some full output, check the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;tree-grepper&#x2F;tree&#x2F;main&#x2F;src&#x2F;snapshots&quot;&gt;&lt;code&gt;all_{language}.snap&lt;&#x2F;code&gt; snapshot test files in the tree-grepper repo&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I really enjoyed writing this tool and learning more about tree-sitter, and I hope you find it useful!
You can also get the source and instructions for contributing at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;tree-grepper&quot;&gt;github.com&#x2F;BrianHicks&#x2F;tree-grepper&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Tree-grepper is packaged using Nix, so if you have that you can just install it like &lt;code&gt;nix-env -if https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;tree-grepper&#x2F;archive&#x2F;refs&#x2F;heads&#x2F;main.tar.gz&lt;&#x2F;code&gt;.
If you have Nix flakes enabled, you can also run &lt;code&gt;nix shell github:BrianHicks&#x2F;tree-grepper&lt;&#x2F;code&gt; to get a shell with &lt;code&gt;tree-grepper&lt;&#x2F;code&gt; already available.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>The Four Rules of Simple Design: In Conclusion</title>
		<published>2021-08-25T00:00:00+00:00</published>
		<updated>2021-08-25T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/the-four-rules-of-simple-design-conclusion/" type="text/html"/>
		<id>https://bytes.zone/posts/the-four-rules-of-simple-design-conclusion/</id>
		<content type="html">&lt;p&gt;OK, it&#x27;s been a couple of weeks since I published &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-4-code-for-now&#x2F;&quot;&gt;Rule 4: Code for Now&lt;&#x2F;a&gt;, and I&#x27;ve had some interesting feedback!&lt;&#x2F;p&gt;
&lt;p&gt;The biggest thing first: how do you apply these?
Surely you don&#x27;t just go down the list one at a time, right?
Well, no, I view it more like like a state machine: you enter at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-1-simplify-when-the-program-works&#x2F;&quot;&gt;Rule 1: Simplify When the Program Works&lt;&#x2F;a&gt; and move from there back and forth between &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-2-clarify-your-intent&#x2F;&quot;&gt;Rule 2: Clarify Your Intent&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-3-centralize-behavior&#x2F;&quot;&gt;Rule 3: Centralize Behavior&lt;&#x2F;a&gt;.
Once you think you&#x27;re more-or-less done, you check your work with &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-4-code-for-now&#x2F;&quot;&gt;Rule 4: Code for Now&lt;&#x2F;a&gt;.
When you don&#x27;t have anywhere else to go—that is, you don&#x27;t see an opportunity to apply any of the rules—you call it good and move on to the next thing you need to do that day!&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s not the only way to apply the rules, of course, but it&#x27;s one that I&#x27;ve found works pretty well for me.
J.B. Rainsberger applies the original rules something like this as well: see his post &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.jbrains.ca&#x2F;permalink&#x2F;the-four-elements-of-simple-design&quot;&gt;The Four Elements of Simple Design&lt;&#x2F;a&gt; for more, in particular his &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.jbrains.ca&#x2F;permalink&#x2F;the-four-elements-of-simple-design#the-two-elements-of-simple-design&quot;&gt;axioms of modular design&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Of course, it&#x27;s not the only way by a long shot! One of my &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;www.mike.is&#x2F;&quot;&gt;colleagues at NoRedInk&lt;&#x2F;a&gt; pointed out that he&#x27;d like to rearrange these rules:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Code for now&lt;&#x2F;li&gt;
&lt;li&gt;Simplify when the program works&lt;&#x2F;li&gt;
&lt;li&gt;Clarify intent&lt;&#x2F;li&gt;
&lt;li&gt;Centralize behavior&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That could certainly work too! It&#x27;s up to you how you apply these, if you want to apply them at all.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s also some tension between the rules. For example, in &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-3-centralize-behavior&#x2F;&quot;&gt;Rule 3: Centralize Behavior&lt;&#x2F;a&gt; I talked a lot about making a &lt;code&gt;Point2d&lt;&#x2F;code&gt; class.
But… is that structure really &lt;em&gt;necessary&lt;&#x2F;em&gt;?
Couldn&#x27;t it violate &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-4-code-for-now&#x2F;&quot;&gt;Rule 4: Code for Now&lt;&#x2F;a&gt;?
How do you decide which to apply?
In the end, you&#x27;ve either got to choose randomly or find a tiebreaker: for example, you could use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;thoughtbot.com&#x2F;blog&#x2F;sandi-metz-rules-for-developers&quot;&gt;Sandi Metz&#x27; Rules For Developers&lt;&#x2F;a&gt;, which emphasize very small classes.
In that context, building a &lt;code&gt;Point2d&lt;&#x2F;code&gt; may or may not be OK depending on how close your class is to 100 lines.&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I&#x27;ve usually gone the other way over the time: I&#x27;ve randomly chosen to prioritize rules randomly based on the situation at hand.
However, since part of the appeal of this whole exercise is that they allow us to reduce guessing about the future, I&#x27;m going to continue to explore situations where there seem to be holes or conflicts!&lt;&#x2F;p&gt;
&lt;p&gt;And, to that end: if you read through the original sources (or mine) and are inspired to make your own remix of the four rules, I&#x27;d love to read it!
Please send me your takes!
Let&#x27;s build on each other&#x27;s work to discover how to make better software.&lt;&#x2F;p&gt;
&lt;p&gt;Thanks for reading!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Rule 4: Code for Now</title>
		<published>2021-07-13T00:00:00+00:00</published>
		<updated>2021-07-13T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/rule-4-code-for-now/" type="text/html"/>
		<id>https://bytes.zone/posts/rule-4-code-for-now/</id>
		<content type="html">&lt;p&gt;Last week, we talked about &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-3-centralize-behavior&#x2F;&quot;&gt;centralizing behavior&lt;&#x2F;a&gt; as part of the &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;my-take-on-the-four-rules-of-simple-design&#x2F;&quot;&gt;four rules of simple design series&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This week, the final rule: &lt;strong&gt;code for now&lt;&#x2F;strong&gt;.
This is about focusing on building the smallest thing that can possibly work.&lt;&#x2F;p&gt;
&lt;p&gt;Another way to put it: are you writing code because you&#x27;re guessing that you&#x27;ll need something later?
Stop it!&lt;&#x2F;p&gt;
&lt;p&gt;This shows up most commonly when building larger pieces of program structure.
In my experience:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;In object-oriented languages like Python and Ruby, it can look like deep class hierarchies and composition by mixins or inheritance.&lt;&#x2F;li&gt;
&lt;li&gt;In Haskell, it can look like lots of custom typeclasses or adding high-complexity features like lenses or monad transformer stacks.&lt;&#x2F;li&gt;
&lt;li&gt;In Elm, it can look like factoring out every little piece of state into a separate model&#x2F;update&#x2F;view triple, or splitting things into smaller and smaller modules.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In summary: don&#x27;t add structure to code before you need it.
(And if we&#x27;re being honest, this rule is not far from &quot;You Aren&#x27;t Gonna Need It&quot; in different clothes.)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happens-if-you-ignore-this&quot;&gt;What Happens If You Ignore This?&lt;&#x2F;h2&gt;
&lt;p&gt;Have you ever seen &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;EnterpriseQualityCoding&#x2F;FizzBuzzEnterpriseEdition&quot;&gt;FizzBuzzEnterpriseEdition&lt;&#x2F;a&gt;?
That.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;but-where-do-i-put-code&quot;&gt;But Where Do I Put Code?&lt;&#x2F;h2&gt;
&lt;p&gt;Removing unnecessary structure can cause some confusion when you want to add some functionality but there&#x27;s no obvious place for it.
In that case: add structure, because you need it &lt;em&gt;now&lt;&#x2F;em&gt;.
The point of the rule is to avoid guessing about what you&#x27;ll need in the future, not to avoid all kinds of structure.
This rule is also the catch-all at the end of the four rules.
You shouldn&#x27;t need to invoke it often.
If you don&#x27;t know where to put code, make sure you&#x27;re &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-3-centralize-behavior&#x2F;&quot;&gt;centralizing behavior&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-2-clarify-your-intent&#x2F;&quot;&gt;clarifying your intent&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Rule 3: Centralize Behavior</title>
		<published>2021-07-06T00:00:00+00:00</published>
		<updated>2021-07-06T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/rule-3-centralize-behavior/" type="text/html"/>
		<id>https://bytes.zone/posts/rule-3-centralize-behavior/</id>
		<content type="html">&lt;p&gt;Last week, we talked about &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-2-clarify-your-intent&#x2F;&quot;&gt;clarifying intent&lt;&#x2F;a&gt; as part of the &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;my-take-on-the-four-rules-of-simple-design&#x2F;&quot;&gt;four rules of simple design series&lt;&#x2F;a&gt;.
This week, we&#x27;re on to rule 3: &lt;strong&gt;centralize behavior.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Imagine you&#x27;re working on project for a school, trying to make sure that the site addresses users appropriately: students should be addressed casually (&quot;Hello, Adrian!&quot;) while teachers should be addressed more formally (&quot;Your teacher, Dr. Beans&quot;.) Imagine that this has been done, but a little bit at a time and in different ways, so that these forms of address are implemented in different and inconsistent ways across the site. That makes what seems like a simple change much more difficult!&lt;&#x2F;p&gt;
&lt;p&gt;In order to make this change tractable, you might create a function or method that takes a &lt;code&gt;User&lt;&#x2F;code&gt; or &lt;code&gt;Role&lt;&#x2F;code&gt; domain concept and returns the right string form, then change each call site across the whole project to use that. A big project, sure, but a pretty reasonable approach! This demonstrates our third rule: &lt;strong&gt;centralize behavior&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This is similar in spirit to the ideas behind Don&#x27;t Repeat Yourself. In that context, &quot;repetition&quot; isn&#x27;t talking about the written form of code so much as authority over defining the behaviors and ideas in the system. A similar concept comes up in Once and Only Once: define each concept in your system in exactly one place, and don&#x27;t split up that authority!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happens-if-you-ignore-this&quot;&gt;What Happens If You Ignore This&lt;&#x2F;h2&gt;
&lt;p&gt;Like in the scenario above, ignoring this rule results in hard-to-change code which needs lots of extra effort to modify successfully. Did you get all the little pieces? Better hope so!&lt;&#x2F;p&gt;
&lt;p&gt;Problem is, it&#x27;s often hard to see this happening—the name change I described above feels a bit contrived because it&#x27;s easy to see, but template systems in web frameworks will push you away from specifying one-off logic and towards reusable functions or methods. I&#x27;ve seen this come up more often in serialization and validation logic: places where it&#x27;s very tempting to just dig into some internal data structure instead of defining behavior where the code lives.&lt;&#x2F;p&gt;
&lt;p&gt;One question to ask is &quot;who should be responsible for this behavior?&quot; In the long term, it&#x27;s nice to answer this by writing behavior attractors.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;behavior-attractors&quot;&gt;Behavior Attractors&lt;&#x2F;h2&gt;
&lt;p&gt;Behavior attractors (a term coined by Corey Haines in his book &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;leanpub.com&#x2F;4rulesofsimpledesign&quot;&gt;Understanding the 4 Rules of Simple Design&lt;&#x2F;a&gt;) are places in your code where it becomes natural to put related code, like water flowing downhill. This gives you a natural way to centralize behavior to keep your system simple and easy to understand.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s look at an example! &lt;em&gt;Understanding the 4 rules of Simple Design&lt;&#x2F;em&gt; uses Conway&#x27;s Game of Life throughout, so let&#x27;s just steal the example Haines uses to explain this: how do we figure out the neighbors of a cell? When you&#x27;re implementing the Game of Life, you might end up writing &lt;code&gt;Cell.neighbors&lt;&#x2F;code&gt; in the course of answering &quot;is this cell alive or dead in the next cycle?&quot; However, this is not necessarily the best way to implement this, because the concept of a neighbor really depends on the topology of the grid. &quot;Neighbor&quot; means something different for 2- versus 3-dimensional grids, not to mention things like hyperbolic geometry. That suggests that &quot;are these cells neighbors&quot; should be defined by the coordinate system, but you might not come to this realization if you&#x27;re just passing around 2-dimensional coordinates like &lt;code&gt;(x, y)&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The behavior attractor solution is to move those coordinates to a central definition (say, &lt;code&gt;2DCoord&lt;&#x2F;code&gt;) and implement the &lt;code&gt;neighbors&lt;&#x2F;code&gt; function&#x2F;method there instead. That means you might end up with &lt;code&gt;2DCoord(1, 1).neighbors&lt;&#x2F;code&gt; instead of &lt;code&gt;Cell.at(1, 1).neighbors&lt;&#x2F;code&gt;. You get a clearer separation of concerns: a &lt;code&gt;2DCoord&lt;&#x2F;code&gt; can clearly define neighbors in a way that a &lt;code&gt;Cell&lt;&#x2F;code&gt; shouldn&#x27;t be responsible for. The behavior attractor made this possible, and will make your life easier the next time you need to ask a question about the coordinate system too!&lt;&#x2F;p&gt;
&lt;p&gt;If you find that you don&#x27;t have a good place to put some code, &lt;em&gt;especially&lt;&#x2F;em&gt; if you find yourself just putting it close to the place you need it, consider writing a behavior attractor!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Rule 2: Clarify Your Intent</title>
		<published>2021-06-29T00:00:00+00:00</published>
		<updated>2021-06-29T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/rule-2-clarify-your-intent/" type="text/html"/>
		<id>https://bytes.zone/posts/rule-2-clarify-your-intent/</id>
		<content type="html">&lt;p&gt;Last week, we talked about &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-1-simplify-when-the-program-works&#x2F;&quot;&gt;simplifying when the program works&lt;&#x2F;a&gt; as part of the &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;my-take-on-the-four-rules-of-simple-design&#x2F;&quot;&gt;four rules of simple design series&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This week, rule two: &lt;strong&gt;clarify your intent.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;The original four rules prioritize clarity of communication&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#four-rules-are-about-communication&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. For example, Beck&#x27;s formulation says &quot;states every intention important to the programmer.&quot; Fowler tightens that up to &quot;reveals intent&quot;, and Rainsberger refines it further to &quot;improve names.&quot; But &lt;em&gt;what&lt;&#x2F;em&gt; intent are we trying to communicate clearly? Here are a few things you might want to consider:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What&#x27;s this thing for?&lt;&#x2F;li&gt;
&lt;li&gt;How are these two things related?&lt;&#x2F;li&gt;
&lt;li&gt;Why is this code doing things &lt;em&gt;this way&lt;&#x2F;em&gt; instead of &lt;em&gt;that way&lt;&#x2F;em&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Please don&#x27;t do &lt;em&gt;this thing&lt;&#x2F;em&gt; if you also do &lt;em&gt;that thing&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Which part of this is core to the program, and which is just supporting structure?&lt;&#x2F;li&gt;
&lt;li&gt;Where does the stuff you send here end up? Or, where is this thing getting stuff from?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can communicate these things in a lot of ways, but I want to talk about three: names, comments, and constructors.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;names&quot;&gt;Names&lt;&#x2F;h2&gt;
&lt;p&gt;Say you have a method called &lt;code&gt;process_data&lt;&#x2F;code&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#process-data-inspiration&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. Both of those words are really vague! What if we made them better? What&#x27;s &lt;code&gt;process&lt;&#x2F;code&gt;? What&#x27;s &lt;code&gt;data&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;Maybe you look at the method body and find out it downloads a CSV of accounts and selects all the ones the sales team has identified as likely to buy a premium license. So in this case, &lt;code&gt;process&lt;&#x2F;code&gt; would be incorrect—it&#x27;s &lt;code&gt;get&lt;&#x2F;code&gt; or &lt;code&gt;download&lt;&#x2F;code&gt;. And it&#x27;s not just any old &lt;code&gt;data&lt;&#x2F;code&gt;, it&#x27;s &lt;code&gt;clients&lt;&#x2F;code&gt;, specially ones on the threshold of upgrading.&lt;&#x2F;p&gt;
&lt;p&gt;So, while there are more ways you could improve this (e.g. separating the downloading from the data processing), renaming it to &lt;code&gt;get_threshold_clients&lt;&#x2F;code&gt; already makes it way easier to understand your code: imagine seeing &lt;code&gt;process_data&lt;&#x2F;code&gt; vs &lt;code&gt;get_threshold_clients&lt;&#x2F;code&gt; at a call site. Which would you prefer when reading later?&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot to think about when choosing a good name, so instead of saying more, I&#x27;m just going to link some things I&#x27;ve found helpful: &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;2018.elm-conf.us&#x2F;schedule&#x2F;ally-kelly-mcknight&quot;&gt;&lt;em&gt;Naming Things in Elm&lt;&#x2F;em&gt; by Ally McKnight&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;macwright.com&#x2F;2021&#x2F;02&#x2F;17&#x2F;the-naming-of-things.html&quot;&gt;&lt;em&gt;Picking better names for variables, functions, and projects&lt;&#x2F;em&gt; by Tom MacWright&lt;&#x2F;a&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.sitepoint.com&#x2F;whats-in-a-name-anti-patterns-to-a-hard-problem&#x2F;&quot;&gt;&lt;em&gt;What&#x27;s in a Name? Anti-Patterns to Hard Problem&lt;&#x2F;em&gt; by Katrina Owen&lt;&#x2F;a&gt;, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buttondown.email&#x2F;hillelwayne&#x2F;archive&#x2F;naming-things-is-a-poor-name-for-naming-things&#x2F;&quot;&gt;&lt;em&gt;&quot;Naming Things&quot; is a Poor Name for Naming Things&lt;&#x2F;em&gt; by Hillel Wayne&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;&#x2F;h2&gt;
&lt;p&gt;Another thing you could do to clarify your intent is to explicitly write it down in a comment. There&#x27;s the constant debate on &quot;what&quot; comments vs &quot;why&quot; comments, of course—&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;buttondown.email&#x2F;hillelwayne&#x2F;archive&#x2F;comment-the-why-and-the-what&#x2F;&quot;&gt;here&#x27;s Hillel Wayne again on why you need both&lt;&#x2F;a&gt;—but in either form, the best comments give the reader insight into what was going on and what you needed when you wrote your code.&lt;&#x2F;p&gt;
&lt;p&gt;I like to write long documentation comments framed like &quot;Hello, future us! I hope you&#x27;re having great day. Here&#x27;s what&#x27;s up.&quot; I&#x27;ve found those to be helpful, both when my coworkers review the code and when we revisit it months later while trying to do something else. Having little hints of intention scattered throughout the code helps remember things we need to keep in mind. Plus, like trying to find a good name, documenting why something exists can often help us realize some other refactor that could help us do what we&#x27;re doing better!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;constructors&quot;&gt;Constructors&lt;&#x2F;h2&gt;
&lt;p&gt;In addition to names and comments, you can often clarify your intent in by making certain circumstances impossible by construction. As a trivial example, you might know that a value can sometimes be null, so you represent it as an optional type. Or, if you know you&#x27;ll always have at least one item, you can use a non-empty list to hold the data. I&#x27;ve found this to be a lot easier in ML-family languages like Haskell or Elm, but you can do it in object-oriented languages as well.&lt;&#x2F;p&gt;
&lt;p&gt;There are tons of talks and examples of this online if you search for things like &quot;make illegal states unrepresentable&quot;. I like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=IcgmSRJHu_8&quot;&gt;Richard Feldman&#x27;s 2016 talk &lt;em&gt;Make Impossible States Impossible&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happens-if-you-ignore-this&quot;&gt;What Happens If You Ignore This?&lt;&#x2F;h2&gt;
&lt;p&gt;If you don&#x27;t clarify your intent when coding, you end up in situations where you have to re-establish context. In names, that might mean having to jump to the definition to figure out what&#x27;s actually happening. In the case of comments, that might mean having to spend a long time looking through the code in order to find the shape of the program&#x27;s core algorithm. In constructors, it means the same but trying to figure out if something should be possible or not.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t clarify intent, you&#x27;ll also miss out on great refactoring opportunities: in the &lt;code&gt;get_threshold_clients&lt;&#x2F;code&gt; example above, we might see that we want to separate getting the raw data and applying our precise filtering rules. That makes the code more modular (and more testable!) but we might not have seen it if we didn&#x27;t stop to clarify intent.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;In summary, remember that code is read many more times than it&#x27;s written. Keeping that in mind can go a long way to making things better for your coworkers (or just you, 6 months from now.) There are lot of ways to do this—way more than we can go over here—but you can get a long way by focusing on names, comments, and constructors.&lt;&#x2F;p&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;four-rules-are-about-communication&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;In fact, I think the entirety of the four rules is secretly about communication: write tests to communicate, remove duplication to make sure you&#x27;re not saying the same thing twice, remove any unnecessary distractions, and here where we&#x27;re being pretty explicit about intention!&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;process-data-inspiration&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Example inspired by Corey Haines&#x27; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;leanpub.com&#x2F;4rulesofsimpledesign&quot;&gt;&lt;em&gt;Understanding the Four Rules of Simple Design&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Rule 1: Simplify When the Program Works</title>
		<published>2021-06-22T00:00:00+00:00</published>
		<updated>2021-06-22T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/rule-1-simplify-when-the-program-works/" type="text/html"/>
		<id>https://bytes.zone/posts/rule-1-simplify-when-the-program-works/</id>
		<content type="html">&lt;p&gt;Last week, I introduced &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;my-take-on-the-four-rules-of-simple-design&#x2F;&quot;&gt;My Take on the Four Rules of Simple Design&lt;&#x2F;a&gt;.
To sum up, I&#x27;ve found Kent Beck&#x27;s four rules of simple design really interesting to think about, and I wanted to come up with my own formulation!&lt;&#x2F;p&gt;
&lt;p&gt;This week, we meet the first rule: &lt;strong&gt;simplify when the program works.&lt;&#x2F;strong&gt;
It&#x27;s similar to the original first rule (&quot;runs all the tests&quot;), but with a broader reach.
So in addition to passing tests we might include things like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Is it possible to get the program into an illegal or &quot;impossible&quot; state by using it?&lt;&#x2F;li&gt;
&lt;li&gt;Do the user-facing parts of the program look the way that the designer intended?&lt;&#x2F;li&gt;
&lt;li&gt;Is the program accessible to all kinds of users?
For example, can the program be operated using only a keyboard?
Only a mouse?
A screen reader?
(Broadly, Is the program &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;WAI&#x2F;WCAG21&#x2F;Understanding&#x2F;intro#understanding-the-four-principles-of-accessibility&quot;&gt;Perceivable, Operable, Understandable, and Robust&lt;&#x2F;a&gt;?&lt;&#x2F;li&gt;
&lt;li&gt;Has QA tested this?
What did they think?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some of those might not apply to your project, and some of them have really long iteration times, but that&#x27;s actually part of the point: if you find that your program is broken in some way, it probably makes sense to fix it before trying to make it faster or organize the code better.
Unit tests are just one way of looking at this, although certainly an attractive one since they can be automated.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-happens-if-you-ignore-this&quot;&gt;What Happens If You Ignore This?&lt;&#x2F;h2&gt;
&lt;p&gt;What happens if you start simplifying the code before the program works?&lt;&#x2F;p&gt;
&lt;p&gt;Well, when I&#x27;ve ignored this advice, I notice I tend to start ripping out useful code or changing things in ways that don&#x27;t make sense.
I end up totally muddling my code with unrelated changes, and despite all the effort the program &lt;em&gt;still&lt;&#x2F;em&gt; doesn&#x27;t work right.
Eventually, I&#x27;ve maybe made something kinda-sorta like the change I tried to make, but it&#x27;s way harder for my team to review.&lt;&#x2F;p&gt;
&lt;p&gt;In my experience, I need to be in one of two modes: making changes to make the program work, or making changes to make the code simpler.
&quot;Simplify when the program works&quot; has been helpful for me in recognizing when I need to switch!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;avoiding-perfectionism&quot;&gt;Avoiding Perfectionism&lt;&#x2F;h2&gt;
&lt;p&gt;But what happens if the vagueness in this rule works against you?
For example, you can pile so many things on to &quot;works&quot; that it becomes &quot;perfect.&quot;
Setting an unattainable standard means never shipping, and we&#x27;d like to at least ship &lt;em&gt;some day&lt;&#x2F;em&gt;, so let&#x27;s try to avoid perfectionism, right?&lt;&#x2F;p&gt;
&lt;p&gt;One way around this is to change your relationship to your standards, at least temporarily.
Something doesn&#x27;t have to be &lt;em&gt;perfect&lt;&#x2F;em&gt; to be &lt;em&gt;working&lt;&#x2F;em&gt;.
For example, maybe messy code is fine &lt;em&gt;for now&lt;&#x2F;em&gt;, or maybe it&#x27;s OK for some tests to fail—just for a bit—while you work on accessibility in the UI.&lt;&#x2F;p&gt;
&lt;p&gt;Said differently, we can take on tech debt to avoid perfectionism.&lt;&#x2F;p&gt;
&lt;p&gt;We usually want to avoid that, and you might have some &quot;yuck!&quot; reaction to reading it, but there are places where it makes sense.
I think &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;yvonnezlam&quot;&gt;Yvonne Lam&lt;&#x2F;a&gt; really nailed it with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;yvonnezlam&#x2F;status&#x2F;1376631868972433408&quot;&gt;her concept of tech debt as housework&lt;&#x2F;a&gt;, specifically:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The thing with tech debt is that in order to have a useful discussion, you need to be able to talk about *who* is affected and how -- who is going to be woken up, be pulled off other work, be unable to perform some critical function, etc. You can&#x27;t hide risk and have that talk.&lt;&#x2F;p&gt;
&lt;p&gt;— &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;yvonnezlam&#x2F;status&#x2F;1376631868972433408&quot;&gt;Yvonne Lam on Twitter&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Let&#x27;s apply that: who is going to be affected by changing your standards, and how?
This will be different in different settings, so asking these questions with your team makes a lot of sense.&lt;&#x2F;p&gt;
&lt;p&gt;As an example, I care a lot about accessibility.
I don&#x27;t want to ship software that prevents people from using assistive technology like screen readers.
In my company&#x27;s case, our users are kids learning to write better.
I really care about not messing that up!&lt;&#x2F;p&gt;
&lt;p&gt;My team shares that value, so when we found out in a recent project that we&#x27;d have to choose between having more complicated view code versus shipping a program that was inaccessible to screen readers, we chose the complex code.
We know we&#x27;re going to have to clean up the code sometime, but for now we&#x27;re OK with maintaining some slightly complicated code to make sure we&#x27;re accommodating everyone&#x27;s needs.&lt;&#x2F;p&gt;
&lt;p&gt;Because we&#x27;d already had the conversation about what values we wanted our code to embody, we were able to make a choice that lined up with those values.
Even though the software wasn&#x27;t perfect, I think we made the right tradeoff.&lt;&#x2F;p&gt;
&lt;p&gt;Point is: talk with your team about this!
Every project, team, and person has a different set of values, so trying to nail down what &quot;works&quot; means can highlight differences in how we think about the things we&#x27;re building.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-the-change-easy&quot;&gt;Make The Change Easy&lt;&#x2F;h2&gt;
&lt;p&gt;Another way to apply this rule is to &quot;make the change easy, then make the easy change.&quot;
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;kentbeck&#x2F;status&#x2F;250733358307500032&quot;&gt;Coincidentally, another Kent Beck-ism&lt;&#x2F;a&gt;.)
In other words, when you go to your code to make a change, consider refactoring towards simpler code &lt;em&gt;first&lt;&#x2F;em&gt; instead of waiting until after you&#x27;re done.&lt;&#x2F;p&gt;
&lt;p&gt;It might seem like this is incompatible with the rule—after all, if you&#x27;re planning to make a change you already know the program doesn&#x27;t work according to the specification—but we often have to do some cleanup in order to make the change possible!
For the purposes of this rule, &quot;works&quot; can also mean &quot;is amenable to change!&quot;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;So, to sum up:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Simplify when the program works&quot; depends a lot on what you mean by &quot;works.&quot;
(It also depends on what you mean by &quot;simplify&quot;, but that&#x27;s what the other rules are for—coming soon!)&lt;&#x2F;li&gt;
&lt;li&gt;This rule can be helpful as a signal to switch from writing-code-to-make-the-program-work mode to clarifying-code-for-future-us mode.&lt;&#x2F;li&gt;
&lt;li&gt;Set a high bar, but no so high that you can&#x27;t ever ship.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;See you next week for rule 2: clarify your intent.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>My Take on the Four Rules of Simple Design</title>
		<published>2021-06-15T00:00:00+00:00</published>
		<updated>2021-06-15T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/my-take-on-the-four-rules-of-simple-design/" type="text/html"/>
		<id>https://bytes.zone/posts/my-take-on-the-four-rules-of-simple-design/</id>
		<content type="html">&lt;p&gt;I&#x27;ve been thinking about how to write better tests recently, and I happened on Kent Beck&#x27;s four rules for simple design.
In a nutshell, these rules were meant to give objective criteria for the question &quot;is this code simple?&quot;
The original formulation goes like this: a piece of code can be said to be simple if it...&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Runs all the tests.&lt;&#x2F;li&gt;
&lt;li&gt;Has no duplicated logic. Be wary of hidden duplication like parallel class hierarchies&lt;&#x2F;li&gt;
&lt;li&gt;States every intention important to the programmer.&lt;&#x2F;li&gt;
&lt;li&gt;Has the fewest possible classes and methods.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;— &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.google.com&#x2F;books&#x2F;edition&#x2F;Extreme_Programming_Explained&#x2F;G8EL4H4vf7UC?hl=en&amp;amp;gbpv=1&amp;amp;pg=PA57&amp;amp;printsec=frontcover&quot;&gt;Kent Beck, &lt;em&gt;Extreme Programming Explained: Embrace Change&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In practice, I really like that while the intention may have been to make an objective metric for simplicity, people tend to treat these rules as &lt;em&gt;imperfect but useful&lt;&#x2F;em&gt;.
That is, they give us a way to judge if a piece of code is better or worse than another one.
This turns out to be a helpful way of judging internal quality of a piece of software—and improving that is why we write tests and refactor code in the first place!&lt;&#x2F;p&gt;
&lt;p&gt;I think this usefulness has made these rules surprisingly uncontroversial (at least to me, based on how much I know we programmers like bikeshedding!)
The most controversial topic, in fact, seems to be which order to apply rules 2 and 3 in.&lt;&#x2F;p&gt;
&lt;p&gt;In reading about this, I collected a bunch of interpretations of the four rules from various authors:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Rule&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.google.com&#x2F;books&#x2F;edition&#x2F;Extreme_Programming_Explained&#x2F;G8EL4H4vf7UC?hl=en&amp;amp;gbpv=1&amp;amp;pg=PA57&amp;amp;printsec=frontcover&quot;&gt;Kent Beck&lt;&#x2F;a&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.martinfowler.com&#x2F;bliki&#x2F;BeckDesignRules.html&quot;&gt;Martin Fowler&lt;&#x2F;a&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;blog.jbrains.ca&#x2F;permalink&#x2F;the-four-elements-of-simple-design&quot;&gt;J.B. Rainsberger&lt;&#x2F;a&gt;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;&lt;a rel=&quot;external&quot; title=&quot;n.b. I switched rules 2 and 3 in Fowler&#x27;s and Haines&#x27; formulations for reading consistency.
Based on these author&#x27;s writings, I think it&#x27;s fine in this context.&quot; href=&quot;https:&#x2F;&#x2F;leanpub.com&#x2F;4rulesofsimpledesign&quot;&gt;Corey Haines&lt;&#x2F;a&gt;&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Runs all the tests.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Passes the tests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Passes its tests&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Tests Pass&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;2.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Has no duplicated logic. […]&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No duplication&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Minimizes Duplication&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No Duplication (DRY)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;3.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;States every intention important to the programmer.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Reveals intention&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Maximizes clarity&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Expresses Intent&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;4.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Has the fewest possible classes and methods.&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Fewest elements&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Has fewer elements&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Small&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;I have enjoyed applying these rules to my own code, but in the spirit of &quot;what I cannot create, I do not understand,&quot; I thought it&#x27;d be fun to try and make my own formulation.
In doing this, I&#x27;d like to avoid simply paraphrasing the four rules—I want to be able to explain what makes code simple to someone who has never heard of this concept.
That means being able to make a reasonable argument for each point, as well as talking about the consequences of leaving them off.&lt;&#x2F;p&gt;
&lt;p&gt;I originally thought that&#x27;d be one post, but then I did a bunch of research to make sure I wasn&#x27;t just making stuff up and it turned out super long.
So here&#x27;s what we&#x27;re going to do: over the next month, I&#x27;ll be publishing one post per week about a new rule.
When I do that, I&#x27;ll come back to this post and link them up.
If you&#x27;re here after July 2021, welcome!
Get started by clicking the links below.
Otherwise, you can sign up to be notified about new posts by putting your email in the box at the bottom of the post.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-1-simplify-when-the-program-works&#x2F;&quot;&gt;Simplify When the Program Works&lt;&#x2F;a&gt; (June 22)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-2-clarify-your-intent&#x2F;&quot;&gt;Clarify Your Intent&lt;&#x2F;a&gt; (June 29)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-3-centralize-behavior&#x2F;&quot;&gt;Centralize Behavior&lt;&#x2F;a&gt; (July 6)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;rule-4-code-for-now&#x2F;&quot;&gt;Code for Now&lt;&#x2F;a&gt; (July 13)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Afterwards, I&#x27;ll probably publish a conclusion including reader feedback in late July.
I&#x27;d love to hear from you as you&#x27;re reading these things; you can email me at &lt;a href=&quot;mailto:brian@brianthicks.com&quot;&gt;brian@brianthicks.com&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Trying (and Failing) to Speed Up String.startsWith</title>
		<published>2021-03-01T00:00:00+00:00</published>
		<updated>2021-03-01T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/trying-and-failing-to-speed-up-string-beginswith/" type="text/html"/>
		<id>https://bytes.zone/posts/trying-and-failing-to-speed-up-string-beginswith/</id>
		<content type="html">&lt;p&gt;When I was optimizing &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;elm-csv-package-and-talk&#x2F;&quot;&gt;elm-csv&lt;&#x2F;a&gt;, I noticed that Elm&#x27;s &lt;code&gt;String.startsWith&lt;&#x2F;code&gt; is implemented using JavaScript&#x27;s &lt;code&gt;indexOf&lt;&#x2F;code&gt; method.
It looks something like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; startsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;haystack&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt; needle&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; haystack.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(needle)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This strikes me as inefficient!
Doesn&#x27;t that mean that if &lt;code&gt;haystack&lt;&#x2F;code&gt; doesn&#x27;t begin with &lt;code&gt;needle&lt;&#x2F;code&gt;, it&#x27;ll still look through the whole string?
That seems to waste a lot of cycles on work after we already know the result.&lt;&#x2F;p&gt;
&lt;p&gt;After thinking about it for a little bit, I imagined a faster way:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; startsWith&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;haystack&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt; needle&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; haystack.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, needle.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span&gt; needle;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But, I didn&#x27;t know for sure if it would be faster, so I started measuring things.
After a few benchmarks things were looking promising!
I found that &lt;code&gt;String.prototype.slice&lt;&#x2F;code&gt; and &lt;code&gt;String.prototype.length&lt;&#x2F;code&gt; operate in more-or-less constant time, and that &lt;code&gt;===&lt;&#x2F;code&gt;, while not constant-time, is heavily optimized.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Hypothetically&lt;&#x2F;em&gt;, I thought, this means that a &lt;code&gt;slice&lt;&#x2F;code&gt;-based implementation of &lt;code&gt;startsWith&lt;&#x2F;code&gt; could run much quicker than scanning through the whole string with &lt;code&gt;indexOf&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;chrome-uses-actual-magic-in-their-strings&quot;&gt;Chrome Uses Actual Magic in Their Strings&lt;&#x2F;h2&gt;
&lt;p&gt;But just to make sure my intuition about &lt;code&gt;indexOf&lt;&#x2F;code&gt; being slow was correct, I decided to benchmark that as well.
Specifically, I decided to benchmark how long browsers took to find a single character in a string that doesn&#x27;t contain the character:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;abc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(size).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;d&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I expected that every time I multiplied &lt;code&gt;size&lt;&#x2F;code&gt; by 10, I should see roughly a 10x slowdown in operations per second.
But then…&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1 &quot;abc&quot;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;10 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;100 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1,000 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Chrome 88.0.4324.182&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;866,026,834&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;869,256,882&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;858,563,862&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;865,370,315&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Firefox 85.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;43,557,275&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;44,947,174&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;41,059,919 (-8.65%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;16,831,928 (-59.01%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Safari 14.0.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;49,240,581&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;34,661,639 (-29.61%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;7,162,615 (-79.01%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;877,449 (-87.75%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Chrome has… NO REAL SLOWDOWN?
WHAT?
This data suggests that Chrome can find a substring in any length string in constant time.
How is that even possible?&lt;&#x2F;p&gt;
&lt;p&gt;At this point, I felt pretty out of my depth, and decided I needed to ask my friend &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lukewestby&quot;&gt;Luke Westby, Real-Life Computer Genius™&lt;&#x2F;a&gt;, what was going on.
He knew right where to look and figured out that &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;v8&#x2F;v8&#x2F;blob&#x2F;dc712da548c7fb433caed56af9a021d964952728&#x2F;src&#x2F;strings&#x2F;string-search.h#L194&quot;&gt;Chrome has a special optimization for single-character search strings in &lt;code&gt;indexOf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
In other words, people have hand-tuned V8 over the years to go ridiculously fast in all sorts of situations, and this is one of them.&lt;&#x2F;p&gt;
&lt;p&gt;Well, OK, let&#x27;s try finding some multi-character string instead.
I used &quot;nope&quot;, which doesn&#x27;t trigger this optimization, but still fails to match &quot;abc&quot;.
Does that give us a result that matches my intuition?&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1 &quot;abc&quot;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;10 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;100 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1,000 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Chrome 88.0.4324.182&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;885,211,831&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;863,845,523 (-2.41%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;849,821,335 (-1.62%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;837,391,727 (-1.46%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Firefox 85.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;58,648,716&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;44,107,472 (-24.79%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;40,529,513 (-8.11%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;16,768,019 (-58.63%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Safari 14.0.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;102,071,445&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;26,242,114 (-74.29%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4,212,339 (-83.95%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;467,329 (-88.91%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;That&#x27;s &lt;em&gt;kinda&lt;&#x2F;em&gt; like what I expected, but Chrome is still incredibly fast at this!
Well, OK, that&#x27;s fine.
Good for them.
But at this point I got curious (and a little devious): how can I defeat Chrome&#x27;s optimizations?
What is the worst-case scenario that they can&#x27;t possibly have handled already?&lt;&#x2F;p&gt;
&lt;p&gt;I tried to imagine the algorithm that you&#x27;d have to write to implement &lt;code&gt;indexOf&lt;&#x2F;code&gt; and came up with something like this:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Look at the first character in &lt;code&gt;haystack&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;If it matches the first character in &lt;code&gt;needle&lt;&#x2F;code&gt;, look at the next pairs of characters in the two strings one by one.&lt;&#x2F;li&gt;
&lt;li&gt;If you get to the end of &lt;code&gt;needle&lt;&#x2F;code&gt; and they all match, you&#x27;ve found the index.&lt;&#x2F;li&gt;
&lt;li&gt;But if one pair doesn&#x27;t match, you&#x27;ve got to start over at the next character in &lt;code&gt;haystack&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;With that in mind, what&#x27;s combination of &lt;code&gt;needle&lt;&#x2F;code&gt; and &lt;code&gt;haystack&lt;&#x2F;code&gt; would perform the worst?
I think it&#x27;s any pair of strings where &lt;code&gt;haystack&lt;&#x2F;code&gt; is all but the last character of &lt;code&gt;needle&lt;&#x2F;code&gt; repeated over and over.
So, maybe a benchmark like this?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;abc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(size).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;abcd&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That can never succeed, but it&#x27;ll make the browser do a lot of extra work reading the initially-matching &quot;abc&quot; over and over.
Let&#x27;s see the results:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1 &quot;abc&quot;&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;10 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;100 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;1,000 &quot;abc&quot;s&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Chrome 88.0.4324.182&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;877,221,230&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;863,929,216 (-1.52%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;688,398,676 (-20.32%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;125,023 (-99.98%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Firefox 85.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;61,384,762&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;12,874,505 (-79.03%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,366,202 (-89.39%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;137,245 (-89.95%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Safari 14.0.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;100,546,438&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;25,948,095 (-74.19%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4,170,375 (-83.93%)&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;467,753 (-88.78%)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Finally, Chrome has similar performance to other browsers.
I don&#x27;t think this is representative of real workloads, but even so Chrome performs admirably.
Hats off to the V8 team for this implementation, but honestly, it&#x27;s good to know they&#x27;re still mortal!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;comparing-the-implementations&quot;&gt;Comparing the Implementations&lt;&#x2F;h2&gt;
&lt;p&gt;So now that we know more about the component pieces involved in this optimization (for example, knowing to avoid single-character test strings), we can check if &lt;code&gt;slice&lt;&#x2F;code&gt; actually gives us a real-world speedup.
In setting this up, I decided to go for the middle of the road to try and benchmark the browsers under something that you can kind of squint at and think &quot;yeah, that will probably happen reasonably often in real life.&quot;
In this case, that meant setup looked like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;haystack&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;abc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;needle&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;abc&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then the &lt;code&gt;indexOf&lt;&#x2F;code&gt; and &lt;code&gt;slice&lt;&#x2F;code&gt; benchmarks looked like the code at the top of this post:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;haystack.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;indexOf&lt;&#x2F;span&gt;&lt;span&gt;(needle)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;haystack.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;, needle.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span&gt; needle;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That gave me these results:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;indexOf&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;slice&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Diff&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Chrome 88.0.4324.192&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;871,312,454&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;870,810,291&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-0.06%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Firefox 86.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;32,428,394&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;46,845,953&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;+44.46%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Safari 14.0.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;430,854,455&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,352,674,261&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;+213.95%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Note that Chrome and Firefox had released new versions between me testing &lt;code&gt;indexOf&lt;&#x2F;code&gt; and this test. I kept the old data here because it doesn&#x27;t seem to have changed the performance, but I just want to be up front that the versions are different!&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, that said: looks like a result within the margin of error for Chrome (around 0.4%), but a reasonable speedup for Firefox and a big win for Safari.&lt;&#x2F;p&gt;
&lt;p&gt;What does the failure case look like? (Same &lt;code&gt;haystack&lt;&#x2F;code&gt;, but &lt;code&gt;needle = &quot;nope&quot;&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Browser&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;indexOf&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;slice&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;% Diff&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Chrome 88.0.4324.192&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;874,951,212&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;854,116,367&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-2.44%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Firefox 85.0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;31,026,729&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;22,180,114&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;-28.51%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Safari 14.0.1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;4,189,768&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;1,508,770,435&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;+35,910.83%&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;This time, Chrome has a slowdown greater than the measurement error, which surprised me!
Same with Firefox, but to a larger degree.
But the real story here is Safari, which can now do this operation over 1.5 billion times per second.
&lt;em&gt;Billion!&lt;&#x2F;em&gt;
What even!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;is-it-worth-it&quot;&gt;Is It Worth It?&lt;&#x2F;h2&gt;
&lt;p&gt;So, with all that measurement done, is this optimization worth it?
I&#x27;m actually not really sure.
This is basically nothing in Chrome but means making some real performance tradeoffs in Firefox.
And, even though it looks like a huge win in Safari, my claims can only be as strong as the underlying assumptions I&#x27;m making in choosing test data.
So, just to be explicit about that:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;These benchmarks assume that &lt;code&gt;haystack&lt;&#x2F;code&gt; is a long-ish string (300 characters.)
Many of the gains we see here are erased when the strings are short.
Safari in particular, drops to only about a 100% gain in the failure case with a shorter string (I tested at 3, 30, 150 characters, all with similar results.)&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;needle&lt;&#x2F;code&gt; strings are neither a single character nor a worst-case scenario for &lt;code&gt;indexOf&lt;&#x2F;code&gt; performance.&lt;&#x2F;li&gt;
&lt;li&gt;Success and failure cases are equally likely.
We can&#x27;t make any assumption other than that at the runtime level, although we could make a good argument to switch this for individual applications.
For example, we could choose to use &lt;code&gt;slice&lt;&#x2F;code&gt; in an application where we expected most checks on &lt;code&gt;startsWith&lt;&#x2F;code&gt; would succeed, since that&#x27;s basically a wash in Chrome and much faster in Firefox and Safari.&lt;&#x2F;li&gt;
&lt;li&gt;We care about browsers based on global total share instead of splitting out into desktop or mobile.
This is another area where you could make different decisions at the application level: if the users of your app skew more towards using Chrome or more towards Safari, this payoff for this optimization gets better or worse.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Of course, if you can measure that your application &lt;em&gt;would&lt;&#x2F;em&gt; benefit from this optimization, go ahead and do it yourself!
Calling &lt;code&gt;String.slice 0 (String.length haystack) === needle&lt;&#x2F;code&gt; has some overhead in Elm (due to equality having different semantics) but you may still able to get the majority of the benefits if your users are mostly using Safari or you&#x27;re running iOS webkit views or something like that.
Just make sure to actually measure it!
Optimizations at this level can be helpful, but they&#x27;re more often dwarfed by DOM stuff in the runtime.&lt;&#x2F;p&gt;
&lt;p&gt;As for me: at this point, I&#x27;m not going to make a PR to &lt;code&gt;elm&#x2F;core&lt;&#x2F;code&gt; with this improvement, even though I intended to when I set out.
However, I found it interesting to look into these things, and I learned a lot about how strings are optimized in various browsers, so I&#x27;m gonna call it a win overall.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>nix-script</title>
		<published>2021-02-23T00:00:00+00:00</published>
		<updated>2021-02-23T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/nix-script/" type="text/html"/>
		<id>https://bytes.zone/posts/nix-script/</id>
		<content type="html">&lt;p&gt;I like writing quick little scripts to avoid having to remember how to do things.
Most of the time I start in bash, thinking the task won&#x27;t be too complicated... but then before I know it I&#x27;m having to reread the bash man pages to figure out how arrays work for the thousandth time.&lt;&#x2F;p&gt;
&lt;p&gt;So really, I&#x27;d rather write scripts in a language that offers some more safety and better data structures.
We&#x27;re trying to learn Haskell at work, so maybe that?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s see a hello world:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;haskell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;usr&#x2F;bin&#x2F;env runghc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; :: IO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; putStrLn &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That seems pretty reasonable, and only takes like 300ms to compile and run.
Not awful, but it&#x27;s not as fast as a bash script, and it means I have to use Haskell&#x27;s standard prelude instead of something safer like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kowainik.github.io&#x2F;projects&#x2F;relude&quot;&gt;relude&lt;&#x2F;a&gt; or &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;hackage.haskell.org&#x2F;package&#x2F;nri-prelude&quot;&gt;nri-prelude&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s reasonable to solve this, though: I can just use &lt;code&gt;nix-shell&lt;&#x2F;code&gt; to get a &lt;code&gt;ghc&lt;&#x2F;code&gt; with packages.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;haskell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;usr&#x2F;bin&#x2F;env nix-shell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!nix-shell -p &amp;quot;(pkgs.haskellPackages.ghcWithPackages (ps: [ ps.text ]))&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!nix-shell -i runghc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{-#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; LANGUAGE OverloadedStrings&lt;&#x2F;span&gt;&lt;span&gt; #-}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; :: IO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO.&lt;&#x2F;span&gt;&lt;span&gt;putStrLn &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Well, that works, but I&#x27;ve traded speed for flexibility: run time has grown to over 2 seconds!
Eep!&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t think I should have to make this trade, so I wrote &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;nix-script&quot;&gt;&lt;code&gt;nix-script&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.
It transparently manages a compilation cache for these kinds of scripts, and lets you specify dependencies and build commands inside your source file!&lt;&#x2F;p&gt;
&lt;p&gt;That means the &lt;code&gt;nix-shell&lt;&#x2F;code&gt; example above can be rewritten like so:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;haskell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;usr&#x2F;bin&#x2F;env nix-script&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!buildInputs (pkgs.haskellPackages.ghcWithPackages (ps: [ ps.text ]))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!build ghc -O -o $OUT_FILE $SCRIPT_FILE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{-#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; LANGUAGE OverloadedStrings&lt;&#x2F;span&gt;&lt;span&gt; #-}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; :: IO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO.&lt;&#x2F;span&gt;&lt;span&gt;putStrLn &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The first time you run that, it&#x27;ll compile the script to a binary, then run it.
That takes about two seconds on my machine.
The second time, and going forward until you change the script, it detects that it already compiled to a binary, so it just runs the compiled version.
That takes 30ms or so for me!
Big improvement!&lt;&#x2F;p&gt;
&lt;p&gt;But it&#x27;s really not that fun to have to figure out that &lt;code&gt;#!build&lt;&#x2F;code&gt; line every time, and I always forget how to call &lt;code&gt;pkgs.haskellPackages.ghcWithPackages&lt;&#x2F;code&gt; correctly... so there&#x27;s also a wrapper script called &lt;code&gt;nix-script-haskell&lt;&#x2F;code&gt; that makes this nicer:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;haskell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;usr&#x2F;bin&#x2F;env nix-script-haskell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!haskellPackages text&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{-#&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; LANGUAGE OverloadedStrings&lt;&#x2F;span&gt;&lt;span&gt; #-}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; :: IO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; Data.Text.IO.&lt;&#x2F;span&gt;&lt;span&gt;putStrLn &lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;Hello, World!&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And, in addition to the speed boost, we can depend on any package in the Nix ecosystem!
For example, here&#x27;s how you&#x27;d add and call &lt;code&gt;jq&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;haskell&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!&#x2F;usr&#x2F;bin&#x2F;env nix-script-haskell&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#!runtimeInputs jq&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; System.Process&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; :: IO&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; ()&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;= do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  formatted &lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;&amp;lt;-&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    readProcess&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;jq&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      [&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;--color-output&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;.&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;      &amp;quot;{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;Atlas&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;species&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;kitty cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;\&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  putStr formatted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It&#x27;s also pretty easy to create more wrapping interpreters, so we also ship one for bash.
Even though it&#x27;s not a compiled language, we can cache the nix environment with the exact dependencies the script needs!&lt;&#x2F;p&gt;
&lt;p&gt;You can get this at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;nix-script&quot;&gt;github.com&#x2F;BrianHicks&#x2F;nix-script&lt;&#x2F;a&gt;.
There are installation instructions in the README, both for standalone use and use within a larger Nix project.&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy, and let me know if—and how—you use this!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>elm-csv, package and talk</title>
		<published>2021-02-15T00:00:00+00:00</published>
		<updated>2021-02-15T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/elm-csv-package-and-talk/" type="text/html"/>
		<id>https://bytes.zone/posts/elm-csv-package-and-talk/</id>
		<content type="html">&lt;p&gt;In the beginning of February, I spent a lot of time working on a new library for parsing CSV data in Elm.
(Basically, the existing libraries did not have &lt;code&gt;andThen&lt;&#x2F;code&gt; and I needed it!)&lt;&#x2F;p&gt;
&lt;p&gt;The resulting library turned out quite nice, I think, and in addition it&#x27;s very fast!
I gave talk to the Elm London user group about it, which you can see at &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;talks&#x2F;elm-csv-be-as-boring-and-as-fast-as-possible&#x2F;&quot;&gt;elm-csv: be as boring—and as fast—as possible&lt;&#x2F;a&gt; or below.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;d just like to see the code, it lives at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;BrianHicks&#x2F;elm-csv&quot;&gt;github.com&#x2F;BrianHicks&#x2F;elm-csv&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;figure class=&quot;youtube-embed&quot;&gt;
  &lt;div&gt;
    &lt;iframe src=&quot;https:&#x2F;&#x2F;www.youtube-nocookie.com&#x2F;embed&#x2F;_z6V1143TDE&quot; title=&quot;elm-csv: be as boring—and as fast—as possible&quot; webkitallowfullscreen mozallowfullscreen allowfullscreen frameborder=0&gt;&lt;&#x2F;iframe&gt;
  &lt;&#x2F;div&gt;
&lt;&#x2F;figure&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Should Elm files be long or short?</title>
		<published>2021-01-11T00:00:00+00:00</published>
		<updated>2021-01-11T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/should-elm-files-be-long-or-short/" type="text/html"/>
		<id>https://bytes.zone/posts/should-elm-files-be-long-or-short/</id>
		<content type="html">&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;webapps&#x2F;structure.html&quot;&gt;the Elm Guide page on app Structure&lt;&#x2F;a&gt; says:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;[Don&#x27;t] Prefer shorter files.
In JavaScript, the longer your file is, the more likely you have some sneaky mutation that will cause a really difficult bug.
But in Elm, that is not possible!
Your file can be 2000 lines long and that still cannot happen.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Someone &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;discourse.elm-lang.org&#x2F;t&#x2F;should-i-prefer-big-elm-files&#x2F;6687&quot;&gt;asked about this on the Elm Discourse&lt;&#x2F;a&gt; recently, wondering if this advice meant they should prefer longer Elm files over shorter ones.
I thought this was a really interesting question!
I answered on Discourse, but I also want to clean up my answer to share a little wider.
Here goes!&lt;&#x2F;p&gt;
&lt;p&gt;So, personally, I don&#x27;t really agree with that part of the docs.
I&#x27;ve worked in codebases with short files and longer files, and it didn&#x27;t seem like either style was more or less prone to mutation bugs.
That makes me think that &lt;strong&gt;file length is not the problem, mutation is.&lt;&#x2F;strong&gt;
And mutation is not a problem in Elm—it&#x27;s just not allowed in the language at all, regardless of file length!&lt;&#x2F;p&gt;
&lt;p&gt;What does that mean for how we write Elm?
Should Elm files be longer or shorter than JavaScript files?
Well, neither really!
If we accept that file length is not strongly correlated with this kind of bug, it doesn&#x27;t matter!
Elm files &lt;strong&gt;may&lt;&#x2F;strong&gt; be longer as a consequence of the things I&#x27;m gonna say below, but that&#x27;s a consequence only—not a goal!&lt;&#x2F;p&gt;
&lt;p&gt;Now, with that out of the way, we can get to the more interesting question: what &lt;em&gt;is&lt;&#x2F;em&gt; the right file length?
I think to answer that we need to figure out what a module is actually good for.
In Elm, I use modules for two reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;encapsulation:&lt;&#x2F;strong&gt; hiding or exposing bits of the implementation to make a cohesive API.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;namespacing:&lt;&#x2F;strong&gt; defining things like &lt;code&gt;toString&lt;&#x2F;code&gt; under different name spaces.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The second is &lt;em&gt;kiiiinda&lt;&#x2F;em&gt; a consequence of the first, so I think we can squint and ignore it.
So for our purposes, I&#x27;m asserting that files are for encapsulation.
That means &lt;strong&gt;the right file length is the one where your encapsulation works to prevent bugs&lt;&#x2F;strong&gt;, either by preventing encapsulation-violating behavior or making your code easy enough to understand that you can verify it&#x27;s doing the right thing.
That doesn&#x27;t mean long files or short files, but correct-for-the-situation files.&lt;&#x2F;p&gt;
&lt;p&gt;I realize that this basically boils down to &quot;it depends&quot;, which I know is a bit unsatisfying.
Sorry!
Maybe it will help if I share some assorted things I use to know if a module needs to be broken up (or not):&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Yes, split if:&lt;&#x2F;strong&gt; there&#x27;s a function that should not be able to access internals of a data structure, but which can because it lives in the same module.
Of course, the opposite is true too... if there&#x27;s a function that needs to be able to access internals but &lt;em&gt;can&#x27;t&lt;&#x2F;em&gt; the module boundary may be in the wrong place!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Yes, split if:&lt;&#x2F;strong&gt; there are two independent data structures in the same module.
One smell here is if I have to write &lt;code&gt;fooToString&lt;&#x2F;code&gt; and &lt;code&gt;barToString&lt;&#x2F;code&gt; in the same module, and &lt;code&gt;foo&lt;&#x2F;code&gt; and &lt;code&gt;bar&lt;&#x2F;code&gt; are independently valuable, it may be time to give at least one a new home.
The opposite (&quot;join if…&quot;) looks the same as the previous item: if you split a single data structure into two modules, you&#x27;ll frequently need to share too many internals and so should move everything into one file.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;No, don&#x27;t split if:&lt;&#x2F;strong&gt; there is just &quot;too much code&quot; in a file.
I&#x27;ve split apps into &lt;code&gt;Model.elm&lt;&#x2F;code&gt;, &lt;code&gt;Update.elm&lt;&#x2F;code&gt;, &lt;code&gt;View.elm&lt;&#x2F;code&gt; and almost always regretted it.
Even if it&#x27;s a little tricky to navigate a long file, it&#x27;s way better than having to perform module gymnastics to prevent import loops.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All that said: like the rest of that page in the guide, I would encourage people to write long files and then break them up instead of making tiny files before it&#x27;s actually necessary.
I&#x27;ve been using Elm for something like half a decade now and the only reliable way I&#x27;ve found to put module boundaries in the right places is to wait and see what the right place &lt;em&gt;is&lt;&#x2F;em&gt;.
I suppose that means I am, in effect, advocating for long files over short ones but again: that&#x27;s a consequence, not a goal!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Learning Requires Effort</title>
		<published>2021-01-05T00:00:00+00:00</published>
		<updated>2021-01-05T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/learning-requires-effort/" type="text/html"/>
		<id>https://bytes.zone/posts/learning-requires-effort/</id>
		<content type="html">&lt;p&gt;I recently finished &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;takesmartnotes.com&quot;&gt;&lt;em&gt;How to Take Smart Notes&lt;&#x2F;em&gt;&lt;&#x2F;a&gt;.
The core idea I took away from the book is that &lt;em&gt;learning requires effort&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;two-kinds-of-effort&quot;&gt;Two Kinds of Effort&lt;&#x2F;h2&gt;
&lt;p&gt;I think people generally recognize that learning requires effort, but have an easier time applying that to physical activities than mental ones.
You don&#x27;t really go around assuming that Olympic athletes are just people who jumped out of bed the morning of the games and decided to win gold in ice dancing.
Instead, we recognize that athletes train hard for many years in order to reach the top of the field.&lt;&#x2F;p&gt;
&lt;p&gt;But when it comes to learning in more abstract ways, it&#x27;s somehow easier to forget that effort is necessary!
For example, I&#x27;ve made the mistake many times of assuming I learned something just because I reached the last page of a book.&lt;&#x2F;p&gt;
&lt;p&gt;With that in mind, I can think of at least two ways of putting in the effort to learn.
There are definitely more, though!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;How to Take Smart Notes&lt;&#x2F;em&gt; recommends using the Zettelkasten (slip-box) system &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;zettelkasten.de&#x2F;posts&#x2F;overview&#x2F;&quot;&gt;covered&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.fastcompany.com&#x2F;90535318&#x2F;this-simple-but-powerful-analog-method-will-rocket-your-productivity&quot;&gt;many&lt;&#x2F;a&gt; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;writingcooperative.com&#x2F;zettelkasten-how-one-german-scholar-was-so-freakishly-productive-997e4e0ca125&quot;&gt;places&lt;&#x2F;a&gt; this past summer.
In this method, &quot;effort&quot; looks like making notes by rephrasing and making connections to things you already know.
Educationally speaking, this is called elaboration.&lt;&#x2F;p&gt;
&lt;p&gt;Outside of note-taking, I see this in how programmers learn new tools and techniques.
When someone says they want to learn something, a common response goes &quot;well, make a small but useful project and see.&quot;
I&#x27;ve told people that many times myself without recognizing that I&#x27;m telling people to learn through effort.&lt;&#x2F;p&gt;
&lt;p&gt;These examples make &quot;learning requires effort&quot; seem pretty general to me, since it applies both in places I expected and places I didn&#x27;t.
That has the feel of a useful principle which I can use to judge if I&#x27;m learning or not!
Am I putting in effort?
If not, I&#x27;m probably not learning!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;someone-else-can-t-learn-for-you&quot;&gt;Someone Else Can&#x27;t Learn For You&lt;&#x2F;h2&gt;
&lt;p&gt;&quot;Learning requires effort&quot; also means that someone else can&#x27;t learn for you.
Think about it: if learning requires effort, and someone else is putting in the effort, then it&#x27;s not &lt;em&gt;you&lt;&#x2F;em&gt; doing the learning.
That&#x27;s another mistake I&#x27;ve made many times over the years!
It&#x27;s natural for me to try and find the perfect resource when what I actually need to just get into the work of effortful learning as quickly as I can.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve seen this show up in several ways.
The first is conflating building a collection with learning.
Where&#x27;s the learning effort in just collecting information from other people, whether papers, conference talks, or blog posts?
If you&#x27;re not putting in the effort to connect new stuff to what you already know, you&#x27;re just playing gotta-catch-em-all with PDFs!&lt;&#x2F;p&gt;
&lt;p&gt;The second is similar, but a little more specific: using other people&#x27;s flashcards.
I&#x27;ve seen quite a few places where you can download flashcard decks for various computer science concepts.
At first glace, that seems fine, but where&#x27;s the learning there?
It&#x27;s certainly a lot of &lt;em&gt;effort&lt;&#x2F;em&gt; to memorize a flashcard deck by rote, but it&#x27;s the kind of effort that makes you learn the deck, not the subject.
Making flashcards, on the other hand, can actually create connections as you think about how best to structure the subject in flashcard form.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;put-it-all-together&quot;&gt;Put It All Together&lt;&#x2F;h2&gt;
&lt;p&gt;So, if learning requires effort, how can we learn?
Simple: just put in the effort.&lt;&#x2F;p&gt;
&lt;p&gt;But, oops, it&#x27;s actually not so simple: &lt;em&gt;what&lt;&#x2F;em&gt; effort?
What&#x27;s right for you and for what you&#x27;re learning?
If you just thrash and flail, by trying things that are way beyond your current skill or level of understanding, you won&#x27;t actually learn much.
To be effective, the things you&#x27;re trying to learn have to be just beyond what you can do already (in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Zone_of_proximal_development&quot;&gt;zone of proximal development&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;You also have to make sure you&#x27;re learning in a way that you can actually apply your new knowledge.
That means working towards some level or standard of mastery, ideally one against which you can measure your progress.
(For example, being able to clear the bar at a certain level in the high jump.)&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s what a good teacher, mentor, or coach is for: someone who has helped other people learn the thing you&#x27;re trying to learn can give you good advice on the best places to apply your effort as well as a standard of mastery to grow towards.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Tracking and Lasting</title>
		<published>2020-12-29T00:00:00+00:00</published>
		<updated>2020-12-29T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/tracking-and-lasting/" type="text/html"/>
		<id>https://bytes.zone/posts/tracking-and-lasting/</id>
		<content type="html">&lt;p&gt;Just a short post to note that I&#x27;ve added a &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;colophon&#x2F;&quot;&gt;colophon&lt;&#x2F;a&gt; to this site.
It goes over how to get the source of this site (git), what I track (not much, and no third-party stuff), and how the page is designed to last.&lt;&#x2F;p&gt;
&lt;p&gt;I admit it&#x27;s pretty self-indulgent, but I wanted to be public about some of the things I&#x27;ve been thinking about while building this site.
Please feel free to &lt;a href=&quot;mailto:brian@brianthicks.com&quot;&gt;email me&lt;&#x2F;a&gt; any questions or comments about it!
I&#x27;d love to make it as clear as possible.&lt;&#x2F;p&gt;
&lt;p&gt;To repeat the link, that&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;colophon&#x2F;&quot;&gt;the colophon page&lt;&#x2F;a&gt;, and linked to from the footer.
Check it out!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>time-to-horse</title>
		<published>2020-12-22T00:00:00+00:00</published>
		<updated>2020-12-22T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/time-to-horse/" type="text/html"/>
		<id>https://bytes.zone/posts/time-to-horse/</id>
		<content type="html">&lt;p&gt;Some colleagues of mine at NoRedInk coined a new term: time-to-horse, the time it takes you to get on your horse and ride.&lt;&#x2F;p&gt;
&lt;p&gt;They used this in the context of their team getting familiar with a new area of our code, but I think it&#x27;s also useful for talking about how long it takes someone to make meaningful contributions when joining a team.&lt;&#x2F;p&gt;
&lt;p&gt;I think you probably want your time-to-horse to be as low as possible in either case.
There are a bunch of things that affect this (like documentation, pairing and collaboration practices, and continuous integration pipeline quality) but I think they all boil down to &quot;do you have fast feedback loops?&quot;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Phantom ID Types</title>
		<published>2020-12-14T00:00:00+00:00</published>
		<updated>2020-12-14T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/reusable-phantom-id-types/" type="text/html"/>
		<id>https://bytes.zone/posts/reusable-phantom-id-types/</id>
		<content type="html">&lt;p&gt;I previously wrote about &lt;a href=&quot;https:&#x2F;&#x2F;bytes.zone&#x2F;posts&#x2F;custom-id-types&#x2F;&quot;&gt;tradeoffs of custom ID types in Elm&lt;&#x2F;a&gt;, and promised to explore a few points in the space.
This is one!&lt;&#x2F;p&gt;
&lt;p&gt;Recently I built a little game for my son to learn his letters and decided to see if I could find a nicer way to solve id-as-string problem.
My goal was to find something in between the wild wild west of using &lt;code&gt;String&lt;&#x2F;code&gt;s directly and the repetition of defining ID types for every resource.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I found something new to me.
Maybe it&#x27;ll be useful to you, too!
The gist: &lt;strong&gt;you can make a reusable ID type by using phantom types&lt;&#x2F;strong&gt; (that is, types with a variable that appears in the definition but not any of the constructors.)&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s define a module for our IDs:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span&gt;(..),&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; decoder&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sorter&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; as&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Sorter&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt; thing&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Decoder&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt; thing&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;map&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;string&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;sorter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Sorter&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt; thing&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;sorter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;by (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;\&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span&gt; id)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; id)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;alphabetical&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;You use it in external definitions like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Shelter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Shelter&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Cat&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type alias Shelter =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; adoptableCats&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : List&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id Cat&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Decoder Shelter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;map2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Shelter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;string)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;adoptableCats&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;list&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;decoder))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Of course, this improvement still doesn&#x27;t come for free.
&lt;code&gt;Id thing&lt;&#x2F;code&gt; is still not &lt;code&gt;comparable&lt;&#x2F;code&gt; so you need a workaround if you want to use it in a &lt;code&gt;Dict&lt;&#x2F;code&gt; or &lt;code&gt;Set&lt;&#x2F;code&gt; (&lt;code&gt;sorter&lt;&#x2F;code&gt; above.)&lt;&#x2F;p&gt;
&lt;p&gt;You also lose some of the assurance that you&#x27;re not constructing or matching on &lt;code&gt;Id&lt;&#x2F;code&gt; in places where you shouldn&#x27;t be.
To put it another way, you can make a bad ID pretty easily: just call &lt;code&gt;Id &quot;a hot dog is a salad&quot;&lt;&#x2F;code&gt;.
Because the type is not used in the constructor, that&#x27;s a valid &lt;em&gt;whatever&lt;&#x2F;em&gt;... &lt;code&gt;Id Cat&lt;&#x2F;code&gt;, &lt;code&gt;Id Dog&lt;&#x2F;code&gt;, it&#x27;s all the same to the constructor.&lt;&#x2F;p&gt;
&lt;p&gt;You also can&#x27;t embed the ID of a record in the record itself (doing so would be a recursive definition.)
I managed to get around needing this in my alphabet game (records do not need to refer to themselves because whenever they&#x27;re displayed there&#x27;s another piece of data containing the ID.)
If you need it, you just do a little type trick:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Json&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;{-| An module-internal identifier. Has to be a custom type instead of an&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;alias so the compiler can detect if it&amp;#39;s being used improperly.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;-}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type alias Id =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.Id CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type alias Cat =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; purriness&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Decoder Cat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;decoder&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;map3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;id&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;decoder)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;name&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;string)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;field&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;purriness&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Decode&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;int)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;All the instances of &lt;code&gt;CatId&lt;&#x2F;code&gt; should be erased during compilation.
That is, they shouldn&#x27;t end up adding any weight to your compiled JavaScript!&lt;&#x2F;p&gt;
&lt;p&gt;And there you have it!
To sum up: with this technique, you get the normal stuff you get by using custom types as IDs, like:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;nice type-checking and good errors (with really obvious fixes, like &quot;you have a &lt;code&gt;Id Dog&lt;&#x2F;code&gt; but you need an &lt;code&gt;Id Cat&lt;&#x2F;code&gt;.&quot;)&lt;&#x2F;li&gt;
&lt;li&gt;reasonable levels of code reuse&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But with these tradeoffs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You have to deal with &lt;code&gt;comparable&lt;&#x2F;code&gt; not being extendable by custom types (which is true without a phantom type, too.)&lt;&#x2F;li&gt;
&lt;li&gt;You have to be disciplined about not deconstructing&#x2F;matching against IDs in places where it wouldn&#x27;t make sense to take responsibility for constructing them.&lt;&#x2F;li&gt;
&lt;li&gt;To get IDs in the records themselves (as opposed to just in &lt;code&gt;Dict&lt;&#x2F;code&gt;s or whatever) you have to do a type trick.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So would I do this again?
I think I would avoid using this in a project where I didn&#x27;t have a high confidence that my fellow programmers knew the intent of the module.
That means that I wouldn&#x27;t to publish a package using this, or contribute code to a high-traffic open source repo using this pattern.
But on my work team in private code, or in small apps that I build for myself, I&#x27;ll definitely be coming back to this!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Bang Shortcuts</title>
		<published>2020-12-08T00:00:00+00:00</published>
		<updated>2020-12-08T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/bang-shortcuts/" type="text/html"/>
		<id>https://bytes.zone/posts/bang-shortcuts/</id>
		<content type="html">&lt;p&gt;Another you-get-to-join-the-&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;1053&#x2F;&quot;&gt;lucky-ten-thousand&lt;&#x2F;a&gt; post here!&lt;&#x2F;p&gt;
&lt;p&gt;There are a bunch of shortcuts involving exclamation marks that shells will automatically expand.
There are so many things you can do with these!
(&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;linux.die.net&#x2F;man&#x2F;1&#x2F;bash&quot;&gt;See the &quot;History Expansion&quot; section of the Bash manual page&lt;&#x2F;a&gt;)
I just want to highlight a few of the ones I use every day:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;!!&lt;&#x2F;code&gt; expands to the same command you just typed.
For example, entering &lt;code&gt;!!&lt;&#x2F;code&gt; will just re-run your last command.
You can use this to fix goof-ups.
For example, &lt;code&gt;sudo !!&lt;&#x2F;code&gt; will re-run the last command with &lt;code&gt;sudo&lt;&#x2F;code&gt; prepended.
Perfect for when you &lt;code&gt;apt-get install&lt;&#x2F;code&gt; something as your login user instead of &lt;code&gt;root&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;!$&lt;&#x2F;code&gt; expands to the last argument of the last command
I mostly use this to perform several operations on the same file.
For example, I &lt;code&gt;mv config&#x2F;database.yml.sample config&#x2F;database.yml&lt;&#x2F;code&gt;, then &lt;code&gt;kak !$&lt;&#x2F;code&gt; to edit it to customize.
I also use this frequently to preview something that I&#x27;ve just generated.
For example, &lt;code&gt;make dist&#x2F;index.json&lt;&#x2F;code&gt; and then &lt;code&gt;less !$&lt;&#x2F;code&gt; (or &lt;code&gt;jq . !$&lt;&#x2F;code&gt; to pretty-print JSON!)&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;!*&lt;&#x2F;code&gt; expands to all the arguments of the last command
I don&#x27;t use this one as much, but it&#x27;s helpful when I misspell a command.
For example, I use a tool called &lt;code&gt;aide&lt;&#x2F;code&gt; at work.
I often misspell it (e.g. &lt;code&gt;aid build monolith&lt;&#x2F;code&gt;), say &quot;ah shoot, again?&quot; and then correct with &lt;code&gt;aide !*&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some shells (ZSH at least) will automatically expand these for you if you hit tab after typing them so you can see what you&#x27;re in for before you commit.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>the hyper key</title>
		<published>2020-12-02T00:00:00+00:00</published>
		<updated>2020-12-02T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/hyper-key/" type="text/html"/>
		<id>https://bytes.zone/posts/hyper-key/</id>
		<content type="html">&lt;p&gt;Have you ever noticed that no app binds a default keyboard shortcut to shift+control+option+command+whatever?
That means you have a potential global keybinding prefix that will never have any conflicts!
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;brettterpstra.com&#x2F;2012&#x2F;12&#x2F;08&#x2F;a-useful-caps-lock-key&#x2F;&quot;&gt;Brett Terpstra calls this the hyper key&lt;&#x2F;a&gt;.
I like that!&lt;&#x2F;p&gt;
&lt;p&gt;Since this is hard to hit (which, well, that&#x27;s why nobody uses it by default) I have this bound to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;BrianHicks&#x2F;26b5c7739d909061f91432bd78897218&quot;&gt;a special key on my Kinesis&lt;&#x2F;a&gt;, but I find it acceptable to use on my normal laptop QWERTY keyboard too.
Here are some goodies I get:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;hyper + arrow: move a window to the top&#x2F;right&#x2F;bottom&#x2F;left half of my screen with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;mizage.com&#x2F;divvy&#x2F;&quot;&gt;Divvy&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;hyper + space: open &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;culturedcode.com&#x2F;things&#x2F;&quot;&gt;Things&lt;&#x2F;a&gt;&#x27; quick input&lt;&#x2F;li&gt;
&lt;li&gt;hyper + semicolon: open Things&#x27; &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;culturedcode.com&#x2F;things&#x2F;mac&#x2F;help&#x2F;things-sandboxing-helper-things3&#x2F;&quot;&gt;quick input with autofill&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;hyper + j: open the link for my next meeting with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;next-meeting&#x2F;id1017470484?mt=12&quot;&gt;Next Meeting&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;hyper + enter: jump to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;kapeli.com&#x2F;dash&quot;&gt;Dash&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;hyper + &#x27;: jump to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;iterm2.com&quot;&gt;iTerm2&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There are a few more I have bound (fullscreen, middle-of-screen shortcuts) but those are the main ones.
I&#x27;ve found them really handy over the years.
Let me know if you have any really good ones for yourself!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>elo-anything</title>
		<published>2020-11-24T00:00:00+00:00</published>
		<updated>2020-11-24T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/elo-anything/" type="text/html"/>
		<id>https://bytes.zone/posts/elo-anything/</id>
		<content type="html">&lt;p&gt;Part of my job as a team lead is dealing with a big list of work that my team is responsible for, but which we&#x27;re not going to do immediately.
Hypothetically, this is work we should be doing all the time, but we&#x27;re a small team with a lot of responsibility, so our list tends to grow.
We &lt;em&gt;should&lt;&#x2F;em&gt; work on the most impactful tasks first... but in a list of 40 or 50 little things, it&#x27;s hard to know what&#x27;s most important.
This makes the job much harder!&lt;&#x2F;p&gt;
&lt;p&gt;After a recent quarter where we didn&#x27;t accomplish any ownership work at all (screaming face emoji), something had to change.
So I built a tool to help!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Before we continue, I&#x27;m assuming it&#x27;s possible to get time allocated to maintenance&#x2F;ownership work.
I&#x27;ve heard of (and been on) engineering teams that can&#x27;t get this time, but that&#x27;s not a problem my current team has.
I have a good enough relationship with our stakeholders that they trust me if I say something needs to be worked on.
Plus, we&#x27;ve baked it into our schedules: at minimum, the team spends 15% of our time on maintenance&#x2F;ownership work in any given week.
(I save my Fridays for this.
It&#x27;s nice to end the week by hammering out 4 or 5 quick bug fixes!)&lt;&#x2F;p&gt;
&lt;p&gt;But even with the time allocated, we can&#x27;t make progress if we don&#x27;t know what to work on, so I set out to figure out how to select tasks from our big list.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;so-what-do-we-work-on&quot;&gt;So what do we work on?&lt;&#x2F;h2&gt;
&lt;p&gt;To start, whatever selection method needs to give me a rough order pretty quickly.
I can&#x27;t choose the most important thing among 50 items; there are just too many things to keep in my head and I&#x27;ll have to guess.
But it &lt;em&gt;is&lt;&#x2F;em&gt; possible if I&#x27;m picking from 10, or better yet 5, so getting even a rough order helps a lot!&lt;&#x2F;p&gt;
&lt;p&gt;Items get added to the list over time, too, so the selection method needs to handle that gracefully.
I don&#x27;t want to make a bajillion comparisons for every new item to figure out exactly where it goes.
And again, a rough order is acceptable: this doesn&#x27;t have to be extremely precise, just good enough to choose which things to work on next.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d also like the order to be able to change over time without having to re-sort the entire list.
If we learn something new or change our minds about where we want to focus, it should be easy to get a new order.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-elo-rating-system&quot;&gt;The Elo rating system&lt;&#x2F;h2&gt;
&lt;p&gt;Fortunately for us, there&#x27;s something that solves for these constraints well already: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Elo_rating_system&quot;&gt;Elo rating system&lt;&#x2F;a&gt;!
It defines a way to get a rough ranking of a big list as well as a way to adjust the ranks quickly when an item is over- or underrated.
It&#x27;s been used to rank chess players forever, too, so I&#x27;m not starting from scratch on a novel algorithm!&lt;&#x2F;p&gt;
&lt;p&gt;To order a list of items (let&#x27;s call them players to match the original use), you start with everyone at a given rating and start playing matches.
When a player wins, they take a portion of the losing player&#x27;s rating proportional to the difference between the two ratings.
Then to get rankings, you simply sort players from the highest rating to lowest.&lt;&#x2F;p&gt;
&lt;p&gt;The algorithm used to determine the rating change says:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When two players are evenly matched, the winner&#x27;s rating will go up by a little. There&#x27;s not a lot of useful information in match outcomes when players are evenly ranked: a win could indicate a skill difference, but it could also be a fluke.&lt;&#x2F;li&gt;
&lt;li&gt;If an underrated player wins against someone more highly-ranked, the underrated player&#x27;s rating will go up a lot. A win here has more information: the winner could be underrated or the loser could be overrated. A fluke could still be possible, but adjustments like these will stabilize rankings over time.&lt;&#x2F;li&gt;
&lt;li&gt;On the other hand, if a strong player wins against someone much lower-ranked, their score doesn&#x27;t rise as much. It shouldn&#x27;t be worth a lot of points to win against players you&#x27;re expected to beat every time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There&#x27;s a little more to the math to adjust for scenarios where there are few matches played versus many, but that&#x27;s the basic idea.&lt;&#x2F;p&gt;
&lt;p&gt;These rules mean that if you win consistently you&#x27;ll go up in ranking (or if you lose, you&#x27;ll go down.)
Given enough matches, you tend to get a stable ordering of players according to their level!&lt;&#x2F;p&gt;
&lt;p&gt;So does the Elo rating system fit my criteria?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A new or under-rated player can rise in the ranks quickly by challenging random higher-ranked players.&lt;&#x2F;li&gt;
&lt;li&gt;The more comparisons we make in the list, the closer to the true order items will get.&lt;&#x2F;li&gt;
&lt;li&gt;If we change our minds about something&#x27;s importance, the rating can change relatively quickly.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Sounds like a plan!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ok-let-s-use-it&quot;&gt;Ok! Let&#x27;s use it!&lt;&#x2F;h2&gt;
&lt;p&gt;To make a long story short, I built an Elm app to create and maintain these rankings for me, and you can use it right now at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;elo.bytes.zone&quot;&gt;elo.bytes.zone&lt;&#x2F;a&gt; (or get the source at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;elo-anything&quot;&gt;git.bytes.zone&#x2F;brian&#x2F;elo-anything&lt;&#x2F;a&gt;.)&lt;&#x2F;p&gt;
&lt;p&gt;Some features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You can rank however many items you need, and add and remove them at any time.&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s an undo button (which is helpful for second-guessing!)&lt;&#x2F;li&gt;
&lt;li&gt;New items are treated specially. Ideally, they&#x27;ll get to the right ranking in 5 matches or less, and the system favors selecting new items so you can finish those play-in matches quickly.&lt;&#x2F;li&gt;
&lt;li&gt;You can load and save rankings to persist items over time.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I&#x27;ve been super happy to use this to sort our task list, and now our ownership work is consistently being accomplished!
A nice benefit that I hadn&#x27;t anticipated: when I go through the list with my team&#x27;s PM, we can have a productive conversation about the relative importance of &lt;em&gt;this&lt;&#x2F;em&gt; versus &lt;em&gt;that&lt;&#x2F;em&gt;, which has revealed new information about differences in our priorities!
Using this rating system has lead to a couple of good conversations about what the team &lt;em&gt;should&lt;&#x2F;em&gt; be working on, which I&#x27;ve been really happy about.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, that&#x27;s a wrap.
Let me know if you end up using elo-anything.
It&#x27;s fairly polished at this point but I&#x27;m sure there are still weird edge cases we could find and fix!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Tradeoffs of Custom ID Types in Elm</title>
		<published>2020-10-26T00:00:00+00:00</published>
		<updated>2020-10-26T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/custom-id-types/" type="text/html"/>
		<id>https://bytes.zone/posts/custom-id-types/</id>
		<content type="html">&lt;p&gt;In Elm, we store data in records.
Here&#x27;s a model to define a &lt;code&gt;Cat&lt;&#x2F;code&gt;.
Mew!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Cat&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type alias Cat =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; purriness&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This particular implementation has a problem: you can do &lt;em&gt;whatever&lt;&#x2F;em&gt; with &lt;code&gt;id&lt;&#x2F;code&gt; since it&#x27;s a &lt;code&gt;String&lt;&#x2F;code&gt;.
Wanna concatenate it with another one?
Go right ahead.
Regex match?
Why not!
Using a &lt;code&gt;String&lt;&#x2F;code&gt; here means that the compiler can&#x27;t tell you if you&#x27;re using the &lt;code&gt;id&lt;&#x2F;code&gt; in the way it was intended to be used.&lt;&#x2F;p&gt;
&lt;p&gt;One name for this is &lt;a rel=&quot;external&quot; href=&quot;http:&#x2F;&#x2F;wiki.c2.com&#x2F;?PrimitiveObsession&quot;&gt;primitive obsession&lt;&#x2F;a&gt;, meaning code uses a language primitive (&lt;code&gt;String&lt;&#x2F;code&gt; here) instead of a domain-specific object.
This can make it harder to reason about and refactor the code, not to mention the fact that it doesn&#x27;t guard against the problems above.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h2 id=&quot;a-custom-type&quot;&gt;A Custom Type&lt;&#x2F;h2&gt;
&lt;p&gt;We can fix this in Elm by making a custom ID type:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;module&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Cat&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; exposing&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;Cat&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; CatId&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; CatId&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;type alias Cat =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; id&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : String&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; purriness&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Int&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;With this, you get pretty much all the benefits of wrapping a primitive in a class in other languages:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;we can distinguish between a &lt;code&gt;CatId&lt;&#x2F;code&gt; and a (hypothetical) &lt;code&gt;DogId&lt;&#x2F;code&gt;. Before defining a type, these would have both been &lt;code&gt;String&lt;&#x2F;code&gt;s and totally indistinguishable.&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ve disallowed &lt;code&gt;String&lt;&#x2F;code&gt; operations on the type. There will be no more concatenation, regex matching, et cetera.&lt;&#x2F;li&gt;
&lt;li&gt;If we don&#x27;t export the &lt;code&gt;CatId&lt;&#x2F;code&gt; constructor, nobody outside the &lt;code&gt;Cat&lt;&#x2F;code&gt; module can construct a value of &lt;code&gt;CatId&lt;&#x2F;code&gt;. With this setup, we can be pretty safe trusting values of &lt;code&gt;CatId&lt;&#x2F;code&gt; have been constructed in safe ways (for example, that they&#x27;ve been deserialized from JSON provided by the server.)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The benefits have been well-documented elsewhere and are (in my opinion) pretty unambiguous, so I&#x27;m keeping this brief.
I want to spend more time talking about the drawbacks of doing it in this particular way and how to get around them!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;testing-is-harder&quot;&gt;Testing is Harder&lt;&#x2F;h2&gt;
&lt;p&gt;First of all, we can no longer construct values of &lt;code&gt;CatId&lt;&#x2F;code&gt; in tests.
This means we cannot construct any records or structures that depend on &lt;code&gt;CatId&lt;&#x2F;code&gt; either.
No &lt;code&gt;Cat&lt;&#x2F;code&gt;, no &lt;code&gt;Model&lt;&#x2F;code&gt; containing &lt;code&gt;Cat&lt;&#x2F;code&gt;s, etc.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve seen some test code provide hand-rolled JSON values to a &lt;code&gt;Decoder Cat&lt;&#x2F;code&gt; to get around this, but I&#x27;m not sure that&#x27;s such a good idea.
It raises complexity in tests and ties them to the &lt;code&gt;decoder&lt;&#x2F;code&gt; instead of only the implementation we&#x27;re trying to verify.&lt;&#x2F;p&gt;
&lt;p&gt;Instead, I usually end up writing some constructor function like &lt;code&gt;catIdForTestOnly : String -&amp;gt; CatId&lt;&#x2F;code&gt;.
It has the same effect as exposing the constructor, but labels your intent clearly.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d call this pragmatic, but I know there are reasonable people who&#x27;d call it a code smell.
That&#x27;s fine!
We can disagree!
But, regardless of your approach, you&#x27;ll have to deal with the tradeoff of the hidden constructor here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;no-comparable-implementation&quot;&gt;No &lt;code&gt;comparable&lt;&#x2F;code&gt; Implementation&lt;&#x2F;h2&gt;
&lt;p&gt;Second, you can&#x27;t use &lt;code&gt;CatId&lt;&#x2F;code&gt; where the compiler expects something matching &lt;code&gt;comparable&lt;&#x2F;code&gt;.
This shows up pretty frequently because &lt;code&gt;elm&#x2F;core&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;Dict&lt;&#x2F;code&gt; needs keys to be &lt;code&gt;comparable&lt;&#x2F;code&gt; in order to provide fast lookups.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately for us, the Elm community know about this (hopefully temporary) problem and has published a bunch of different packages to help get around it.
I like &lt;code&gt;rtfeldman&#x2F;elm-sorter-experiment&lt;&#x2F;code&gt;, in which we just need to define a custom sorter function and pass it to dictionary and set constructors.
For &lt;code&gt;CatId&lt;&#x2F;code&gt;, that looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;elm&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;idSorter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; : Sorter CatId&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;idSorter&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;by (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;\&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;CatId&lt;&#x2F;span&gt;&lt;span&gt; id)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; id)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; Sort&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;alphabetical&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now we can construct dictionaries and sets containing &lt;code&gt;CatId&lt;&#x2F;code&gt; for fast lookups and still get the benefits we want.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;&#x2F;h2&gt;
&lt;p&gt;So, that&#x27;s one way to make sure you&#x27;re doing the right things with IDs (or other small data) in your Elm code.&lt;&#x2F;p&gt;
&lt;p&gt;Should you do it?
I think that despite the tradeoffs, it&#x27;s a &lt;strong&gt;yes&lt;&#x2F;strong&gt; most of the time!
I&#x27;ve had a lot of success with doing this, and I&#x27;d recommend it to others.&lt;&#x2F;p&gt;
&lt;p&gt;That said, there are some more ways to solve this problem, and I&#x27;m planning on exploring them in future posts.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Renaming Files with Braces</title>
		<published>2020-10-19T00:00:00+00:00</published>
		<updated>2020-10-19T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/renaming-files/" type="text/html"/>
		<id>https://bytes.zone/posts/renaming-files/</id>
		<content type="html">&lt;p&gt;This has been well-documented elsewhere, but in the interests of helping someone else join &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;1053&#x2F;&quot;&gt;the lucky ten thousand&lt;&#x2F;a&gt; today:
Bash, ZSH, and other shells will expand comma-sepated arguments surrounded by braces into positional arguments.&lt;&#x2F;p&gt;
&lt;p&gt;As an altogether contrived example, imagine you want to echo &quot;cat car can&quot; in the terminal.
You could write &lt;code&gt;echo cat car can&lt;&#x2F;code&gt;, right?
But then you&#x27;d be writing &lt;code&gt;ca&lt;&#x2F;code&gt; twice.
That won&#x27;t do!
We must be &lt;strong&gt;maximally efficient&lt;&#x2F;strong&gt;!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;We can use brace expansion to only write &lt;code&gt;ca&lt;&#x2F;code&gt; once: &lt;code&gt;echo ca{t,r,n}&lt;&#x2F;code&gt;.
The shell will expand that to your original &quot;cat car can.&quot;
Ta-da!&lt;&#x2F;p&gt;
&lt;p&gt;Next, say you want to add &quot;bat bar ban&quot; to the output.
Good news: the shell will expand more than one set of braces in the same word!
&lt;code&gt;echo {c,b}a{t,r,n}&lt;&#x2F;code&gt; will get you &quot;cat car can bat bar ban&quot;.
Sweet!&lt;&#x2F;p&gt;
&lt;p&gt;So, back to real life... when is this actually useful?
Well, I use it all the time for renaming or moving files!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;mv someFile.txt{,.bak}&lt;&#x2F;code&gt; expands to &lt;code&gt;mv someFile.txt someFile.txt.bak&lt;&#x2F;code&gt;, adding the &lt;code&gt;.bak&lt;&#x2F;code&gt; extension&lt;&#x2F;li&gt;
&lt;li&gt;conversely, &lt;code&gt;mv someFile.txt{.bak,}&lt;&#x2F;code&gt; removes the &lt;code&gt;.bak&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;mv config&#x2F;database.yml{.sample,}&lt;&#x2F;code&gt; expands to &lt;code&gt;mv config&#x2F;database.yml.sample config&#x2F;database.yml&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;cp {src,dist}&#x2F;index.js&lt;&#x2F;code&gt; works great in &lt;code&gt;Makefile&lt;&#x2F;code&gt;s too!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Some shells (ZSH at least) will even expand that if you hit &lt;code&gt;tab&lt;&#x2F;code&gt; so you can see what you&#x27;re going to execute before you hit commit.
Handy!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>state-transition tables</title>
		<published>2020-10-13T00:00:00+00:00</published>
		<updated>2020-10-13T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/state-transition-tables/" type="text/html"/>
		<id>https://bytes.zone/posts/state-transition-tables/</id>
		<content type="html">&lt;p&gt;I was reading Wikipedia the other day (as you do) and found out about &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;State-transition_table&quot;&gt;state-transition tables&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Basically, state transition tables show how a state machine transitions between different states.
It&#x27;s an alternative to drawing a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;State_diagram&quot;&gt;state diagram&lt;&#x2F;a&gt; that helps you find holes in your logic.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Wikipedia shows some pretty abstract tables, so I&#x27;m going to model a vending machine instead.
To simplify things, we&#x27;ll serve a single drink for a single quarter.
The idealized version of the interaction with this machine (the &quot;happy path&quot;) is:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Put a quarter in&lt;&#x2F;li&gt;
&lt;li&gt;Press the button for the drink you want&lt;&#x2F;li&gt;
&lt;li&gt;Get the drink&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;To implement this, we have to manage two independent pieces of state: whether you&#x27;ve put money in the machine and whether it has at least one drink left to vend.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s model the interaction above with a one-dimensional state-transition table.
Using only one dimension keeps the modeling as simple as possible while still capturing enough detail to be useful: we have a column each for &lt;strong&gt;input&lt;&#x2F;strong&gt;, &lt;strong&gt;current state&lt;&#x2F;strong&gt;, &lt;strong&gt;next state&lt;&#x2F;strong&gt;, and &lt;strong&gt;side effects&lt;&#x2F;strong&gt;.
To find out what happens after an event you just find the &lt;strong&gt;input&lt;&#x2F;strong&gt; and &lt;strong&gt;current state&lt;&#x2F;strong&gt; rows you care about and look at the matching &lt;strong&gt;next state&lt;&#x2F;strong&gt; and &lt;strong&gt;side effect&lt;&#x2F;strong&gt;.
For our vending machine, it might look like this:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Input&lt;&#x2F;th&gt;&lt;th&gt;Current State&lt;&#x2F;th&gt;&lt;th&gt;Next State&lt;&#x2F;th&gt;&lt;th&gt;Side Effect&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Insert Quarter&lt;&#x2F;td&gt;&lt;td&gt;No Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;Some Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;-&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Hit Button&lt;&#x2F;td&gt;&lt;td&gt;Some Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;No Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;Vend Drink&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;But, of course, we have to model what happens when we do things that are not on the happy path.
Unfortunately, the one-dimensional version of the table doesn&#x27;t give us a great view of that!&lt;&#x2F;p&gt;
&lt;p&gt;To figure out where we have holes, we need to add more dimensions.
Let&#x27;s reorganize our states along the vertical axis and inputs along the horizontal axis to get a two-dimensional state-transition table.&lt;&#x2F;p&gt;
&lt;p&gt;To read this table, match the &lt;strong&gt;current state&lt;&#x2F;strong&gt; along the vertical axis with the &lt;strong&gt;input&lt;&#x2F;strong&gt; along the horizontal.
Our &lt;strong&gt;next state&lt;&#x2F;strong&gt; and &lt;strong&gt;side effects&lt;&#x2F;strong&gt; live in the intersections (I&#x27;ve separated them with a &lt;code&gt;&#x2F;&lt;&#x2F;code&gt;):&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;↓ Current State &#x2F; Input →&lt;&#x2F;th&gt;&lt;th&gt;Insert Quarter&lt;&#x2F;th&gt;&lt;th&gt;Hit Button&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;No Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;Some Money, Some Drinks &#x2F; Nothing&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Some Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;No Money, Some Drinks &#x2F; Vend Drink&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No Money, No Drinks&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Some Money, No Drinks&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;And we see, uh... problems.
When we look at things this way, it&#x27;s clear that we&#x27;ve only defined two of the possible 8 outcomes!
Writing things down in an orderly way revealed that we haven&#x27;t specified all of the possibilities implied by our modeling.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s fill the rest out.
To make things easier, when the state stays the same or there&#x27;s no side effect I&#x27;ve marked &lt;code&gt;-&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;↓ Current State &#x2F; Input →&lt;&#x2F;th&gt;&lt;th&gt;Insert Quarter&lt;&#x2F;th&gt;&lt;th&gt;Hit Button&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;No Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;Some Money, Some Drinks &#x2F; -&lt;&#x2F;td&gt;&lt;td&gt;- &#x2F; Beep&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Some Money, Some Drinks&lt;&#x2F;td&gt;&lt;td&gt;- &#x2F; Refund Quarter&lt;&#x2F;td&gt;&lt;td&gt;No Money, Some Drinks &#x2F; Vend Drink&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No Money, No Drinks&lt;&#x2F;td&gt;&lt;td&gt;- &#x2F; Refund Quarter&lt;&#x2F;td&gt;&lt;td&gt;- &#x2F; Beep&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Some Money, No Drinks&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;???&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td&gt;&lt;strong&gt;???&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;But when we fill things out, we can see that we have a potentially weird situation: what if we somehow have some money, but no drinks?
The state machine should prevent that, since there&#x27;s no new state field that could create this situation.
But it&#x27;s feasible to get there either via programming (for example, by modeling the state as two independent fields) or hardware issues (for example, someone prying open the machine to leave quarters in an atypical act of vandalism.)&lt;&#x2F;p&gt;
&lt;p&gt;Our modeling has revealed this undefined behavior way before we got to the code parts of our application, and the hardest part was making a table and looking for empty cells.
Now I can take this same table to a stakeholder or domain expert and have a productive conversation about what they think should happen.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;d call that a win for just a little time spent modeling!&lt;&#x2F;p&gt;
&lt;p&gt;(oh, and bonus: if you&#x27;re using Elm, the one-dimensional form here is probably pretty familiar.
&quot;Input, Current State, Next State, Output&quot; does the same job as &lt;code&gt;update : msg -&amp;gt; model -&amp;gt; ( model, Cmd msg )&lt;&#x2F;code&gt;!)&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Thanks to Charlie Koster and Richard Feldman for reviewing drafts of this post.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>clown computing</title>
		<published>2020-10-12T00:00:00+00:00</published>
		<updated>2020-10-12T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/clown-computing/" type="text/html"/>
		<id>https://bytes.zone/posts/clown-computing/</id>
		<content type="html">&lt;p&gt;I keep using the term &quot;clown computing&quot; in conversations but I&#x27;ve never defined it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Clown computing is a riff on &quot;cloud computing&quot;, but instead of provisioning your software according to high availability or other actual reasonable architectural principles, you just stuff as much software as you can into a single cloud VM.&lt;&#x2F;p&gt;
&lt;p&gt;Like a clown car.
Get it?&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Fuzzy Finding with Levenstein Distance</title>
		<published>2020-10-06T00:00:00+00:00</published>
		<updated>2020-10-06T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/similar-sort/" type="text/html"/>
		<id>https://bytes.zone/posts/similar-sort/</id>
		<content type="html">&lt;p&gt;When I&#x27;m navigating around a codebase I know, I prefer to use a fuzzy file finder instead of browsing a directory tree.
Lowering the barrier to navigating between files is really helpful for me to move around a codebase quickly and get work done.
Ideally, I type a few characters and end up in the file that I was thinking of, quick as that.&lt;&#x2F;p&gt;
&lt;p&gt;But in late 2019, I got fed up trying to fuzzy-find files in my main repo at work.
It has something like 8,000 checked-in files and 170,000 untracked files (bundled gems, &lt;code&gt;node_modules&lt;&#x2F;code&gt;, etc.) so it took a long time to load all the files.
To add to that, &lt;code&gt;fzf&lt;&#x2F;code&gt;&#x27;s default configuration favors short matches near the beginning of the string.
When I&#x27;m deep in the project hierarchy, I want long matches with my term near the end first!&lt;&#x2F;p&gt;
&lt;p&gt;In short: my fuzzy matches became both &lt;em&gt;slow&lt;&#x2F;em&gt; and &lt;em&gt;irrelevant&lt;&#x2F;em&gt;.
Not a good combination for a tool meant to save me time!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;Both of these things are fairly easy fixes on their own (I could sub in &lt;code&gt;git ls-files&lt;&#x2F;code&gt; for &lt;code&gt;find&lt;&#x2F;code&gt; to get matches and configure fzf to sort by long&#x2F;end instead of short&#x2F;beginning.)
But I decided that I wanted to improve my experience: in addition to end-of-filename matching, I want to favor matches for equivalent test files.
If I&#x27;m in a file and I type &quot;spec&quot; or &quot;test&quot; (depending on the language conventions) I should ideally get to the relevant test code.&lt;&#x2F;p&gt;
&lt;p&gt;Given that most test files have some name symmetry to the files they test (e.g. &lt;code&gt;app&#x2F;models&#x2F;user.rb&lt;&#x2F;code&gt;, &lt;code&gt;spec&#x2F;models&#x2F;user_spec.rb&lt;&#x2F;code&gt;), what I want is files that are named similarly to the file I&#x27;m currently editing.
Enter Levenshtein distance, an algorithm which calculates the amount of edits you&#x27;d have to change to change a string into another string.
For example:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a&lt;&#x2F;code&gt; to &lt;code&gt;ab&lt;&#x2F;code&gt; has an edit distance of 1, because you add &lt;code&gt;b&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;ab&lt;&#x2F;code&gt; to &lt;code&gt;a&lt;&#x2F;code&gt; also has an edit distance of 1, because you remove &lt;code&gt;b&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;a&lt;&#x2F;code&gt; to &lt;code&gt;b&lt;&#x2F;code&gt;, on the other hand, has an edit distance of... 1! The first time I looked at this, I thought it was 2 (add 1, remove 1) but replacements also count as a single operation!&lt;&#x2F;li&gt;
&lt;li&gt;ok, more complex: &lt;code&gt;grammar&lt;&#x2F;code&gt; to &lt;code&gt;programmer&lt;&#x2F;code&gt; is 4. (add &lt;code&gt;pro&lt;&#x2F;code&gt;, replace &lt;code&gt;a&lt;&#x2F;code&gt; with &lt;code&gt;e&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You can do this with any two strings, although it requires more calculations as the strings get longer.
Let&#x27;s apply this to filenames!
Starting from &lt;code&gt;app&#x2F;models&#x2F;user.rb&lt;&#x2F;code&gt;, &lt;code&gt;spec&#x2F;models&#x2F;user_spec.rb&lt;&#x2F;code&gt; has an edit distance of 8, but a less similarly-named file, &lt;code&gt;app&#x2F;controllers&#x2F;admin_controller.rb&lt;&#x2F;code&gt;, has an edit distance of 22.&lt;&#x2F;p&gt;
&lt;p&gt;If we sort our candidate files by their edit distance from the source string, we can tell &lt;code&gt;fzf&lt;&#x2F;code&gt; that the input files are already in preference order (&lt;code&gt;--tiebreak=index&lt;&#x2F;code&gt;) and get exactly the editing experience I was after!&lt;&#x2F;p&gt;
&lt;p&gt;So, does this work?
I&#x27;m happy to report that it totally does!
I&#x27;ve been using similar-sort for about a year and a half now with no modifications, and I anticipate being able to use it as long as I need!&lt;&#x2F;p&gt;
&lt;p&gt;And now you can grab it as well at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;brian&#x2F;similar-sort&quot;&gt;git.bytes.zone&#x2F;brian&#x2F;similar-sort&lt;&#x2F;a&gt;.
That repo includes instructions for building and installing, as well as integration with Kakoune and Vim.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s licensed CC BY-SA 4.0 (since that&#x27;s the license for the implementation of the Levenshtein distance implementation I grabbed from Wikipedia.)&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy, and let me know if you use it!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>callCabal2nix</title>
		<published>2020-09-22T00:00:00+00:00</published>
		<updated>2020-09-22T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/callcabal2nix/" type="text/html"/>
		<id>https://bytes.zone/posts/callcabal2nix/</id>
		<content type="html">&lt;p&gt;When you&#x27;re setting up a Haskell project in Nix, most advice says &quot;call &lt;code&gt;cabal init&lt;&#x2F;code&gt; and then &lt;code&gt;cabal2nix . &amp;gt; default.nix&lt;&#x2F;code&gt; to get a buildable Haskell project.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;But it turns out you don&#x27;t have to run &lt;code&gt;cabal2nix&lt;&#x2F;code&gt; by hand to keep it up to date!
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;34f475f5eae13d18b4e4b8a17aa7a772d8619b0b&#x2F;pkgs&#x2F;development&#x2F;haskell-modules&#x2F;make-package-set.nix#L216&quot;&gt;&lt;code&gt;pkgs.haskellPackages.callCabal2nix&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; does the same thing as &lt;code&gt;cabal2nix&lt;&#x2F;code&gt;, but without writing a file.
The nixpkgs manual doesn&#x27;t mention it since it uses import-from-derivation, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;issues&#x2F;16130#issuecomment-229939552&quot;&gt;which can cause some problems with Hydra&lt;&#x2F;a&gt; but I&#x27;ve found it super helpful so I&#x27;m writing it up!
If you need more detail than what I&#x27;ve written here, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;34f475f5eae13d18b4e4b8a17aa7a772d8619b0b&#x2F;pkgs&#x2F;development&#x2F;haskell-modules&#x2F;make-package-set.nix#L216&quot;&gt;diving into the source is probably your best bet&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;The basic usage: call &lt;code&gt;pkgs.haskellPackages.callCabal2nix&lt;&#x2F;code&gt; (note the lowercase &lt;code&gt;n&lt;&#x2F;code&gt; in &lt;code&gt;nix&lt;&#x2F;code&gt;) with...&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;the project name&lt;&#x2F;li&gt;
&lt;li&gt;the path to the project source&lt;&#x2F;li&gt;
&lt;li&gt;the set of options you&#x27;d normally provide to a call to the stuff &lt;code&gt;cabal2nix&lt;&#x2F;code&gt; generates&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If you want something similar to the &lt;code&gt;default.nix&lt;&#x2F;code&gt; produced by the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;manual&#x2F;nixpkgs&#x2F;stable&#x2F;#haskell&quot;&gt;&lt;code&gt;cabal2nix&lt;&#x2F;code&gt; instructions in the nixpkgs manual&lt;&#x2F;a&gt;, put this in &lt;code&gt;default.nix&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{ pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;lt;nixpkgs&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; { }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;, ...&lt;&#x2F;span&gt;&lt;span&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;haskellPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;callCabal2nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;name-of-your-haskell-project&amp;quot; .&#x2F;.&lt;&#x2F;span&gt;&lt;span&gt; { }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;callCabal2nix&lt;&#x2F;code&gt; is always a sibling of &lt;code&gt;ghcWithPackages&lt;&#x2F;code&gt;, so you can pin your compiler version in the same way:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{ pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;lt;nixpkgs&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; { }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;, ...&lt;&#x2F;span&gt;&lt;span&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;haskell&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;packages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;ghc865&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;callCabal2nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;name-of-your-haskell-project&amp;quot; .&#x2F;.&lt;&#x2F;span&gt;&lt;span&gt; { }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you rely on getting a build environment with &lt;code&gt;cabal2nix&lt;&#x2F;code&gt;, the output &lt;code&gt;callCabal2nix&lt;&#x2F;code&gt; has a &lt;code&gt;.env&lt;&#x2F;code&gt; which you can provide to a &lt;code&gt;mkShell&lt;&#x2F;code&gt;&#x27;s &lt;code&gt;inputsFrom&lt;&#x2F;code&gt; stanza:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{ pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;lt;nixpkgs&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; { }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;, ...&lt;&#x2F;span&gt;&lt;span&gt; }:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;mkShell&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;  inputsFrom&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [ (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;haskellPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;callCabal2nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;project&amp;quot; .&#x2F;.&lt;&#x2F;span&gt;&lt;span&gt; { })&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;env&lt;&#x2F;span&gt;&lt;span&gt; ];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you were previously calling &lt;code&gt;cabal2nix&lt;&#x2F;code&gt; with command-line options, you can instead use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;34f475f5eae13d18b4e4b8a17aa7a772d8619b0b&#x2F;pkgs&#x2F;development&#x2F;haskell-modules&#x2F;make-package-set.nix#L201-L214&quot;&gt;&lt;code&gt;callCabal2nixWithOptions&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; (again, note the lowercase &lt;code&gt;n&lt;&#x2F;code&gt; in &lt;code&gt;nix&lt;&#x2F;code&gt;.)
This form adds a string before the final argument &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;nixpkgs&#x2F;blob&#x2F;34f475f5eae13d18b4e4b8a17aa7a772d8619b0b&#x2F;pkgs&#x2F;development&#x2F;haskell-modules&#x2F;make-package-set.nix#L136&quot;&gt;which is eventually used literally in a call to &lt;code&gt;cabal2nix&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s it!
Enjoy not having to write &lt;code&gt;make&lt;&#x2F;code&gt; rules to call &lt;code&gt;cabal2nix&lt;&#x2F;code&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Oh and one final note: all the code links in this post are links to specific lines the repo as of the time of writing.
If you are trying to find more information in the future, you may want to switch to the &lt;code&gt;master&lt;&#x2F;code&gt; branch of nixpkgs and sniff around for &lt;code&gt;callCabal2nix&lt;&#x2F;code&gt; to get the most up-to-date view.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>git root</title>
		<published>2020-09-10T00:00:00+00:00</published>
		<updated>2020-09-10T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/git-root/" type="text/html"/>
		<id>https://bytes.zone/posts/git-root/</id>
		<content type="html">&lt;p&gt;I sometimes find myself in a situation where I&#x27;ve &lt;code&gt;cd&lt;&#x2F;code&gt;&#x27;d several levels into a project and don&#x27;t remember exactly where I am, but I want to get back to the project root for my next command.
This doesn&#x27;t come up for me &lt;em&gt;alllll&lt;&#x2F;em&gt; the time, but the last time it did I decided to do something about it.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;To cut this short: if you run &lt;code&gt;git rev-parse --show-toplevel&lt;&#x2F;code&gt;, &lt;code&gt;git&lt;&#x2F;code&gt; will print out the location you cloned the repo.
In other words, if you run &lt;code&gt;git clone https:&#x2F;&#x2F;git-host.com&#x2F;user&#x2F;project.git ~&#x2F;code&#x2F;project&lt;&#x2F;code&gt;, that command will return &lt;code&gt;~&#x2F;code&#x2F;project&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So I&#x27;ve just added that to my aliases: &lt;code&gt;root = &quot;rev-parse --show-toplevel&quot;&lt;&#x2F;code&gt;.
Now I can do &lt;code&gt;git root&lt;&#x2F;code&gt; or &lt;code&gt;cd $(git root)&lt;&#x2F;code&gt;.
That makes this problem much less annoying for me!&lt;&#x2F;p&gt;
&lt;p&gt;Someone pointed out to me that I can probably also add an alias in my shell like &lt;code&gt;alias cdroot=&#x27;cd $(git root)&#x27;&lt;&#x2F;code&gt; to make this even easier, but I haven&#x27;t felt the need for that as urgently yet.&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>tmux-session</title>
		<published>2020-04-16T00:00:00+00:00</published>
		<updated>2020-04-16T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/tmux-session/" type="text/html"/>
		<id>https://bytes.zone/posts/tmux-session/</id>
		<content type="html">&lt;p&gt;I wrote a little script to create and fast-switch between different &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tmux&#x2F;tmux&#x2F;wiki&quot;&gt;tmux&lt;&#x2F;a&gt; sessions, and thought I&#x27;d share it real quick so others can use it too.
It&#x27;s got two composable components you can use in your own stuff:&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;&lt;h2 id=&quot;the-script-itself&quot;&gt;The Script Itself&lt;&#x2F;h2&gt;
&lt;p&gt;If I&#x27;m not running the right tmux session, I run &lt;code&gt;tmux-session&lt;&#x2F;code&gt;.
If I&#x27;m not in a tmux client (that is, just in a plain shell) it creates a session with a nice name and opens it.
If such a session already exists, it switches to it instead.
This works inside a client too!
The point is for it to always do the right thing so I don&#x27;t have to think about the four cases: outside and inside a client, with the target session existing or not.&lt;&#x2F;p&gt;
&lt;p&gt;The session names are based on git checkouts, when possible.
If you invoke the script from within a git checkout, it&#x27;ll find the root of the project (the directory containing &lt;code&gt;.git&lt;&#x2F;code&gt;) and name the session after it.
If you&#x27;re outside a git checkout, it&#x27;ll name the session after the current directory.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the commented shell script that you can stick somewhere in your &lt;code&gt;$PATH&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;#!&#x2F;usr&#x2F;bin&#x2F;env bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;set -euo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; pipefail&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# find the project root directory by examining each parent starting from the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# current directory. If you use a different VCS, you can change `$ROOT&#x2F;.git`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# below to something else: for example, Mercurial would use `$ROOT&#x2F;.hg`.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;${1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span&gt;pwd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;while !&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; test -d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;.git&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt; &amp;amp;&amp;amp;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot; != &amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;dirname&lt;&#x2F;span&gt;&lt;span&gt; $ROOT)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# if we walked all the way up and hit the root directory, just name the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# session after the current directory. Handy for creating sessions when&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# you&amp;#39;re working in directories like ~&#x2F;Downloads that won&amp;#39;t be tracked&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# with git.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; test&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot; = &amp;quot;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;pwd&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# tmux doesn&amp;#39;t allow dots in session names, so we replace them with&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# dashes. This shows up surprisingly often in my life! For example, my dotfiles&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# repo is named `dotfiles.nix`, which gets normalized to `dotfiles-nix`. This&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# is close enough to the actual name that I know what I&amp;#39;m selecting when I&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# switch to it.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SESSION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;basename&lt;&#x2F;span&gt;&lt;span&gt; $ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;#39;s&#x2F;\.&#x2F;-&#x2F;g&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# are we in a tmux client already? If we are, `$TMUX` will be set.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; test -z&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;${&lt;&#x2F;span&gt;&lt;span&gt;TMUX&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;:-&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;}&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # ah, we&amp;#39;re not in a client? Create a session and enter it! Some quirk in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # my installation make the TMUX_TMPDIR and -u2 necessary when starting&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # the server; you may not need it.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  exec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; env TMUX_TMPDIR=&#x2F;tmp tmux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -u2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; new-session&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -As&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$SESSION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # we&amp;#39;re already in a client? Neat. Let&amp;#39;s make a new session with the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # target name in the background...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  if !&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; tmux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; has-session&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$SESSION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &#x2F;dev&#x2F;null&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    tmux&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; new-session&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -ds&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$SESSION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -c&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$ROOT&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  # ... and then switch our current client to it!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;  exec&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; tmux switch-client&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$SESSION&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;the-quick-jumper&quot;&gt;The Quick Jumper&lt;&#x2F;h2&gt;
&lt;p&gt;I like to be able to jump between project directories quickly by just typing a couple letters of their name.
&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;junegunn&#x2F;fzf&quot;&gt;&lt;code&gt;fzf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; helps a lot here!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s the commented ZSH source (but I &lt;em&gt;think&lt;&#x2F;em&gt; this would work in bash as well!)&lt;&#x2F;p&gt;
&lt;p&gt;Note that this assumes my own checkout pattern (I check out all repos like &lt;code&gt;~&#x2F;code&#x2F;owner&#x2F;project&lt;&#x2F;code&gt; and have some git aliases to manage that for me.)
You could just as easily adapt it to your own checkout patterns, or just a commonly used projects list that you keep as a file (just pipe that into fzf!)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;tmux_jump&lt;&#x2F;span&gt;&lt;span&gt;() {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # starting in ~&#x2F;code...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    BASE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;code&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # ... look for directories exactly two levels deep (`~&#x2F;code&#x2F;owner&#x2F;project`)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # and match them with fzf. In this case we break ties by favoring matches&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # on the project name instead of the owner name (implementation means&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # favoring matches closer to the end of the string.) This is simplified a&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # little bit with the `--select-1 --query=&amp;quot;$1&amp;quot;` line: if there&amp;#39;s only one&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # match for the argument passed in as the first argument to this function,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # we select immediately instead of asking for an interactive selection.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    SELECTED&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;find&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$BASE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; -mindepth 2 -maxdepth 2 -type&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; sed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;s|&lt;&#x2F;span&gt;&lt;span&gt;$BASE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;||g&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; fzf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; --tiebreak=end --select-1 --query=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;$1&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # fzf will exit with a non-zero code if you ctrl-c or ctrl-g out of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # it. We use this as a signal that we don&amp;#39;t want to jump after all.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; [[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;$?&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; !=&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt; ]];&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; then&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;cancelling!&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; return&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    # call tmux-session on the *full* path to the matched project!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;    tmux-session&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$BASE&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$SELECTED&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# and alias this so I can just do `t bytes.zone` instead of having to type&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;# tmux_jump every time.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;alias&lt;&#x2F;span&gt;&lt;span&gt; t&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;tmux_jump&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And... that&#x27;s it!
Hope you enjoy it!&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="en">
		<title>Reducing Asset Size With Subsetting</title>
		<published>2020-03-03T00:00:00+00:00</published>
		<updated>2020-03-03T00:00:00+00:00</updated>
		<link href="https://bytes.zone/posts/reducing-asset-size-with-subsetting/" type="text/html"/>
		<id>https://bytes.zone/posts/reducing-asset-size-with-subsetting/</id>
		<content type="html">&lt;p&gt;&lt;strong&gt;Note:&lt;&#x2F;strong&gt; as of December 2020, this site no longer uses custom fonts.
This script is still useful, but you&#x27;re not loading the output in your browser &lt;em&gt;right now&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;When I was building this site, I noticed that my web fonts were kind of big:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;52K	fonts&#x2F;OpenSans.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	fonts&#x2F;Exo2-BoldItalic.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;24K	fonts&#x2F;Jetbrains-Mono.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	fonts&#x2F;OpenSans-Italic.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	fonts&#x2F;Exo2-Regular.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	fonts&#x2F;Exo2-Bold.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	fonts&#x2F;OpenSans-BoldItalic.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;52K	fonts&#x2F;OpenSans-Bold.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;368K	total&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you happened to load all the fonts with a cold cache, you&#x27;d be downloading a bit over a third of a megabyte.
These were by far the heaviest part of the site, especially compared to the HTML files.
Those are something like 12kb each, a quarter of the size of even one font.
I don&#x27;t like that difference, so let&#x27;s see if we can make them smaller!&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;p&gt;But what&#x27;s in those files anyway?
Why are they so big?
Consider &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NDISCOVER&#x2F;Exo-2.0&quot;&gt;Exo 2&lt;&#x2F;a&gt;, my heading font: in addition to the normal ASCII characters, it has a ton of Greek, Cyrillic, and Vietnamese characters.
This is great!
Tons of people speaking all kinds of languages can use this font to communicate… but since this site doesn&#x27;t use those characters, serving them doesn&#x27;t do anyone any good.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately, this is a known problem, and we can solve it with something called &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Subsetting&quot;&gt;subsetting&lt;&#x2F;a&gt;.
Subsetting, in general, is where you retrieve only the parts you need from a large data set.
In fonts, this means removing all the glyphs you don&#x27;t need from a font before you serve it.
Sounds like a plan!
But how?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;breaking-it-down&quot;&gt;Breaking it Down&lt;&#x2F;h2&gt;
&lt;p&gt;Well, since this is a static site, all the content is known in advance.
Hypothetically, that means that I can examine all the markdown files and figure out what characters I need to render it.
But when I thought about this more, I realized it wouldn&#x27;t work: what about the navigation and footer?
I also render headers and code samples in different fonts than I do the body copy.&lt;&#x2F;p&gt;
&lt;p&gt;Well, again, static site!
I can surely just inspect the output, right?
But another challenge there: the exact font—including things like bold and italic—is set with CSS.
Since normal, bold, and italic live in separate files, I need to calculate the style rules the way a browser would to be able to make the smallest possible subsets.&lt;&#x2F;p&gt;
&lt;p&gt;But that means… oh… I should probably just drive a headless browser, huh?&lt;&#x2F;p&gt;
&lt;p&gt;Sigh.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;accepting-the-inevitable&quot;&gt;Accepting the Inevitable&lt;&#x2F;h3&gt;
&lt;p&gt;I really didn&#x27;t want to set up a headless browser for this.
I had some bad experiences in the past trying to automate things with Selenium, not to mention all the pain I feel writing Capybara tests at work…
I felt like it would be slow, error-prone, and be hard to set up in &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.netlify.com&quot;&gt;Netlify&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Well, good news if you&#x27;re in the same boat: the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;pptr.dev&quot;&gt;Puppeteer&lt;&#x2F;a&gt; project makes this way easier than it used to be!
I evaluated several options (including Servo and jsdom) before settling on it, and I was happily surprised!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;get-the-data-already&quot;&gt;Get the Data, Already!&lt;&#x2F;h2&gt;
&lt;p&gt;The basic strategy here looks like:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;start a headless Chrome instance with puppeteer&lt;&#x2F;li&gt;
&lt;li&gt;load an HTML page&lt;&#x2F;li&gt;
&lt;li&gt;find all the visible text nodes&lt;&#x2F;li&gt;
&lt;li&gt;remember their content and computed style&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;That&#x27;s it; let&#x27;s go!
First we start the browser and load a page:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;async&lt;&#x2F;span&gt;&lt;span&gt; ()&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; puppeteer&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; require&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;puppeteer&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; browser&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; = await&lt;&#x2F;span&gt;&lt;span&gt; puppeteer.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;launch&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; = await&lt;&#x2F;span&gt;&lt;span&gt; browser.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;newPage&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  for&lt;&#x2F;span&gt;&lt;span&gt; (file&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span&gt; process.argv.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;slice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    await&lt;&#x2F;span&gt;&lt;span&gt; page.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;goto&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;file:&#x2F;&#x2F;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; file);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; the rest of our script (next code block)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;})();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aside: I don&#x27;t know if these should be &lt;code&gt;const&lt;&#x2F;code&gt; or &lt;code&gt;let&lt;&#x2F;code&gt;... I&#x27;ve heard both but the puppeteer docs use &lt;code&gt;const&lt;&#x2F;code&gt; so I&#x27;m going to, too&lt;&#x2F;p&gt;
&lt;p&gt;Then let&#x27;s walk the DOM to find all the text nodes:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;const&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; fileOut&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; = await&lt;&#x2F;span&gt;&lt;span&gt; page.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;evaluate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt; () {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; we have to define everything we need inline here, since `page.evaluate`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; runs everything in the browser context.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  function&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; uniqChars&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FFAB70;&quot;&gt;text&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; char&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span&gt; text) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;      if&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;!&lt;&#x2F;span&gt;&lt;span&gt;out.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;includes&lt;&#x2F;span&gt;&lt;span&gt;(char)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        out.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(char);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    out.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;sort&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; out.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;join&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; we&amp;#39;re going to accumulate information about each text node in this object.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; see the next code snippet for how we actually use it.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; out&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {};&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; todo is a stack of nodes we have left to visit. We start with the top node&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; of the document and work our way down from there.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; todo&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [window.document];&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  while&lt;&#x2F;span&gt;&lt;span&gt; (todo.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; !==&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    let&lt;&#x2F;span&gt;&lt;span&gt; node&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; todo.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;shift&lt;&#x2F;span&gt;&lt;span&gt;();&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; `childNodes` is not an array, but you can still access its children by&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; index. We are descending into the node so we just push all the children&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; onto the stack. It doesn&amp;#39;t matter much for this application whether we&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; use a depth-first or a breadth-first search—we want to get all the&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;    &#x2F;&#x2F; nodees either way.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;var&lt;&#x2F;span&gt;&lt;span&gt; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; node.childNodes.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;length&lt;&#x2F;span&gt;&lt;span&gt;; i&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;++&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      todo.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;push&lt;&#x2F;span&gt;&lt;span&gt;(node.childNodes[i]);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; (node.nodeName&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;#text&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;      &#x2F;&#x2F; the rest of our script (next code block)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  return&lt;&#x2F;span&gt;&lt;span&gt; out;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;});&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally we can accumulate our calculated styles.
I do this in an object with the style information as the key (&lt;code&gt;out&lt;&#x2F;code&gt; above) so we can add characters to it easily:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; styles&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; window.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;getComputedStyle&lt;&#x2F;span&gt;&lt;span&gt;(node.parentElement);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; we don&amp;#39;t want to include text in invisible nodes (things like the guts of&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;&#x2F;&#x2F; &amp;lt;script&amp;gt; or &amp;lt;style&amp;gt; tags.) Just skip &amp;#39;em!&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;if&lt;&#x2F;span&gt;&lt;span&gt; (styles.display&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; ===&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;none&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  continue&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;let&lt;&#x2F;span&gt;&lt;span&gt; face&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; of&lt;&#x2F;span&gt;&lt;span&gt; styles.fontFamily.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&amp;quot;, &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6A737D;&quot;&gt;  &#x2F;&#x2F; fonts with spaces in the name get quoted, so we need to remove those.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  face&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; face.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;replace&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #DBEDFF;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;g&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; info&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    face: face,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    weight: styles.fontWeight,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    style: styles.fontStyle&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  let&lt;&#x2F;span&gt;&lt;span&gt; key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt; JSON&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt;stringify&lt;&#x2F;span&gt;&lt;span&gt;(info);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #F97583;&quot;&gt;  if&lt;&#x2F;span&gt;&lt;span&gt; (out[key]) {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    out[key].chars&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; uniqChars&lt;&#x2F;span&gt;&lt;span&gt;(out[key].chars&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; node.textContent);&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; else&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    out[key]&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F97583;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      font: info,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      chars:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #B392F0;&quot;&gt; uniqChars&lt;&#x2F;span&gt;&lt;span&gt;(node.textContent)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    };&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, after analyzing each file, we combine the objects into a list.
(I haven&#x27;t shown this but you can see it &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;bytes.zone&#x2F;bytes.zone&#x2F;src&#x2F;commit&#x2F;7d957f13a7801ecbf1a5f663d263538214e44990&#x2F;script&#x2F;faces.js&quot;&gt;in the source as of this writing&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;p&gt;When the script returns, we end up with a JSON blob like this.
This is just &lt;a href=&quot;&#x2F;&quot;&gt;the homepage&lt;&#x2F;a&gt; and I&#x27;ve removed all the fallback fonts.
If I had passed more files into the script (for example, the output of &lt;code&gt;find&lt;&#x2F;code&gt;) I would see much more information in this array:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;json&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;font&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;face&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;Exo 2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;weight&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;700&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;style&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;normal&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;chars&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot; .HTbehnorstyz👋&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;font&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;face&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;Exo 2&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;weight&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;400&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;style&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;normal&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;chars&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;acdeklopst&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;font&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;face&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;Open Sans&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;weight&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;400&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;      &amp;quot;style&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot;normal&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    },&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #79B8FF;&quot;&gt;    &amp;quot;chars&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #9ECBFF;&quot;&gt; &amp;quot; !&amp;#39;,-.04:ABCDEHIJLMNORSTYabcdefghiklmnoprstuvwyz❤️&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There are some interesting things happening here.
First, I mentioned that I only was using ASCII… well, that&#x27;s a bit of a lie; I&#x27;m also using a few emoji.
The script found and included these.
Second, I&#x27;m not even using &lt;em&gt;all&lt;&#x2F;em&gt; the ASCII characters; &lt;code&gt;q&lt;&#x2F;code&gt; and &lt;code&gt;x&lt;&#x2F;code&gt; are absent from Open Sans (my body font.)&lt;&#x2F;p&gt;
&lt;p&gt;True, this is just for one page, but it holds across the rest of the content: each font ends up only needing to include well under 100 glyphs to be able to render everything on the whole site.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;subsetting-for-real&quot;&gt;Subsetting, for Real&lt;&#x2F;h2&gt;
&lt;p&gt;After all that analysis, how do we actually subset the fonts?&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m using a tool called &lt;code&gt;pyftsubset&lt;&#x2F;code&gt;, available in the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;fonttools&#x2F;fonttools&quot;&gt;fonttools&lt;&#x2F;a&gt; Python package.
Put simply, to subset a font, you need to call &lt;code&gt;pyftsubset {input font} --unicodes={code points}&lt;&#x2F;code&gt;.
There are a few more flags in the actual script I use, but that&#x27;s basically it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;bytes.zone&#x2F;bytes.zone&#x2F;src&#x2F;commit&#x2F;7d957f13a7801ecbf1a5f663d263538214e44990&#x2F;script&#x2F;subset.py&quot;&gt;The script itself&lt;&#x2F;a&gt; is not terribly exciting, so I&#x27;m not going to show much of it here.
It basically slurps down the output of the face-finding script, calls &lt;code&gt;pyftsubset&lt;&#x2F;code&gt; on the result, and prints some statistics about the filesize difference:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Finding subsets of 8 fonts on 10 pages&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;Exo2-Bold.woff2 from 46656 to 5608 bytes (12.02% of original size, 61 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;OpenSans.woff2 from 50116 to 8240 bytes (16.44% of original size, 81 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;Exo2-Regular.woff2 from 46300 to 3440 bytes (7.43% of original size, 35 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;Jetbrains-Mono.woff2 from 22368 to 10564 bytes (47.23% of original size, 87 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;OpenSans-Italic.woff2 from 48148 to 3668 bytes (7.62% of original size, 18 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Subset .&#x2F;dist&#x2F;fonts&#x2F;OpenSans-Bold.woff2 from 51932 to 4548 bytes (8.76% of original size, 27 glyphs)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And looking at the final font sizes, I feel much better aabout shipping these to people&#x27;s browsers.
With this result, I can load all the glyphs in all the fonts I need in less size than just one of the original font files:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #E1E4E8; background-color: #24292E;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;12K	dist&#x2F;fonts&#x2F;OpenSans.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;12K	dist&#x2F;fonts&#x2F;Jetbrains-Mono.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4.0K	dist&#x2F;fonts&#x2F;OpenSans-Italic.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;4.0K	dist&#x2F;fonts&#x2F;Exo2-Regular.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8.0K	dist&#x2F;fonts&#x2F;Exo2-Bold.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;8.0K	dist&#x2F;fonts&#x2F;OpenSans-Bold.woff2&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;48K	total&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Aside: I also have some uncompressed-but-unused files in the output of my site, but they aren&#x27;t ever referenced, so a browser shouldn&#x27;t ever load them.
I&#x27;m not counting those towards the filesize total here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;success&quot;&gt;Success!&lt;&#x2F;h2&gt;
&lt;p&gt;In the end, I reduced the size of my font files by about 87%, going from 386K to 48K.
The final subsets have about 300 total characters to render my site.
While this will grow over time as I add more content, it should level out pretty quickly.&lt;&#x2F;p&gt;
&lt;p&gt;In short: it worked!&lt;&#x2F;p&gt;
&lt;p&gt;So, in writing about this, would I recommend you do it too?
Solid &lt;strong&gt;maybe&lt;&#x2F;strong&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;I think it makes sense to do this on a site where:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;You&#x27;re using custom fonts.&lt;&#x2F;strong&gt; If you&#x27;re using generic font names (like &lt;code&gt;sans-serif&lt;&#x2F;code&gt; or &lt;code&gt;monospace&lt;&#x2F;code&gt;) or your font stack is a mix of built-ins and system fonts (e.g. &lt;code&gt;Helvetica, Arial, sans-serif&lt;&#x2F;code&gt;), there&#x27;s no need to save on bandwidth here; your readers already have the whole font!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;All your content is known in advance.&lt;&#x2F;strong&gt; If you have anything that&#x27;s dynamically loaded, you&#x27;ll get really weird results where some—but not all—of your characters will be replaced by the fallback font.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Fonts are actually the largest asset class.&lt;&#x2F;strong&gt; If you&#x27;re serving a 10mb JPEG as a background image, compressing that is a lot easier and will be a much better experience for your readers than figuring out a subsetting pipeline for your site. The framework I use, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;elm-pages.com&quot;&gt;elm-pages&lt;&#x2F;a&gt;, takes care of compressing images for me. If yours doesn&#x27;t, searching around for image optimization tools will probably net you some easy wins.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;You don&#x27;t care about cache misses (in the short term.)&lt;&#x2F;strong&gt; Your custom subset is all but guaranteed not to be in a visitor&#x27;s cache. For a site like mine, a really high percent of visitors are likely to be new. That makes me feel more OK about serving fresh font files every time. But a big note here: on a site with more content, the subsets will tend to stabilize to the characters you actually use. In the long run, the fonts won&#x27;t be quite this small but they will be much stabler.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;If your site meets all those conditions, give it a try!
I&#x27;ve linked to the source of my scripts in the page above, and you can check out the full latest source for this site at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;git.bytes.zone&#x2F;bytes.zone&#x2F;bytes.zone&quot;&gt;git.bytes.zone&#x2F;bytes.zone&#x2F;bytes.zone&lt;&#x2F;a&gt;. (is it just me or is it echoing in here?)&lt;&#x2F;p&gt;
&lt;p&gt;Have fun!&lt;&#x2F;p&gt;
</content>
	</entry>
</feed>
