<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>blog posts on joe blubaugh</title>
    
    
    
    <link>/blog/</link>
    <description>Recent content in blog posts on joe blubaugh</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <copyright>© 2016-{year} Joe Blubaugh</copyright>
    <lastBuildDate>Wed, 29 Nov 2017 09:24:12 -0800</lastBuildDate>
    
	<atom:link href="/blog/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>Zen: A Browser You Can Love</title>
      <link>/blog/2026_02_zen-a-browser-you-can-love/</link>
      <pubDate>Sat, 07 Feb 2026 01:17:18 +0000</pubDate>
      
      <guid>/blog/2026_02_zen-a-browser-you-can-love/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;a href=&#34;https://www.google.com/chrome/ai-innovations/&#34;&gt;Everyone&lt;/a&gt; is &lt;a href=&#34;https://www.microsoft.com/en-us/edge/features/ai?form=MT0160&#34;&gt;putting AI&lt;/a&gt; in &lt;a href=&#34;https://blog.mozilla.org/en/firefox/ai-window/&#34;&gt;their browsers&lt;/a&gt; now. Good (maybe even great!) &lt;a href=&#34;https://www.engadget.com/ai/the-browser-company-stops-active-development-of-arc-in-favor-of-new-ai-focused-product-153045276.html&#34;&gt;browsers have been discontinued&lt;/a&gt; so users can focus on AI.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://joeblu.com/blog/2026_02_multi-agent-coding-and-the-resurgence-of-the-terminal/&#34;&gt;I&amp;rsquo;m not opposed&lt;/a&gt; to &lt;a href=&#34;https://joeblu.com/blog/2026_01_introducing-nvim-beads-manage-beads-in-neovim/&#34;&gt;using AI agents&lt;/a&gt; for &lt;a href=&#34;https://joeblu.com/blog/2025_12_llms-are-great-at-box-diagrams/&#34;&gt;all sorts of things&lt;/a&gt;, but I don&amp;rsquo;t feel comfortable giving it nigh-unfettered access to the web. I&amp;rsquo;m not ready to let LLMs take on life-altering responsibilities like booking flights, looking at my banking sites, or reading my emails. I don&amp;rsquo;t trust their data retention policies with potentially sensitive context like calendars or emails. Nobody&amp;rsquo;s&lt;/p&gt;
&lt;p&gt;So I don&amp;rsquo;t want it in my web browser, which is where most of this work gets done in 2026. I&amp;rsquo;ve been using Firefox at home and Arc at work, and they&amp;rsquo;ve got different problems. Arc is over. &amp;ldquo;The Browser Company&amp;rdquo; has gone all in on an &lt;a href=&#34;https://www.diabrowser.com/&#34;&gt;AI Browser&lt;/a&gt; that&amp;rsquo;s not dissimilar to &lt;a href=&#34;https://chatgpt.com/atlas/&#34;&gt;OpenAI&amp;rsquo;s browser&lt;/a&gt;. Firefox added AI features in November and is only now &lt;a href=&#34;https://blog.mozilla.org/en/firefox/ai-controls/&#34;&gt;adding controls&lt;/a&gt; that can give you some control over data sharing.&lt;/p&gt;
&lt;p&gt;Fortunately, someone recommended &lt;a href=&#34;https://zen-browser.app/&#34;&gt;Zen&lt;/a&gt; to me. Arc&amp;rsquo;s approach to tabs always clicked with me: every view of a profile, even in a different window, has the same tab set. You can use multiple profiles (&amp;ldquo;spaces&amp;rdquo;) to manage different tab groups, and different sign-in contexts. Zen&amp;rsquo;s latest release uses the same behavior, and it&amp;rsquo;s delightful. It has a simple but very useful split-view feature. I use it for things like reading the meeting notes and having the video call in the same window, or comparing two related documents without having to tab back and forth. Where it has any AI features at all, they are limited and require advanced settings to turn on. You&amp;rsquo;re not going to get them intruding on you, at least for now.&lt;/p&gt;
&lt;p&gt;It turns out I&amp;rsquo;m &lt;a href=&#34;https://www.xda-developers.com/stop-using-brave-and-chrome-zen-browser-is-the-only-one-id-recommend/&#34;&gt;not the only one&lt;/a&gt; who&amp;rsquo;s &lt;a href=&#34;https://werd.io/why-im-all-in-on-zen-browser/&#34;&gt;had this experience&lt;/a&gt;.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Multi-agent coding and the resurgence of the terminal</title>
      <link>/blog/2026_02_multi-agent-coding-and-the-resurgence-of-the-terminal/</link>
      <pubDate>Thu, 05 Feb 2026 15:23:52 +0000</pubDate>
      
      <guid>/blog/2026_02_multi-agent-coding-and-the-resurgence-of-the-terminal/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;All of the exciting AI coding is happening in terminals. Codex is good, the various VSCode forks are good, but if you&amp;rsquo;re doing multi-agent orchestration you&amp;rsquo;re generally using terminal UIs, roped together with &lt;a href=&#34;https://github.com/steveyegge/gastown&#34;&gt;Gastown&lt;/a&gt;, &lt;a href=&#34;https://github.com/asheshgoplani/agent-deck&#34;&gt;agent-deck&lt;/a&gt;, &lt;a href=&#34;https://openclaw.ai/&#34;&gt;OpenClaw&lt;/a&gt;, &lt;a href=&#34;https://github.com/petergaultney/lemonaid&#34;&gt;lemonaid&lt;/a&gt;, or something else.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s gratifying, as someone who never left the terminal for a &amp;ldquo;grown up&amp;rdquo; IDE, to see how small programs piped together is the paradigm for innovation here. The stuff that makes it possible? Simple, largely open-source tech:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;tmux&lt;/strong&gt;: lemonaid, agent-deck, and Gastown all use tmux panes and windows to let you jump to the right place and give the right info to the right agent at the right time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;git&lt;/strong&gt;: Lots of work-tracking and communication systems (like &lt;a href=&#34;https://github.com/steveyegge/beads&#34;&gt;beads&lt;/a&gt;) use git. &lt;a href=&#34;https://matklad.github.io/2024/07/25/git-worktrees.html&#34;&gt;Git worktrees&lt;/a&gt; are an essential component for multiple agents to work on the same codebase at the same time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;sqlite&lt;/strong&gt;: agent-deck uses this for communication, and so many other tools seem to as well.&lt;/li&gt;
&lt;/ul&gt;

        
        </description>
    </item>
    
    <item>
      <title>Introducing nvim-beads: Manage beads in neovim</title>
      <link>/blog/2026_01_introducing-nvim-beads-manage-beads-in-neovim/</link>
      <pubDate>Wed, 14 Jan 2026 06:29:35 +0000</pubDate>
      
      <guid>/blog/2026_01_introducing-nvim-beads-manage-beads-in-neovim/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;A few days ago my &lt;a href=&#34;https://www.linkedin.com/in/bobfromdenver&#34;&gt;co-worker Bob&lt;/a&gt; turned me on to &lt;a href=&#34;https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a&#34;&gt;Beads&lt;/a&gt;. Beads is a step up from things like &lt;code&gt;PLAN.md&lt;/code&gt; when managing work for coding agents, because it provides more structure, is easy for agents to parse, and provides things like progress tracking for multi-agent workflows.&lt;/p&gt;
&lt;p&gt;I was intrigued by the setup because it seemed like a way to improve my agent&amp;rsquo;s ability to work autonomously, but I needed a way to try it out without stepping on top of some complex multi-person projects at work. I decided to use &lt;code&gt;bd&lt;/code&gt; and a coding agent to implement a neovim plugin for managing beads.&lt;/p&gt;
&lt;p&gt;The result is &lt;a href=&#34;https://github.com/joeblubaugh/nvim-beads&#34;&gt;nvim-beads&lt;/a&gt;. Implementing this took about 106 commits over 3 hours of pairing between me and the Claude CLI. It&amp;rsquo;s written in technologies I know about but am not good at: Lua and the Neovim API. There&amp;rsquo;s no way I would have built something with this level of complexity in this environment in less than a week of good, solid work. It has tests - which I wouldn&amp;rsquo;t know how to write.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think it would have gotten built without Beads, either. The workflow of building a &lt;code&gt;PLAN.md&lt;/code&gt; and then having an agent execute it works OK, but if the plan is large, you&amp;rsquo;re wasting a lot of context using it over and over again. Beads&amp;rsquo; structure wastes less context on large plans, and make it easy to either blow away an agent and start again, or makes conversation compaction less of an issue. It also makes parallel agent workflows on multiple git worktrees easier to manage, because the agents can share beads state on one beads-specific branch while doing their work implementing those beads on feature branches.&lt;/p&gt;
&lt;p&gt;I think the tech is worth trying, and &lt;a href=&#34;https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a&#34;&gt;this post from the author Steve Yegge&lt;/a&gt; is a good place to start. After that, &lt;a href=&#34;https://steve-yegge.medium.com/beads-best-practices-2db636b9760c&#34;&gt;read the best practices post&lt;/a&gt;.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Podcasting is dominated by warm applesauce</title>
      <link>/blog/2026_01_podcasting-is-dominated-by-warm-applesauce/</link>
      <pubDate>Tue, 13 Jan 2026 11:37:44 +0000</pubDate>
      
      <guid>/blog/2026_01_podcasting-is-dominated-by-warm-applesauce/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;The end of podcasting as a viable cottage industry really does seem to be here now, and the end of podcasting as a culture-moving media format has arrived. &lt;a href=&#34;https://www.nbc.com/nbc-insider/amy-poehler-2026-golden-globe-acceptance-speech&#34;&gt;Amy Poehler won the first podcasting Golden Globe&lt;/a&gt; for a &amp;ldquo;hang out and promote my new project&amp;rdquo; podcast. Most celebrity podcasts are this way. Even the ones that ostensibly have a theme: Smartless is barely anything anymore. They&amp;rsquo;re warm applesauce, and they dominate the format to the point that ad revenue is draining away from a lot of better shows.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Actors give podcast award to actor with a podcast&amp;rdquo; isn&amp;rsquo;t all that shocking, but still, look at the nominees for &amp;ldquo;best podcast&amp;rdquo;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Good Hang with Amy Poehler&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Armchair Expert With Dax Shepard&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Call Her Daddy&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;The Mel Robbins Podcast&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;SmartLess&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;NPR&amp;rsquo;s &lt;em&gt;Up First&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I guess we can at least be grateful they didn&amp;rsquo;t nominate JRE. For every single niche these shows occupy - maybe with the exception of &lt;em&gt;Up First&lt;/em&gt; - you can easily find a better version that is less popular. I guess the fact that &amp;ldquo;massive media breeds mediocre media&amp;rdquo; is maybe not all that surprising, either.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>All the data had nowhere to go</title>
      <link>/blog/2026_01_all-the-data-had-nowhere-to-go/</link>
      <pubDate>Tue, 06 Jan 2026 12:24:03 +0000</pubDate>
      
      <guid>/blog/2026_01_all-the-data-had-nowhere-to-go/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Pablo Torre is on &lt;a href=&#34;https://www.youtube.com/watch?v=QpPpBJk0oEo&#34;&gt;episode 506 of TrueAnon&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;, which is mostly about the NBA-adjacent poker fixing scandals, but touches on sports betting. The fact that data collection is not a neutral activity comes up repeatedly.&lt;/p&gt;
&lt;p&gt;Player performance is monitored and measured for non-nefarious reasons: players and teams both benefit from better understanding of strengths and weaknesses in different ways. Pablo makes the case that the mere existence of this data - the predominance of statistical analysis in front offices and the advanced stats published by the leagues - changed the culture of American sports in negative ways. Rationalization of athletic performance led fans to believe the could get some edge (&lt;em&gt;alpha&lt;/em&gt; in the financier&amp;rsquo;s language), and sports gambling was the natural outlet for this.&lt;/p&gt;
&lt;p&gt;Black markets were bad enough, but after sports gambling was effectively legalized in the entire USA, goody-two-shoes started to get in on the act, too. If you&amp;rsquo;re desperate not to fall behind, windfalls are really attractive. If you&amp;rsquo;ve known any really committed gamblers, you know this is really a path of destruction. It&amp;rsquo;s leading to behaviors in basketball that we&amp;rsquo;d eradicated in the US (aside from boxing): players intimidated by organized crime, refs in league with bookies.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The NBA is particularly affected, though the MLB is, too. Pablo says the leagues are &amp;ldquo;fracking themselves&amp;rdquo; extracting money from this gambling culture while destroying the joy in sports that brings the audience in the first place.&lt;/p&gt;
&lt;p&gt;This discussion is a great companion to Maciej Cegłowski&amp;rsquo;s metaphor of &lt;a href=&#34;https://idlewords.com/talks/haunted_by_data.htm&#34;&gt;data as &amp;ldquo;toxic waste&amp;rdquo;&lt;/a&gt;. A company retaining data after its useful life presents incredible privacy risks for individuals. I do not think the GDPR has made that much difference here - legitimate business purposes can still be defined with an infinite lifespan. Maciej focuses on these risks to individuals and companies, but I think this near-infinite data collection has had corrosive effects on our culture, too.&lt;/p&gt;
&lt;p&gt;Because we routinely hand over data that we must assume will be leaked, we weaken our own expectations for privacy, which makes us more likely to hand over this information over and over again. Because we have this data, we are tempted - nearly compelled - to think of our lives in terms of this data. Systematizing and rationalizing ourselves and our cultural institutions are not leading to human flourishing. Rationalizing commerce has pulled people out of poverty, but we need some way to contain these tendencies.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Be forwarned that TrueAnon is quite profane and sarcastic by design.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;Maybe there&amp;rsquo;s a comparison to be made with the resurgence of measles, too.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        
        </description>
    </item>
    
    <item>
      <title>Finding &#34;Just Fine&#34;</title>
      <link>/blog/2025_12_finding-just-fine/</link>
      <pubDate>Thu, 04 Dec 2025 06:01:17 +0000</pubDate>
      
      <guid>/blog/2025_12_finding-just-fine/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;ve had a record player for at least 20 years. For the last 10 or so it was an &lt;a href=&#34;https://www.audio-technica.com/en-us/at-lp120-usb&#34;&gt;Audio Technica LP120&lt;/a&gt;, which is on the upper end of &amp;rsquo;non-audiophile&amp;rsquo; turntables. Swappable cartridges, tons of tracking force adjustments, speed adjustments and strobes to check for accuracy. Direct drive. I ended upgrading the cartridge at some point. I loved that turntable, and it was darn reliable.&lt;/p&gt;
&lt;p&gt;I did manage to fry it on 220v here in Singapore, despite the fact that it advertised compatibility with 220v. After it broke, it didn&amp;rsquo;t take me long to find a successor: an &lt;a href=&#34;https://www.audio-technica.com/en-us/at-lp70xbt&#34;&gt;Audio Technica LP70X&lt;/a&gt;, which is in many ways the opposite: Fixed cartridge, no tracking force adjustment or speed adjustment. Belt driven.&lt;/p&gt;
&lt;p&gt;So why the &amp;ldquo;downgrade&amp;rdquo;? And given that I&amp;rsquo;ve been buying and playing LPs for so long, why not upgrade to something really great?&lt;/p&gt;
&lt;p&gt;Because the price+time vs results curve on so many things in our life looks like this:
&lt;img src=&#34;/images/justfine/effort.png&#34; alt=&#34;output to effort&#34;&gt;&lt;/p&gt;
&lt;p&gt;and the LP70 is just past the sigmoid component, while the LP120 is higher up: almost twice the price, but diminishing returns.&lt;/p&gt;
&lt;p&gt;You can apply this to &lt;em&gt;tons&lt;/em&gt; of things. There&amp;rsquo;s a relationship like this when it comes to cars, musical instruments, cameras, youth sports, vacations, kitchen equipment. For myself, it was even true with schooling - thankfully I was blessed with choice. Going to Purdue over something like Indiana State made a much bigger difference in my life than going to Yale or MIT over Purdue would have.&lt;/p&gt;
&lt;p&gt;For some people, those top end results are worth the time and whatever money it takes. For most of us, finding that point just past the sigmoid is a real sweet spot for quality of life. Except the one or two things you&amp;rsquo;re truly passionate about.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>LLMs are great at box diagrams</title>
      <link>/blog/2025_12_llms-are-great-at-box-diagrams/</link>
      <pubDate>Tue, 02 Dec 2025 12:26:16 +0000</pubDate>
      
      <guid>/blog/2025_12_llms-are-great-at-box-diagrams/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I love diagram languages like &lt;a href=&#34;https://mermaid.js.org/&#34;&gt;mermaid.js&lt;/a&gt; and &lt;a href=&#34;https://plantuml.com/&#34;&gt;PlantUML&lt;/a&gt; because the text format that backs the diagram can be tweaked, takes up very little space in storage, and can be re-rendered at various resolutions any time you need. They&amp;rsquo;re great! I &lt;strong&gt;hate&lt;/strong&gt; writing them. Mermaid has a different syntax for every diagram type, and they&amp;rsquo;re hard to remember. PlantUML is more consistent, though not totally, and in my experience can be quite verbose.&lt;/p&gt;
&lt;p&gt;I use drawing tools to make diagrams for presentations. I&amp;rsquo;ve gotten pretty good at &lt;a href=&#34;https://excalidraw.com/&#34;&gt;Excalidraw&lt;/a&gt;. I like that I have total control over the layout, which is difficult for me to get with diagram languages. They stink for documentation. The images look good, but need to be rendered at multiple resolutions. You need to save the file somewhere to make edits. Nodes get disconnected accidentally all the time.&lt;/p&gt;
&lt;p&gt;What I &lt;strong&gt;want&lt;/strong&gt; is to draw the diagram by hand - the fastest way. Then I want it digitized into a format I can tweak. We&amp;rsquo;ve been trying to do that for ages with digital whiteboards, etc. It&amp;rsquo;s &amp;ldquo;eh&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been incorporating LLMs more and more into my software development process in the last month or so. The Claude CLI and Cursor have both been working well for me. I&amp;rsquo;ve done a lot of the &amp;ldquo;normal&amp;rdquo; software dev stuff: have an agent write up and revise benchmark tests, write a CRUD API for a new type I&amp;rsquo;m introducing.&lt;/p&gt;
&lt;p&gt;So I decided to see how good it would be at making Mermaid diagrams for me. I drew this diagram on paper, snapped a photo and dropped it onto my laptop.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/llm/sketch.jpg&#34; alt=&#34;sketch&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the prompt I gave the Claude CLI:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Turn the IMG_ file in this folder into a mermaid diagram in the .mmd format. It should be either a flowchart or a box diagram&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Claude can&amp;rsquo;t read HEIC, so it even converted the image to a JPEG first with &lt;code&gt;sips&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After that I used &lt;code&gt;mermaid-cli&lt;/code&gt; to render a PNG and opened it in Chrome:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ npx @mermaid-js/mermaid-cli -i file.mmd &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; open file.mmd.svg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s the first output:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/llm/architecture.mmd.svg&#34; alt=&#34;generated&#34;&gt;&lt;/p&gt;
&lt;p&gt;Pretty good!&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used this at work too, for box diagrams with around 20 elements, and it&amp;rsquo;s done pretty well. I get the diagram I want after two or three rounds of tweaking. The feedback loop is really fast and less error prone than the mermaid live editor. I think I cut a diagram drawing session about in half, because my hand-drawn diagram turns into text so quickly.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Ten years of joeblu.com</title>
      <link>/blog/2025_10_ten-years-of-joeblucom/</link>
      <pubDate>Tue, 28 Oct 2025 03:37:40 +0000</pubDate>
      
      <guid>/blog/2025_10_ten-years-of-joeblucom/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;ve had this domain, this site for 10 years. Running on Hugo the whole time. There are only a few things here, but it&amp;rsquo;s no big deal. There are very few things I have on the internet that have been live that whole time and are still a going concern:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this site&lt;/li&gt;
&lt;li&gt;gmail&lt;/li&gt;
&lt;li&gt;Facebook&lt;/li&gt;
&lt;li&gt;Instagram&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Only one of those gives me real control of what I put out here, with no rules but my own and the law. It&amp;rsquo;s a blessing to still be able to do that in 2025 - give anyone the power to publish anything.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>What was *WTF with Marc Maron*?</title>
      <link>/blog/2025_10_what-was-wtf-with-marc-maron/</link>
      <pubDate>Tue, 28 Oct 2025 00:00:00 +0000</pubDate>
      
      <guid>/blog/2025_10_what-was-wtf-with-marc-maron/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I first met Marc Maron in 2010 at a &lt;a href=&#34;https://www.maxfuncon.com/entertainment&#34;&gt;comedy festival in Southern California&lt;/a&gt;. &lt;em&gt;WTF&lt;/em&gt; was less than a year old, and most of the guests thus far were “alternative comedians”. All podcasts back then were either tech-focused or comedy-focused, particularly LA alternative comedy. I found the tech ones interminably boring, but alternative comedy was really important to me and I was hooked on &lt;em&gt;WTF&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I was in the back of a van with Maron and Maria Bamford (in hindsight, insane situation) as we drove around Lake Arrowhead for half an hour. Marc bored into me for at least half the time, asking me about what made me move to California, my ethnic background and family history in the US, opinions on late night shows. It was more intense than any first date conversation, and I was flattered by the attention. Still, I doubt it&amp;rsquo;s an encounter he&amp;rsquo;d remember. He’d been doing his radio show for years, meeting hundreds of new people a year between that and standup sets. I think he’s just that way - curious about every person and what drives them.&lt;/p&gt;
&lt;p&gt;15 years later, &lt;em&gt;WTF with Marc Maron&lt;/em&gt; has ended, and Marc’s released a new special, &lt;em&gt;Panicked&lt;/em&gt;. He never stopped releasing specials or doing sets, which is amazing when you consider the cadence of &lt;em&gt;WTF&lt;/em&gt;. It seems like every comedian has a podcast now, but so many of them are just two hours of bullshitting on YouTube or reminiscing for a Millennial audience with very little preparation. Apologies to Bill Burr and Amy Poehler, but so many of them are nothing-burger ways to sell mattresses and athleisure. &lt;em&gt;WTF&lt;/em&gt; interviews always had an impressive amount of prep - reading books, watching film, and actually &lt;strong&gt;thinking&lt;/strong&gt; about the person you&amp;rsquo;re talking to.&lt;/p&gt;
&lt;p&gt;It was more compelling (and obviously less intellectual) than &lt;em&gt;Fresh Air&lt;/em&gt;, and most other interview shows. Not nearly as high-minded, while still being substantial. It&amp;rsquo;s telling that somebody as savvy as Barack Obama went on &lt;em&gt;WTF&lt;/em&gt; five years before appearing on &lt;em&gt;Fresh Air&lt;/em&gt;, and even came back two weeks ago for the final episode of the show. (&lt;strong&gt;edit&lt;/strong&gt;: Obama actually &lt;a href=&#34;https://freshairarchive.org/segments/barack-obama-his-us-senate-bid&#34;&gt;went on Fresh Air in 2004&lt;/a&gt; when he was an Illinois State Senator. He wouldn&amp;rsquo;t return until 2020. I don&amp;rsquo;t think this undermines the point about choosing WTF as an interview venue instead of traditional media)&lt;/p&gt;
&lt;p&gt;That Obama episode in 2015 changed everything about the show, and possibly about podcasting in general. It&amp;rsquo;s so important to the show that it gets its own entry in the &amp;ldquo;About&amp;rdquo; section of the &lt;em&gt;WTF&lt;/em&gt; site. &lt;em&gt;Serial&lt;/em&gt; had come out a year earlier, so your Serious Aunt was finally thinking of it as a legitimate medium. Getting &lt;em&gt;the sitting President&lt;/em&gt; was a media appearance previously reserved for 60 Minutes or the New York Times. It was the beginning of &amp;ldquo;everyone has a podcast&amp;rdquo; and also the beginning of big money investment in the medium. We got a lot out of it, and surprisingly little. Much like Spotify, once the money showed up, the power laws of internet distribution also took over. It&amp;rsquo;s hard to build a small audience for your podcast, and maybe harder than ever. I&amp;rsquo;m sad that what will fill the space left behind by WTF is likely to be either fluff or slop.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Short aside: Did you know &lt;a href=&#34;https://en.wikipedia.org/wiki/History_of_podcasting#The_first_on-demand_radio_show_and_the_first_podcast&#34;&gt;the New England Patriots arguably created the first podcast?&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I wonder a lot about the connection between &lt;em&gt;WTF&lt;/em&gt; and &lt;em&gt;The Joe Rogan Experience&lt;/em&gt;, and I&amp;rsquo;d guess Maron does too. They&amp;rsquo;re practically the same age, they used to compete for the same acting jobs and the same spots on stage. They both have an amazing capacity to be spiteful, and to hold a grudge. They started their podcasts the same year! One of them has grown as a person, one of them has become maybe the most important person in American media in a generation.  &lt;a href=&#34;https://web.archive.org/web/20250803015535/https://www.nytimes.com/2025/08/01/arts/television/marc-maron-panicked-wtf-podcast-comedy.html&#34;&gt;This New York Times piece&lt;/a&gt; reflects on the connection a bit, but there&amp;rsquo;s probably a media studies thesis to be written there.&lt;/p&gt;
&lt;h3 id=&#34;appendix&#34;&gt;Appendix&lt;/h3&gt;
&lt;p&gt;Maron has been doing a few interviews, I enjoyed &lt;a href=&#34;https://youtu.be/RDoUqyNLy2g?si=JpIe4fDOb6hx_vrU&#34;&gt;this one with Jesse Thorn&lt;/a&gt;, where Brendan McDonald calls out Marc&amp;rsquo;s intensity on initial meeting, too.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;13 January, 2026&lt;/em&gt;: Well, that answers my question about &lt;a href=&#34;https://youtu.be/tlS0k9EkNzU?si=MpGVyOHKEVR4OI-L&#34;&gt;what Maron thinks about Rogan and the show&lt;/a&gt;.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>hugo-editor: A Claude experiment</title>
      <link>/blog/2025_08_hugo-editor-a-claude-experiment/</link>
      <pubDate>Tue, 26 Aug 2025 02:52:59 +0000</pubDate>
      
      <guid>/blog/2025_08_hugo-editor-a-claude-experiment/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I write and publish this blog with &lt;a href=&#34;gohugo.io&#34;&gt;Hugo&lt;/a&gt;, a static site builder that uses YAML for its posts. It&amp;rsquo;s also what we use to build the &lt;a href=&#34;https://grafana.com/docs&#34;&gt;Grafana docs site&lt;/a&gt;, so I use it often. For the past few years I&amp;rsquo;ve been using vim to write posts. The final site content is copied onto a server after it&amp;rsquo;s built.&lt;/p&gt;
&lt;p&gt;I have an SSH client on my phone, so I can theoretically write posts from anywhere. However, using a text editor over SSH on a phone leaves a lot to be desired. There are also iOS git clients like &lt;a href=&#34;https://apps.apple.com/us/app/working-copy-git-client/id896694807&#34;&gt;Working Copy&lt;/a&gt;, but they can&amp;rsquo;t run a script to verify my builds. Before publishing, I need a separate step, and it&amp;rsquo;s not part of my writing workflow.&lt;/p&gt;
&lt;p&gt;So I decided to play with Claude to build a basic blog editor. After about an hour I had &lt;a href=&#34;https://github.com/joeblubaugh/hugo-editor&#34;&gt;hugo-editor&lt;/a&gt;, which just hosts a basic web app that interacts with the file system on a server directly. It has a simple plain-text, styled &lt;code&gt;&amp;lt;textinput&amp;gt;&lt;/code&gt; and periodically saves the contents to the file system. It also keeps a hugo development server running at all times so I can preview the entries. There&amp;rsquo;s a configurable &amp;ldquo;publish&amp;rdquo; command, which I have set up to run a hugo build and then rsync the contents to my web server.&lt;/p&gt;
&lt;p&gt;I skipped over some more complicated things like user authentication and management because I host the editor itself on my private &lt;a href=&#34;https://tailscale.com&#34;&gt;Tailscale&lt;/a&gt; network, which makes it easy to bookmark the URL on trusted devices only. If someone wants to add user handling, contributions are welcome, but right now I&amp;rsquo;m very happy with the fact that there&amp;rsquo;s no database here, just a file system. If I wanted to add password protection, I would probably just use something like &lt;a href=&#34;https://caddyserver.com/docs/caddyfile/directives/basic_auth&#34;&gt;Caddy basic auth&lt;/a&gt;, which is what I use to protect the apps that I expose for myself over the public internet.&lt;/p&gt;
&lt;p&gt;When I use Claude at work, I give it very limited freedom, and spend a lot of time in a tight loop making changes. For this, I decided to let it off the leash a bit. If you look at the code, you&amp;rsquo;ll find comments that go nowhere. For example, the Hugo development server is started, and there&amp;rsquo;s a comment &lt;code&gt;// Set up cleanup on program exit&lt;/code&gt;, followed by code that does nothing to set up cleanup.&lt;/p&gt;
&lt;p&gt;Claude decided to put everything in the main file, and use Go HTML templates for HTML rendering, which are parsed at startup time. This means the app can&amp;rsquo;t be distributed as a single binary. When I tried to convince it to &lt;code&gt;//go:embed&lt;/code&gt; the templates, it struggled mightily. It also means the code is fairly ugly. It made config flags and then never used them to actually configure the application.&lt;/p&gt;
&lt;p&gt;I would continue to be very wary of any products that proudly call themselves &amp;ldquo;vibe-coded&amp;rdquo; because of how many simple errors are in this very basic app, but I did get something genuinely useful in less than an hour.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Why would you use .gitkeep?</title>
      <link>/blog/2025_06_gitkeep/</link>
      <pubDate>Mon, 30 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>/blog/2025_06_gitkeep/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;ve been using &lt;a href=&#34;https://git-scm.com/&#34;&gt;Git&lt;/a&gt; for 9 years now, and today was the first time I knowingly encountered a file named &lt;code&gt;.gitkeep&lt;/code&gt; in a diff. &amp;ldquo;What&amp;rsquo;s the point of this empty file?&amp;rdquo;, I thought. Quickly realizing that the idea is to track an empty directory, I started to wonder &amp;ldquo;Why would you want to make sure a directory exists when there&amp;rsquo;s nothing to put there?&amp;rdquo; and I started seeing blog posts about &lt;a href=&#34;https://www.freecodecamp.org/news/what-is-gitkeep/&#34;&gt;preparing for future changes that &lt;em&gt;will&lt;/em&gt; add files to the directory&lt;/a&gt; or &lt;a href=&#34;https://www.deployhq.com/blog/understanding-keep-and-gitkeep-files-a-guide&#34;&gt;making sure your team has a consistent directory structure&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I feel like this is putting the cart before the horse. If there&amp;rsquo;s no information, there&amp;rsquo;s no reason for the directory. If your tools rely on a structure full of empty directories, those tools should be able to create the directories. If for some reason, they can&amp;rsquo;t, a set of &lt;code&gt;mkdir -p&lt;/code&gt; commands prepended to the call is all it takes.&lt;/p&gt;
&lt;p&gt;An example given in that second blog post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have a &lt;code&gt;deployment&lt;/code&gt; directory that contains configuration files or scripts, adding a &lt;code&gt;.keep&lt;/code&gt; file to it ensures that it&amp;rsquo;s included in the deployment process.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;No! Your build process should create the deployment directory, and that directory &lt;strong&gt;should be in &lt;code&gt;.gitignore&lt;/code&gt;&lt;/strong&gt;! You shouldn&amp;rsquo;t be saving an empty directory into which you might then commit deployment-generated files in error.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m honestly struggling to think of a use-case for these .keep files that isn&amp;rsquo;t better solved by $tools that generate directory structure according to a template (project setup). If you have one you think is really valuable, I&amp;rsquo;d &lt;a href=&#34;mailto:joe.blubaugh@gmail.com?subject=gitkeep%20blog%20post&#34;&gt;love an email&lt;/a&gt; to help me understand.&lt;/p&gt;
&lt;p&gt;Git made the right design decision to ignore empty directories because they&amp;rsquo;re do-nothing data.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Think big, build small</title>
      <link>/blog/2025_06_think_big_build_small/</link>
      <pubDate>Thu, 26 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>/blog/2025_06_think_big_build_small/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;ve been at a few software organizations navigating the transition from small company to established businesses with big customers. At that point, duplication starts to become a cost, and consistency and stability in the product becomes more important than earlier in the business cycle. This is when everything starts getting &amp;ldquo;platformized&amp;rdquo; and standardized: application platforms, app development frameworks, managed job queues, data pipelines. These all have the chance to be transformative for your company from a dev experience and cost control perspective. They also routinely get mired in endless migration timelines, and never being &amp;ldquo;ready enough&amp;rdquo; for big systems to transition to the platforms.&lt;/p&gt;
&lt;p&gt;A phrase I&amp;rsquo;ve been using as a mantra when approaching platform projects and questions is &amp;ldquo;think big, build small&amp;rdquo;. Thinking big means finding leverage outside of your team, finding the opportunities that will make a real difference for your coworkers and hte business. Then, keeping a &amp;ldquo;build small&amp;rdquo; focus encourages incremental delivery, adding capabilities one-at-a-time, in order of usefulness.&lt;/p&gt;
&lt;h2 id=&#34;thinking-big&#34;&gt;Thinking big&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;If you&amp;rsquo;re building for your team, ask the question &amp;ldquo;could we build for the department&amp;rdquo;? If you&amp;rsquo;re building for the department, ask &amp;ldquo;could we build for the company?&amp;rdquo; Play out those scenarios to identify opportunities, even if you choose not to tackle them.&lt;/li&gt;
&lt;li&gt;You don&amp;rsquo;t need a &amp;ldquo;platform team&amp;rdquo; to think big like this. If you find an opportunity you may end up with one, but finding these opportunities is a mark of solid and ambitious engineers, and a skill you should cultivate.&lt;/li&gt;
&lt;li&gt;Treat any platform as an internal product. Mandated adoption - at least early on - is not likely to succeed. If people &lt;em&gt;don&amp;rsquo;t want it&lt;/em&gt;, you&amp;rsquo;re not helping the company. Finding out why they don&amp;rsquo;t want it and address that. Or abandon the platform! Honorable retreat is a reasonable response to failure.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;build-small&#34;&gt;Build small:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Start with two customers, preferably within your target customer profile but fairly different from one another. One customer is too specific, and you won&amp;rsquo;t build generally enough. Three or four customers are going to have sufficiently different needs that you&amp;rsquo;re going to spend too much time building before you&amp;rsquo;re getting real usage. Teams building new systems are great candidates early on, because you can nail some features before you invest in migration tooling.&lt;/li&gt;
&lt;li&gt;Continue to prioritize active customers over theoretic ones. Don&amp;rsquo;t build for someone you hope will use the tool in 2 years.&lt;/li&gt;
&lt;li&gt;Assess often. A two-year roadmap may or may not be reasonable, but if you&amp;rsquo;re unable to make progress in 6-month intervals you&amp;rsquo;re biting off chunks that are too large. If active users and early customers are waiting two quarters after the start of work, it&amp;rsquo;s time to take a hard look at the project.&lt;/li&gt;
&lt;li&gt;Accept that &amp;ldquo;the big complex team&amp;rdquo; may never adopt a given platform. As long as the costs of that are internalized to the team rather than externalized to the company, that&amp;rsquo;s actually &lt;em&gt;fine&lt;/em&gt;. It&amp;rsquo;s an Engineering trade-off, and custom deployments are often necessary somewhere.&lt;/li&gt;
&lt;/ul&gt;

        
        </description>
    </item>
    
    <item>
      <title>Our Furtive Encrypted Future</title>
      <link>/blog/2025_06_our_furtive_encrypted_future/</link>
      <pubDate>Wed, 04 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>/blog/2025_06_our_furtive_encrypted_future/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;a href=&#34;https://therecord.media/european-commission-takes-aim-encryption-europol-fbi-proposal&#34;&gt;By 2030, real encryption may be illegal in the European Union&lt;/a&gt;. Heck, &lt;a href=&#34;https://www.wired.com/story/europe-break-encryption-leaked-document-csa-law/&#34;&gt;Spain wants to ban end-to-end encryption entirely&lt;/a&gt; It&amp;rsquo;s already &lt;a href=&#34;https://daringfireball.net/2025/02/apple_pulls_advanced_data_protection_from_the_uk&#34;&gt;under attack in the UK&lt;/a&gt;. Be sure that the USA and China will be nipping at their heels.&lt;/p&gt;
&lt;p&gt;Because mathematics are mathematics, there&amp;rsquo;s not &lt;a href=&#34;https://bigbrotherwatch.org.uk/blog/there-is-no-safe-breaking-of-encryption/&#34;&gt;actual safe way&lt;/a&gt; to provide lawful government access to encrypted communications. If the government has a key, at some point the key will leak. The shared keys for DVD CSS and AACS leaked. Any shared keys for decrypting content eventually leak, and there&amp;rsquo;s no sound way to update content to disable those private keys.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s are some likely outcomes?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Most messaging systems will eventually get hacked. Even if they aren&amp;rsquo;t most users will not know they&amp;rsquo;re being surveilled, as surveillance warrants will be secret. Assume the government is reading all your sexts.&lt;/li&gt;
&lt;li&gt;Many users move to Signal, which will be technically illegal but somehow side-loadable. Moxie Marlinspike ends up living in Brunei.&lt;/li&gt;
&lt;li&gt;Anybody with Signal installed on their phone gets special attention from the cops if they&amp;rsquo;re detained for something else.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If companies are required to do this, what are some strategies that can reduce the blast radius?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Companies retain a private key on a user-by-user basis (or even a conversation-by-conversation basis), meaning that a leaked key affects a small number of users. Von der Leyen&amp;rsquo;s keys are still worth millions, and leaking many keys is as likely as leaking a single one, in the event of a cyberattack.&lt;/li&gt;
&lt;li&gt;Users can move communications move to more obscure platforms, things like &lt;a href=&#34;https://www.theguardian.com/us-news/2022/sep/29/cia-websites-security-sources-communication-safety&#34;&gt;fake websites with strange payloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Users can &lt;em&gt;stop using the internet&lt;/em&gt;, and much illegal activity that can be monitored at a fuzzy, obscure level now, can simply not be monitored without massive boots-on-the-ground surveillance. Welcome to the old world of spycraft.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>American schools should all have uniforms</title>
      <link>/blog/2025_06_uniforms/</link>
      <pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate>
      
      <guid>/blog/2025_06_uniforms/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I love fashion as a means of self-expression. Getting creative with pieces found in thrift stores and flea markets was a big part of my 20s. I&amp;rsquo;ve been thinking a lot recently about fashion in a school context and its effects on children.&lt;/p&gt;
&lt;p&gt;My daughters are in their early years of school. Even in preschool here in Singapore, kids wear a school uniform. It&amp;rsquo;s usually simple - a polo shirt and shorts, with embroidered school logos. Everybody buys them from the same uniform supplier, and you buy a PE uniform too. State schools, international schools, religious schools. They all have uniforms. My daughters have very different tastes in their own wardrobes, but at school they&amp;rsquo;re essentially equal.&lt;/p&gt;
&lt;p&gt;I grew up wearing school uniforms, because I went to Catholic schools. For one year after we moved, in fourth grade, I attended a public school. I needed to build a &amp;ldquo;school wardrobe&amp;rdquo; quickly, going from wearing street clothes 2 days a week to 7. Having sidestepped American class-conscious fashion for my first 10 years, I was suddenly in a world where my shoes were the wrong brand, and my odd taste in t-shirts was ridiculed. Regularly. How much of the bullying is attributable to fourth graders being fourth graders - casually cruel and newly obsessed with internal hierarchy - and what of the bullying about my clothes was actually down to the clothes is hard to untangle. But it&amp;rsquo;s hard for me to see fashion in schools as anything but a distraction.&lt;/p&gt;
&lt;p&gt;Most prestigious private schools have uniforms. Many public charter schools, especially those branded as &amp;ldquo;academies&amp;rdquo; in lower-income neighborhoods, wear uniforms. Why? They help build a school identity and culture. They raise the poor and lower the rich a tiny bit. They save families money - uniforms are often inexpensive compared to street clothes.&lt;/p&gt;
&lt;p&gt;So what does &amp;ldquo;free dress&amp;rdquo; buy us in a school environment? A means of self-expression surely. Another opportunity for money to create a hierarchy.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Loog Piano: My review</title>
      <link>/blog/2024_12_loog/</link>
      <pubDate>Mon, 16 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>/blog/2024_12_loog/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I bought two of the original Loog guitars, little 3-string acoustic guitars intended for new learners and especially children. There&amp;rsquo;s a lot to like about them: they&amp;rsquo;re inexpensive, cute, and have surprisingly good sound and volume. The larger one gives a tenor guitar sort of sound that&amp;rsquo;s pleasant, and the smaller one is ukulele-ish. The kids are getting old enough to be into them, and I love having them around.&lt;/p&gt;
&lt;p&gt;So when Loog announced a piano, I was excited. The design is cute and very &amp;ldquo;Loog&amp;rdquo;, and the small 3-octave keyboard seemed like a perfect fit for the small rooms in our condo. Simple on-off with one good sound seemed really nice to me. I was used to the 70 useless presets of the old Yamahas of my youth.&lt;/p&gt;
&lt;p&gt;There was one problem with the original Loog guitar: they ran pretty late getting delivered. This isn&amp;rsquo;t uncommon on Kickstarter, of course. Especially for a first-time manufacturer, and especially with woodworking, there&amp;rsquo;s a lot to learn. Just ask the good folks at &lt;a href=&#34;https://keyboard.io&#34;&gt;keyboard.io&lt;/a&gt;, who are some of the best at providing transparent updates about contract manufacturing. It was Loog&amp;rsquo;s first kickstarter project, so I wasn&amp;rsquo;t surprised.&lt;/p&gt;
&lt;p&gt;All these years later, I figured Loog would have their ducks in a row. Boy was I wrong. The piano had numerous manufacturing and quality problems. The product features were repeatedly revised over the course of the campaign. My model didn&amp;rsquo;t even turn on when it first arrived, suggesting quite poor quality control. In the end, delivery was over 6 months past projections, and it was the sort of creeping &amp;ldquo;maybe next month!&amp;rdquo; lateness that any manager is familiar with in a poorly executed project.&lt;/p&gt;
&lt;p&gt;First, some thoughts the piano itself:&lt;/p&gt;
&lt;h2 id=&#34;quality&#34;&gt;Quality&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;/images/loog/keyboard.jpeg&#34; alt=&#34;Loog&#34;&gt;&lt;/p&gt;
&lt;p&gt;The Loog claims on its box to be &amp;ldquo;a portable, digital piano for children and grown-ups, with pro sound and design.&amp;rdquo; Let&amp;rsquo;s investigate.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll get the worst out of the way first: &lt;strong&gt;I was delivered a defective product that won&amp;rsquo;t turn on&lt;/strong&gt;, and I have no idea when I&amp;rsquo;ll be able to get a working one. More on the service quality below, but I proceeded to check out other issues with the keyboard while hoping for a replacement.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/loog/guide.jpeg&#34; alt=&#34;User guide&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the user guide. One thing I want you to notice is the pasted-on warning about what kind of charger to attach. The Loog doesn&amp;rsquo;t even come with the cheapest of wall warts that meet it&amp;rsquo;s requirements, and evidently the device is &amp;ldquo;engineered&amp;rdquo; in such a way that it can&amp;rsquo;t handle being connected to a fast charger setup. They evidently noticed this so late in the process that they needed to attach an after-thought of a warning to these cards rather than re-print something professional.&lt;/p&gt;
&lt;p&gt;The loog has a 1/8&amp;quot; stereo connector for headphones, a USB-C port that serves double-duty for charging and for use as a MIDI controller. It has two more 1/8&amp;quot; connectors for the octave shifter and sustain pedal.&lt;/p&gt;
&lt;p&gt;The fabric cover of the piano is nice enough. It&amp;rsquo;s a nylon mesh in the characteristic Loog red, though some Kickstarter backers complain that theirs came out pink. I wonder about how it would age with the hurting a kid may put on it.&lt;/p&gt;
&lt;p&gt;The wood on the keyboard is quite nice. There&amp;rsquo;s clear coat maple for the sides and the octave shifter that&amp;rsquo;s beautiful, and the dark-stained volume knob is nice and chunky with really smooth rotation. It feels great.&lt;/p&gt;
&lt;h2 id=&#34;sound&#34;&gt;Sound&lt;/h2&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;// TODO if and when I get a working keyboard. However, there are quite a few comments on the Kickstarter page
// complaining that the sound quality is nowhere near the &amp;#34;pro&amp;#34; claimed on the packaging and marketing.
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;keys&#34;&gt;Keys&lt;/h2&gt;
&lt;p&gt;The plastic of the keys is pretty atrocious, though. It&amp;rsquo;s really slick and cheap feeling. Compared to an inexpensive Akai 25-key MIDI controller, it&amp;rsquo;s pretty disappointing. Let&amp;rsquo;s also be clear, it&amp;rsquo;s not &amp;ldquo;for kids and grownups&amp;rdquo;. Unless you have pretty small hands, it&amp;rsquo;s definitely &amp;ldquo;for kids&amp;rdquo;. Smaller adults who struggle to do stretches on a full-size keyboard may benefit from that, though. I found it almost unplayably cramped.&lt;/p&gt;
&lt;p&gt;The action feels cheap. I can&amp;rsquo;t tell how good the key sensitivity is, and whether the volume response is smooth with varying force, but if I get a working one, I&amp;rsquo;ll update this.&lt;/p&gt;
&lt;h2 id=&#34;accessories&#34;&gt;Accessories&lt;/h2&gt;
&lt;p&gt;I got two significant accessories with the Loog: legs and an octave shifter to extend the three-octave range. The legs appear clever at first. They&amp;rsquo;re powder coated and come in to sections you can screw together. That means that you get 3 heights with two components. There are two big flaws. The longest version of the legs are still the size of a child-height desk, so I think by the time a kid is seven it&amp;rsquo;s already too short for them. Second, to install the legs you have to remove the black rubber pads on the bottom of the keyboard. This is an incredible design oversight - having to permanently alter the product to install some temporary legs. You&amp;rsquo;d be better off buying a keyboard stand from a music shop if you want to actually move the piano from place to place.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/loog/legs.jpeg&#34; alt=&#34;Legs&#34;&gt;&lt;/p&gt;
&lt;p&gt;The octave shifter has a great feel - a nice click when you depress it makes it obvious what you&amp;rsquo;re doing. It&amp;rsquo;s got a really attractive design and was beautifully executed. Unfortunately there&amp;rsquo;s no indication on the keyboard what octave range it&amp;rsquo;s currently in. I think the octave shifter would make a really great control for a personal electronic project using an ESP32 or similar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/loog/shifter.jpeg&#34; alt=&#34;shifter&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;production&#34;&gt;Production&lt;/h2&gt;
&lt;p&gt;So, there are quite a few disappointments from a product and quality perspective, and the production process itself did not inspire confidence, with late updates. Again, compare to someone like keyboard.io, who manage to be both timely and transparent with their updates. Even when the content of the updates is disappointing, the reasoning - and even the thought behind the reasoning - is clear and justifiable. They rarely make the same mistake twice.&lt;/p&gt;
&lt;p&gt;Manufacturing delays and production startup issues are somewhat par for the course for new Kickstarter projects, but Loog has been producing wooden products in China for over five years. That they would still have this much trouble managing a manufacturing situation is concerning to me - they&amp;rsquo;re either still naive and incompetent or simply dishonest.&lt;/p&gt;
&lt;p&gt;By the time Loog finally delivered a piano to me, they were already selling a production model co-branded with Duolingo, and the discount on the black friday piano brought it to nearly the same price as the one-year-long pre-order. So Loog got either interest or cheap capital for a year from my purchase and didn&amp;rsquo;t even actually give me a significant discount.&lt;/p&gt;
&lt;h2 id=&#34;support&#34;&gt;Support&lt;/h2&gt;
&lt;p&gt;When I first tried to contact Loog about the issues I encountered with the first piano I received, I got a promise to &amp;ldquo;follow up over email.&amp;rdquo; It took 12 days before I got a message from support, wherein they asked me to disassemble they keyboard and check the connections by hand. There&amp;rsquo;s a lithium ion battery inside, and they provided no documentation about what parts of the circuit board are &amp;ldquo;hot&amp;rdquo; or which connections that I should be checking, or the technique for checking them. They&amp;rsquo;re lucky I have an EE degree and an ohmmeter.&lt;/p&gt;
&lt;p&gt;Disassembly required removing 12 (!) screws, and the top of the volume control, because the volume pot is soldered directly to the circuit board. I don&amp;rsquo;t think it&amp;rsquo;s a good sign for product longevity to have an external control that&amp;rsquo;s going to be manipulated a lot soldered directly to the board without much other support structure. The pot isn&amp;rsquo;t even secured to the top with a nut, so you can put a lot of force on the board just by pulling the pot side-to-side. I couldn&amp;rsquo;t find any obvious problems with the wiring, so I&amp;rsquo;m currently in a holding pattern with their support. I&amp;rsquo;ll update this post with new notes as that progresses.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/loog/knob.jpeg&#34; alt=&#34;Volume knob&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;nevermind&#34;&gt;Nevermind&lt;/h2&gt;
&lt;p&gt;What was originally intended as a present to learn and play with over the summer holidays has now slipped to post-Christmas and I have no idea if I actually &lt;strong&gt;will&lt;/strong&gt; receive a working product. There are other similar complaints on the keyboard page. If I get this resolved, I&amp;rsquo;ll let you know how they addressed my concerns, but as of the date of publication I&amp;rsquo;m out $300 USD with no remedy in sight.&lt;/p&gt;
&lt;p&gt;In the end, I bought my kids (and let&amp;rsquo;s be honest, myself) a Casio. The &lt;a href=&#34;https://casiomusic.sg/products/ct-s1&#34;&gt;CT-S1&lt;/a&gt; is a competently executed version of the Loog: simple buttons, with a minimalist style in an attractive cover. It can run off of a battery and has 15 sounds. It also functions as a basic MIDI controller like the Loog. 61 keys is more than I really wanted, but it&amp;rsquo;s still compact enough. It&amp;rsquo;s cheaper than the Loog, sounds great, and best of all, actually works. I managed to source brand new one for under $90 USD on Lazada, about 1/3 the price of a Loog.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>What&#39;s a &#34;thunk&#34;?</title>
      <link>/blog/2024_12_thunks/</link>
      <pubDate>Wed, 04 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>/blog/2024_12_thunks/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I remember when everyone started talking about using &lt;a href=&#34;https://redux.js.org/usage/writing-logic-thunks&#34;&gt;&amp;ldquo;thunks&amp;rdquo; in Redux&lt;/a&gt; to execute code without blocking the UI. And I remember getting tripped up on the word &amp;ldquo;thunk&amp;rdquo;. What&amp;rsquo;s a &amp;ldquo;thunk&amp;rdquo;? Why are we calling it that?&lt;/p&gt;
&lt;p&gt;The term &amp;ldquo;thunk&amp;rdquo; goes &lt;a href=&#34;https://en.wikipedia.org/wiki/Thunk&#34;&gt;all the way back to ALGOL 60&lt;/a&gt;! It&amp;rsquo;s a play on &amp;ldquo;think&amp;rdquo;, because the ALGOL compiler needed to &amp;ldquo;think&amp;rdquo; about what sort of subroutines to generate. ALGOL allows passing expressions as arguments to subroutines, not just constants. One way of allowing this is to substitute the expression for another subroutine that evaluates the expression when the original subroutine call happens. This new subroutine is the &amp;ldquo;thunk&amp;rdquo; - the result of the &amp;ldquo;thinking&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In redux-thunk, thunks are passed as an argument to &lt;code&gt;dispatch&lt;/code&gt;, which delays their execution until after the React render cycle. This avoids blocking the UI thread when state updates need to happen. If actions need any async logic like making API calls, thunks are the way to handle it.&lt;/p&gt;
&lt;p&gt;I think Redux made a mistake using the word &amp;ldquo;thunk&amp;rdquo; here, when most programming languages have a concept of an inline function definitions and delayed execution. This sort of behavior is everywhere. In Go&amp;rsquo;s sort.Slice, you pass a thunk that gets called over and over:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;ints&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;{&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;sort&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Slice&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;ints&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ints&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;] &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ints&lt;/span&gt;[&lt;span style=&#34;color:#a6e22e&#34;&gt;j&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Python uses thunks all over, as &amp;ldquo;lambdas&amp;rdquo;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ints &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;4&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;9&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;6&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;ints&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sort(key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; x: &lt;span style=&#34;color:#f92672&#34;&gt;-&lt;/span&gt;x) &lt;span style=&#34;color:#75715e&#34;&gt;# sort in reverse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&amp;ldquo;Lambda&amp;rdquo; isn&amp;rsquo;t much better as a term for inline function definition, but at least most programmers are exposed to it. The term &amp;ldquo;thunk&amp;rdquo;, though, makes you think that you have to learn some deep new concept.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>jq -n \-\-argjson</title>
      <link>/blog/2024_12_jq_tip/</link>
      <pubDate>Mon, 02 Dec 2024 00:00:00 +0000</pubDate>
      
      <guid>/blog/2024_12_jq_tip/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;code&gt;jq&lt;/code&gt;, a tool for manipulating JSON data, is one of the &lt;del&gt;easiest&lt;/del&gt; most powerful ways to work with JSON text values in a terminal. I use it frequently. Mostly, I use it to extract values from HTTP responses:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$ http get https://api.github.com/repos/grafana/grafana | jq &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{ name: .name, stars: .stargazers_count }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;grafana&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;stars&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;65313&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The other day I wanted to use jq to combine the values from two different API calls, so I pulled up the man page and found the &lt;code&gt;--argjson&lt;/code&gt; option. Easy, I thought: just write a zsh function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;together&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  orgjson&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;http get https://api.github.com/orgs/$1&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  repojson&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;http get https://api.github.com/repos/$1/$2&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  jq --argjson org $orgjson --argjson repo $repojson &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{ org: $org.name, repo: $repo.name }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This blocks forever. It blocks forever because &lt;code&gt;jq&lt;/code&gt; is waiting on data to come in via stdin for it to process, even when it also has arguments to process. You can observe this by writing to the stdin file descriptor you find in &lt;code&gt;/prod/&amp;lt;pid&amp;gt;/fd/&lt;/code&gt;, or closing the descriptor, which will terminate &lt;code&gt;jq&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To run jq when no input is expected, use &lt;code&gt;--null-input&lt;/code&gt; or &lt;code&gt;-n&lt;/code&gt;. As the manual says:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;--null-input / -n:

Don´t read any input at all. Instead, the filter is run once using null as the input. This is
useful when using jq as a simple calculator or to construct JSON data from scratch.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add that to the function:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;together&lt;span style=&#34;color:#f92672&#34;&gt;()&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  orgjson&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;http get https://api.github.com/orgs/$1&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  repojson&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;$(&lt;/span&gt;http get https://api.github.com/repos/$1/$2&lt;span style=&#34;color:#66d9ef&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  jq -n --argjson org $orgjson --argjson repo $repojson &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;{ org: $org.name, repo: $repo.name }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we get:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ together grafana grafana
{
  &amp;#34;org&amp;#34;: &amp;#34;Grafana Labs&amp;#34;,
  &amp;#34;repo&amp;#34;: &amp;#34;grafana&amp;#34;
}
&lt;/code&gt;&lt;/pre&gt;
        
        </description>
    </item>
    
    <item>
      <title>Don&#39;t use mocks</title>
      <link>/blog/2023_06_mocks/</link>
      <pubDate>Tue, 20 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_06_mocks/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Writing good unit tests is made much easier by &lt;strong&gt;dependency injection&lt;/strong&gt;. This lets you separate your code&amp;rsquo;s behavior from that of your dependencies:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyType&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DependencyOne&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DependencyTwo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GoodConstructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DependencyOne&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;DependencyTwo&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyType&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyType&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;BadConstructor&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyType&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;MyType&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;NewDepOne&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;NewDepTwo&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The example&amp;rsquo;s a little bit contrived, because you could just directly write
&lt;code&gt;dep1&lt;/code&gt; to the struct field, but for types with complex initialization, the
testability of a type created with &lt;code&gt;GoodConstructor&lt;/code&gt; is much higher.&lt;/p&gt;
&lt;p&gt;So, what should you pass into the constructor when testing? Many people pass in
&lt;strong&gt;mock objects&lt;/strong&gt;. These are objects that provide the same API as your concrete
dependencies and provide tools for constructing responses to API calls and
assert that certain calls were made with certain arguments. A popular example
in Go is &lt;a href=&#34;https://pkg.go.dev/github.com/stretchr/testify/mock&#34;&gt;&lt;code&gt;testify/mock&lt;/code&gt;&lt;/a&gt;.
In Java, you&amp;rsquo;ll often encounter &lt;a href=&#34;https://site.mockito.org&#34;&gt;Mockito&lt;/a&gt;. Mocks look
convenient: have the framework generate a mock for you, and then script the
interactions. I&amp;rsquo;m here to argue that the cost of the convenience is too high.&lt;/p&gt;
&lt;h2 id=&#34;mocks-create-brittle-tests&#34;&gt;Mocks create brittle tests&lt;/h2&gt;
&lt;p&gt;Most mocking frameworks create an &lt;strong&gt;assertion&lt;/strong&gt; on each mock API call and then
verify these assertions after a test. You may write a test like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;TestAdd&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;testing&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;T&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NewDepOneMock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;NewDepTwoMock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dut&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;GoodConstructor&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dep2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;On&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AddOne&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Return&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dep1&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;On&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;AddOne&amp;#34;&lt;/span&gt;).&lt;span style=&#34;color:#a6e22e&#34;&gt;Return&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;dut&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Add&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;assert&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Equal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dut&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Count&lt;/span&gt;(), &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What happens in &lt;code&gt;dep1&lt;/code&gt; gets a new function &lt;code&gt;Add&lt;/code&gt; and you change your code to
use it? You break your test, even though &lt;strong&gt;no behavior in your code has
changed&lt;/strong&gt;. We shouldn&amp;rsquo;t be rewriting tests that have the exact same inputs and
expected outputs.&lt;/p&gt;
&lt;h2 id=&#34;mocks-encourage-tests-that-are-too-fine-grained&#34;&gt;Mocks encourage tests that are too fine-grained&lt;/h2&gt;
&lt;p&gt;Because your mocked calls are dependent on the current state of the Device
Under Test, you&amp;rsquo;re also tempted to assert on the internal state of your &lt;code&gt;dut&lt;/code&gt;.
These kinds of fine-grained tests are also fragile. You can&amp;rsquo;t modify the code
without modifying the test, even if the inputs and outputs are unchanged.
That&amp;rsquo;s unnecessary extra work.&lt;/p&gt;
&lt;h2 id=&#34;alternative-use-a-fake&#34;&gt;Alternative: Use a Fake&lt;/h2&gt;
&lt;p&gt;Migrating to a &amp;ldquo;Fake&amp;rdquo; is probably the simplest way to move a unit tests off of
a brittle mock. A Fake is an implementation of the dependency that isn&amp;rsquo;t
&amp;ldquo;real.&amp;rdquo; It&amp;rsquo;s less complex to set up, perhaps, or it doesn&amp;rsquo;t require a network
connection. An example might be an on-disk implementation of the Amazon S3 API.
To the caller this is indistinguishable, but it&amp;rsquo;s much easier to use in tests.&lt;/p&gt;
&lt;h2 id=&#34;alternative-dont-unit-test-calls-to-complex-dependencies-write-integration-and-behavioral-tests-for-multiple-components&#34;&gt;Alternative: Don&amp;rsquo;t unit test calls to complex dependencies. Write integration and behavioral tests for multiple components&lt;/h2&gt;
&lt;p&gt;One reason we don&amp;rsquo;t use complex dependencies (even from other packages in the
same codebase) is to avoid testing the code of the dependencies. That shouldn&amp;rsquo;t
be a unit test&amp;rsquo;s responsibility.&lt;/p&gt;
&lt;p&gt;But an integration test &lt;strong&gt;does&lt;/strong&gt; care about the code in other packages. That&amp;rsquo;s
the right place to test complex interactions with dependencies.&lt;/p&gt;
&lt;h2 id=&#34;what-about-stubs&#34;&gt;What about Stubs?&lt;/h2&gt;
&lt;p&gt;In most definitions, a Stub is like a mock that doesn&amp;rsquo;t do assertions. This can
be less fragile than a mock - there aren&amp;rsquo;t assertions that break if the number of calls change - but it is still a
programmable double of a dependency. It has many of the same problems with
brittleness. Internal logic tests can break tests in the absence of observable
external changes.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Supreme Court DoS attack</title>
      <link>/blog/2023_02_supreme_court_dos/</link>
      <pubDate>Wed, 22 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_02_supreme_court_dos/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;In the latest edition of Casey Newton&amp;rsquo;s newsletter, Platformer, he makes the case for &lt;a href=&#34;https://www.platformer.news/p/the-people-who-want-to-end-section&#34;&gt;why the plaintiffs in Gonzalez vs Google botched their oral arguments&lt;/a&gt;. The mechanism is fascinating:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[H]ow many &amp;hellip; lawyers were conflicted out of representing Gonzalez.
Platforms pay so many high-powered lawyers at so many DC firms that
plaintiffs had to recruit an 80-year-old man who once argued a case before an
appointee of Franklin Roosevelt.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;They DoSed the Supreme Court! I&amp;rsquo;m on Google&amp;rsquo;s side in this particular case, so, &amp;ldquo;good&amp;rdquo;. But this isn&amp;rsquo;t a tactic that should work.&lt;/p&gt;
&lt;p&gt;PS: Platformer is good, consider subscribing!&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Looking in Go&#39;s Mirror: How and When to use `reflect`</title>
      <link>/blog/2023_01_reflection/</link>
      <pubDate>Mon, 16 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_01_reflection/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href=&#34;https://gopheradvent.com/calendar/2022/looking-in-gos-mirror/&#34;&gt;gopheradvent.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Go&amp;rsquo;s static typing is a headline feature of the language. It prevents whole classes of bugs, makes code easier to navigate and refactor, and makes it easier for linters to analyze. But there are times when it&amp;rsquo;s very constricting. What if we&amp;rsquo;re reading JSON files from disk with unknown structure? We can&amp;rsquo;t define a type ahead of time that will cover all cases. The &lt;code&gt;reflect&lt;/code&gt; package gives us the power to handle this situation, and to do much more. We can write functions that handle arbitrary types. &lt;code&gt;reflect&lt;/code&gt; is also the technology behind Go&amp;rsquo;s &amp;ldquo;magical&amp;rdquo; struct tags, which are often used in serialization. &lt;code&gt;reflect&lt;/code&gt; can be intimidating to new Go programmers because it&amp;rsquo;s very generic and you lose access to many niceties in the language. But it doesn&amp;rsquo;t have to be. Let&amp;rsquo;s build some programs that use &lt;code&gt;reflect&lt;/code&gt; as a way to demystify the package and illustrate the power and pitfalls that come with using it.&lt;/p&gt;
&lt;h2 id=&#34;reflect-package-basics&#34;&gt;&lt;code&gt;reflect&lt;/code&gt; package basics&lt;/h2&gt;
&lt;h3 id=&#34;value-type-and-kind&#34;&gt;Value, Type, and Kind&lt;/h3&gt;
&lt;p&gt;The fundamental concepts of reflection are the &lt;code&gt;Value&lt;/code&gt; and &lt;code&gt;Type&lt;/code&gt; structs, and the key starting points are the &lt;code&gt;ValueOf&lt;/code&gt; and &lt;code&gt;TypeOf&lt;/code&gt; functions. You can use the &lt;code&gt;TypeOf&lt;/code&gt; function to get the &lt;code&gt;Type&lt;/code&gt; of any variable in Go; &lt;code&gt;ValueOf&lt;/code&gt; provides access to the underlying value, along with the type information.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;8.4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt;{ &lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt;}{ &lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1.2&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TypeOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;y&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;TypeOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;z&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// Prints:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//  float64&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//  struct { Field float64 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//  {1.2}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//  struct { Field float64 }&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;//  {1.2}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://goplay.tools/snippet/wBwGPWJsZnJ&#34;&gt;Try it yourself&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Every &lt;code&gt;Type&lt;/code&gt; and &lt;code&gt;Value&lt;/code&gt; also has a &lt;code&gt;Kind&lt;/code&gt; that provides the building block for handling arbitrary data structures with &lt;code&gt;reflect&lt;/code&gt;. The &lt;code&gt;Kind()&lt;/code&gt; function returns one of Go&amp;rsquo;s primitive types like &lt;code&gt;Float64&lt;/code&gt;, &lt;code&gt;Map&lt;/code&gt;, &lt;code&gt;Array&lt;/code&gt;, and &lt;code&gt;Struct&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&#34;settability&#34;&gt;Settability&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;reflect&lt;/code&gt; also lets us modify the &lt;code&gt;Value&lt;/code&gt; that we have as long as the value is &amp;ldquo;settable&amp;rdquo;. It&amp;rsquo;s convenient to think of &amp;ldquo;settability&amp;rdquo; as similar to &amp;ldquo;is a pointer&amp;rdquo;. &lt;code&gt;reflect&lt;/code&gt; needs a pointer at the value it&amp;rsquo;s referring to if it wants to modify it. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt; = &lt;span style=&#34;color:#ae81ff&#34;&gt;8.4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;v is settable:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CanSet&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pointer is settable:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CanSet&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Elem&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;pointer&amp;#39;s value is settable:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;CanSet&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetFloat&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;6.1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;x:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;x&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://goplay.tools/snippet/YuJldNOG4Td&#34;&gt;Try it on goplay.tools&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;OK, let&amp;rsquo;s look at some programs using &lt;code&gt;reflect&lt;/code&gt;! These programs will be missing some checks and cases in the interest of brevity. Don&amp;rsquo;t base your production code on this!&lt;/p&gt;
&lt;h2 id=&#34;deserializing-structured-data-with-reflect&#34;&gt;Deserializing structured data with &lt;code&gt;reflect&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Suppose you have a file with JSON in it, but you don&amp;rsquo;t know its structure:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Joe Blubaugh&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;location&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Singapore&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;job&amp;#34;&lt;/span&gt;: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;company&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Grafana&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;title&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Software Engineer&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  } 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;json.Unmarshal&lt;/code&gt; function uses reflection to deserialize data when it&amp;rsquo;s passed an &lt;code&gt;any&lt;/code&gt; value as the destination. We can also inspect the result with &lt;code&gt;reflect&lt;/code&gt;. The &lt;a href=&#34;https://goplay.tools/snippet/xu---vyeyH5&#34;&gt;full example is available on goplay.tools&lt;/a&gt;, and the key element for our analysis is a function that recursively processes a reflect.Value according to the &lt;code&gt;Kind&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Interface&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Elem&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Map&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;indentIt&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Map\n&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MapRange&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Next&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;indentIt&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Key: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;().&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;indentIt&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Value: &amp;#34;&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;visit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;), &lt;span style=&#34;color:#a6e22e&#34;&gt;indent&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;\n\n&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;String&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The switch on the kind of that &lt;code&gt;Value&lt;/code&gt; tells us how to handle it. If it&amp;rsquo;s a map, we iterate over the keys and visit each value, and if it&amp;rsquo;s an interface we dereference it to get the underlying value before handling it. Most kinds are left out here for brevity, since the data supplied only has JSON objects and string.&lt;/p&gt;
&lt;p&gt;How does &lt;code&gt;Unmarshal&lt;/code&gt; create the right types when building up the structure here? As it scans the JSON, it chooses appropriate types based on the structure of the data, as &lt;a href=&#34;https://cs.opensource.google/go/go/&amp;#43;/refs/tags/go1.19.3:src/encoding/json/decode.go;l=22;drc=3a7a528c2d7ee0c7b2988a7aee0b9347e973cbed;bpv=0;bpt=1&#34;&gt;documented here&lt;/a&gt;. It uses the &lt;code&gt;MakeMap&lt;/code&gt;, &lt;code&gt;MakeSlice&lt;/code&gt;, and &lt;code&gt;New&lt;/code&gt; functions to create each &lt;code&gt;Value&lt;/code&gt; and assign the parsed data to them. Using those functions, we can build any data structure we want at runtime without a prior definition. We can even build new structs without a predeclared type definition by using the &lt;code&gt;StructOf&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re clever with reflection, that means that you can parse a schema at runtime and then validate whether any data matches that schema. The JSON below is an example of our schema. It has a value for every required field, with the correct type set.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Street Name&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;City&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;11111&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;age_days&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;height_cm&amp;#34;&lt;/span&gt;:	&lt;span style=&#34;color:#ae81ff&#34;&gt;1.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and here is a structure that &lt;strong&gt;doesn&amp;rsquo;t&lt;/strong&gt; match our schema because of a missing field.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;name&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Joe Blubaugh&amp;#34;&lt;/span&gt;, 
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;address&amp;#34;&lt;/span&gt;: [&lt;span style=&#34;color:#ae81ff&#34;&gt;101&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Foo Street&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Bazville&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#ae81ff&#34;&gt;10101&lt;/span&gt;],
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;height_cm&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;180.1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This program can parse the schema and validate other input data by checking that the &lt;code&gt;Type&lt;/code&gt; for each field matches and that there are no missing fields in the input data. Notice how similar the &lt;code&gt;validate&lt;/code&gt; function is to the &lt;code&gt;visit&lt;/code&gt; function that we used to analyze the structure of arbitrary JSON.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// This program loads a schema defined in JSON and validates other JSON &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// against the schema. Every field in the schema is required in the data, &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// and the order and type of values in any JSON arrays must also match.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;encoding/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reflect&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;schema&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt; = []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;name&amp;#34;: &amp;#34;foo&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;address&amp;#34;: [123, &amp;#34;Street Name&amp;#34;, &amp;#34;City&amp;#34;, 11111],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;age_days&amp;#34;: 1,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;height_cm&amp;#34;:	1.0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// This data does not match the schema because of a missing &amp;#34;age_days&amp;#34; field.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;invalidData&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt; = []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;name&amp;#34;: &amp;#34;Joe Blubaugh&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;address&amp;#34;: [101, &amp;#34;Foo Street&amp;#34;, &amp;#34;Bazville&amp;#34;, 10101],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;height_cm&amp;#34;: 180.1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#75715e&#34;&gt;// This data is valid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;validData&lt;/span&gt; []&lt;span style=&#34;color:#66d9ef&#34;&gt;byte&lt;/span&gt; = []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;{ 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;name&amp;#34;: &amp;#34;Baby Blubaugh&amp;#34;, 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;address&amp;#34;: [101, &amp;#34;Foo Street&amp;#34;, &amp;#34;Bazville&amp;#34;, 10101],
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;height_cm&amp;#34;: 39,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;  &amp;#34;age_days&amp;#34;: 91
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;schema&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;invalidData&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Invalid data passed validation?&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;validData&lt;/span&gt;, &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Valid data passed validation?&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;schema&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;schema&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;data&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;sKind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;dKind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;sKind&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dKind&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;switch&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Elem&lt;/span&gt;(), &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Elem&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Map&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// Do both maps have the same length? If not, the data can&amp;#39;t be valid.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; len(&lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MapKeys&lt;/span&gt;()) &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; len(&lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MapKeys&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MapRange&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Next&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Key&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;sValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;iter&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Value&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;dValue&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;MapIndex&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;key&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;dValue&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;IsZero&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#75715e&#34;&gt;// The key isn&amp;#39;t present in the data:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sValue&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dValue&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;case&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Slice&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// Our schema is restrictive: the slice *must* be present in both structures, &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// they must have the same length, and the same order of data types.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Len&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Len&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Len&lt;/span&gt;(); &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;sVal&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;s&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Index&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;dVal&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Index&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; !&lt;span style=&#34;color:#a6e22e&#34;&gt;validate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;sVal&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dVal&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;				&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// Skip reflect.Struct because the code to iterate over fields is complex, &lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// and json Unmarshalling into `any` never produces a struct.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#75715e&#34;&gt;// If types match for primitive values like ints, then this is valid.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://goplay.tools/snippet/PiEKLDnMR0r&#34;&gt;Play around yourself&lt;/a&gt;, modifying the schema and the data to understand better how that works.&lt;/p&gt;
&lt;h2 id=&#34;using-struct-tags-to-access-metadata&#34;&gt;Using struct tags to access metadata&lt;/h2&gt;
&lt;p&gt;In Go we often use struct tags on data transfer objects: HTTP body data, structs that are mapped to database tables, etc. &lt;code&gt;reflect&lt;/code&gt; gives you access to those tags. Suppose we want to redact some fields (like a credit card number) from a struct before sending it to other functions. We could write this program using tags:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;errors&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;reflect&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PaymentForm&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueCents&lt;/span&gt;       &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;SKU&lt;/span&gt;              &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;CreditCardNumber&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`redact:&amp;#34;true&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;PaymentForm&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueCents&lt;/span&gt;:       &lt;span style=&#34;color:#ae81ff&#34;&gt;10_00&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;SKU&lt;/span&gt;:              &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;abe15f59&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;CreditCardNumber&lt;/span&gt;: &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;5555555555555555&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unredacted:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;redact&lt;/span&gt;(&lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		panic(&lt;span style=&#34;color:#a6e22e&#34;&gt;err&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Redacted:&amp;#34;&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;f&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;redact&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;any&lt;/span&gt;) &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;ValueOf&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;val&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Pointer&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errors&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Must pass a struct pointer to be set.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Elem&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Kind&lt;/span&gt;() &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;reflect&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;errors&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;New&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Must pass a struct pointer to be set.&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Type&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;; &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt; &amp;lt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;NumField&lt;/span&gt;(); &lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;++&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;v&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;t&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;i&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;shouldRedact&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Tag&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Get&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;redact&amp;#34;&lt;/span&gt;) &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;shouldRedact&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#75715e&#34;&gt;// This is simplified to assume that all redacted fields are strings.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		 	&lt;span style=&#34;color:#75715e&#34;&gt;// That&amp;#39;s just for brevity in this example.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;			&lt;span style=&#34;color:#a6e22e&#34;&gt;value&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;SetString&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;XXXXX&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://goplay.tools/snippet/xmvCGT5P7uP&#34;&gt;The full program is also here on the playground&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;lets-not-overdo-it&#34;&gt;Let&amp;rsquo;s not overdo it&lt;/h2&gt;
&lt;p&gt;If you get &lt;strong&gt;really&lt;/strong&gt; creative with reflect you can do all sorts of things. It becomes possible to write partially applied functions and implement other functional programming primitives. &amp;ldquo;Clever&amp;rdquo; programming is an awkward fit for Go, though. When you use reflect, your code can quickly become complex and recursive, which can make it harder to maintain and harder to explain to your teammates.&lt;/p&gt;
&lt;p&gt;Reflection also impacts your program&amp;rsquo;s performance. Accessing type information for a value is slow compared to code generated by the compiler. Even type assertions at compile time are significantly faster than similar code using &lt;code&gt;reflect&lt;/code&gt;, so it&amp;rsquo;s best to use the package only when you can&amp;rsquo;t use the language&amp;rsquo;s ordinary type handling features like interfaces and type assertions.&lt;/p&gt;
&lt;p&gt;All that being said, reflection provides an essential tool that we should all be familiar with: handling data when we can&amp;rsquo;t predict its structure at build time. That makes it really essential when handling byte streams from reading data off of disk or as the result of network calls.&lt;/p&gt;
&lt;h2 id=&#34;additional-reading&#34;&gt;Additional reading&lt;/h2&gt;
&lt;p&gt;Rob Pike has an excellent blog post called &lt;a href=&#34;https://go.dev/blog/laws-of-reflection&#34;&gt;The Laws of Reflection&lt;/a&gt; that expands on the basics above, providing more detail about the underlying implementation of interface types the reflect package uses to do its thing.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>What is software for?</title>
      <link>/blog/2023_01_software_for/</link>
      <pubDate>Fri, 13 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_01_software_for/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;In the &lt;a href=&#34;../2023_01_code_prose&#34;&gt;last blog&lt;/a&gt;, I wrote a few things that software is for:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;putting people on the moon&lt;/li&gt;
&lt;li&gt;safely deploying airbags&lt;/li&gt;
&lt;li&gt;making my bank account add up&lt;/li&gt;
&lt;li&gt;making sure Mom gets my texts&lt;/li&gt;
&lt;li&gt;video editing and animation&lt;/li&gt;
&lt;li&gt;drawing in 10 million colors&lt;/li&gt;
&lt;li&gt;ebikes&lt;/li&gt;
&lt;/ul&gt;&lt;/blockquote&gt;
&lt;p&gt;But it&amp;rsquo;s also used for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;surveilling protesters&lt;/li&gt;
&lt;li&gt;bombing weddings&lt;/li&gt;
&lt;li&gt;tracking Uyghurs&lt;/li&gt;
&lt;li&gt;helping Nazis track Jewish people&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We often fail to reckon with this as an industry. (And yes, IBM punch cards are very much part of our history). Actively giving the Nazis an extra data column for &amp;ldquo;Jewish? (y/n):&amp;rdquo; to win business is just unbelievably craven.&lt;/p&gt;
&lt;p&gt;Much of this software is developed by nation states, or their wholly-dependent defense contractor subsidiaries, but some of it&amp;rsquo;s not. Consider NSO Group, who has sold their hacks to anyone with an open wallet. How many people are dead because of them? How many people are arrested because of awful ShotSpotter systems, &amp;ldquo;predictive&amp;rdquo; crime AI, and faulty face recognition?&lt;/p&gt;
&lt;p&gt;And even if software is state-sponsored, some professional designed and wrote that software under whatever ethical umbrella they constructed for themselves.&lt;/p&gt;
&lt;p&gt;Consider very carefully how much data you need to have; be even more careful about what you build and for who.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Code is not Prose</title>
      <link>/blog/2023_01_code_prose/</link>
      <pubDate>Thu, 12 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_01_code_prose/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Once a month or so, &lt;a href=&#34;https://hachyderm.io/@chetHendrickson@agilealliance.social/109671373341301581&#34;&gt;this idea&lt;/a&gt; comes rambling out of the programming community&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Software is prose.  It is written to communicate ideas to others, it has the interesting side effect that it can be transformed into something a computer can execute.&lt;br&gt;
&lt;small&gt;- Chet Hendrickson&lt;/small&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This sounds nice on paper. It makes for a good conference talk about how to structure software, how to design programming languages, and how to collaborate. I agree with what I &lt;em&gt;think&lt;/em&gt; is the intent: write software so that other people can understand it and change it as they need to.&lt;/p&gt;
&lt;p&gt;But it is &lt;strong&gt;dead&lt;/strong&gt; wrong.&lt;/p&gt;
&lt;p&gt;Software is for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;putting people on the moon&lt;/li&gt;
&lt;li&gt;safely deploying airbags&lt;/li&gt;
&lt;li&gt;making my bank account add up&lt;/li&gt;
&lt;li&gt;making sure Mom gets my texts&lt;/li&gt;
&lt;li&gt;video editing and animation&lt;/li&gt;
&lt;li&gt;drawing in 10 million colors&lt;/li&gt;
&lt;li&gt;ebikes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Software is &lt;strong&gt;not&lt;/strong&gt; primarily for communicating with people. It is for creating tools to accomplish things. That can be tools for people to use, tools for other software to use, or tools that just produce things for the hell of it. That&amp;rsquo;s not a &lt;em&gt;side effect&lt;/em&gt;, it&amp;rsquo;s the whole deal.&lt;/p&gt;
&lt;p&gt;Does that mean code can&amp;rsquo;t be beautiful? Can&amp;rsquo;t communicate more than the program that it generates? No, of course not. Consider the famous brainfuck hello world:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;gt;++++++++[&amp;lt;+++++++++&amp;gt;-]&amp;lt;.&amp;gt;++++[&amp;lt;+++++++&amp;gt;-]&amp;lt;+.+++++++..+++.&amp;gt;&amp;gt;++++++[&amp;lt;+++++++&amp;gt;-]&amp;lt;+
+.------------.&amp;gt;++++++[&amp;lt;+++++++++&amp;gt;-]&amp;lt;+.&amp;lt;.+++.------.--------.&amp;gt;&amp;gt;&amp;gt;++++[&amp;lt;++++++++&amp;gt;-
]&amp;lt;+.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s art. It communicates the abstract and arbitrary nature &lt;em&gt;of communication&lt;/em&gt; via the code. It&amp;rsquo;s interesting to note that the code itself is the tool, but it&amp;rsquo;s not prose. It&amp;rsquo;s a puzzle.&lt;/p&gt;
&lt;p&gt;You read software more than you write it, so make it readable. But don&amp;rsquo;t get caught up in the idea that you&amp;rsquo;re writing or editing a novel.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;I know that Chet has done more for software development than I&amp;rsquo;m ever likely to, but sometimes Agile drives you into absurdity.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        
        </description>
    </item>
    
    <item>
      <title>Nasty refactorings with go.mod replace</title>
      <link>/blog/2023_01_go_mod_replace/</link>
      <pubDate>Wed, 11 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_01_go_mod_replace/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;At work I&amp;rsquo;ve been building a new program on top of an SDK that&amp;rsquo;s under very active development. After about 6 weeks without updating the version, the SDK had deleted some code I was using and had a ton of breaking changes. If I&amp;rsquo;d simply updated the library, my entire program would fail to build, and it &lt;em&gt;also&lt;/em&gt; wouldn&amp;rsquo;t be clear how to get it back to a good state. Following in the spirit of &lt;a href=&#34;https://martinfowler.com/bliki/BranchByAbstraction.html&#34;&gt;Branch by Abstraction&lt;/a&gt;, I&amp;rsquo;d rather introduce the new code side-by-side with the old and incrementally migrate without breaking the program.&lt;/p&gt;
&lt;p&gt;I work in Go, where our import statements look like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;github&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;com&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;grafana&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;grafana&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;github&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;com&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;joeblubaugh&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;lib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;import&lt;/code&gt; statements use package names, and if the packages are URLs, then &lt;code&gt;go build&lt;/code&gt; will try and download modules by doing a &lt;code&gt;git clone&lt;/code&gt; based on the package&amp;rsquo;s URL. Go&amp;rsquo;s dependency management uses &lt;code&gt;require&lt;/code&gt; statements to describe the dependency tree:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;require github.com/joeblubaugh/lib v0.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So, let&amp;rsquo;s say I need to update from &lt;code&gt;v0.1.0&lt;/code&gt; to &lt;code&gt;v0.5.0&lt;/code&gt;, but I need to keep the &lt;code&gt;v0.1.0&lt;/code&gt; code around. I can&amp;rsquo;t have duplicate module names in &lt;code&gt;go.mod&lt;/code&gt;, but I can play a sneaky trick with &lt;code&gt;replace&lt;/code&gt; directives:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;require (
  github.com/joeblubaugh/lib v0.1.0
  joe/upgraded               v0.0.1
)

replace joe/upgraded v0.0.1 =&amp;gt; github.com/joeblubaugh/lib v0.5.0
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By introducing a &lt;em&gt;fake&lt;/em&gt; module name and a replace directive, I can import code from two different versions into the same program and incrementally move over to the new package:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;github&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;com&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;joeblubaugh&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;lib&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#a6e22e&#34;&gt;joe&lt;/span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;/&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;upgraded&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
        
        </description>
    </item>
    
    <item>
      <title>Caroline Ellison is not a sympathetic figure</title>
      <link>/blog/2023_01_ellison/</link>
      <pubDate>Wed, 04 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>/blog/2023_01_ellison/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I recently read this &lt;a href=&#34;https://www.washingtonpost.com/business/2023/01/02/caroline-ellison-ftx/&#34;&gt;Washington Post Article&lt;/a&gt; (&lt;a href=&#34;https://web.archive.org/web/20230104124557/https://www.washingtonpost.com/business/2023/01/02/caroline-ellison-ftx/&#34;&gt;archive.org&lt;/a&gt;) titled &lt;em&gt;Caroline Ellison wanted to make a difference. Now she&amp;rsquo;s facing prison.&lt;/em&gt; After the first read-through I was incredulous at the sympathetic presentation in the biography. It reads like a resume, like a &amp;ldquo;it all spun out of control!&amp;rdquo; story about someone who got in over her head. &lt;a href=&#34;https://twitter.com/JoeBlubaugh/status/1610093266757877760&#34;&gt;I tweeted&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Lol how do you get such a sympathetic bio after stealing billions of dollars @GerritD&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;I thought I&amp;rsquo;d elaborate on my reaction to the article, and what I think is wrong with the approach. First, it puts a nice gloss on Ellison&amp;rsquo;s CV and her stated motivations. Regarding herself and Sam Bankman-Fried:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Both were children of accomplished academics, studied math at prestigious universities and touted the importance of giving money away to make the world a better place.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This is the first thing we learn about Caroline Ellison, in the second paragraph of the article. That&amp;rsquo;s &lt;em&gt;before&lt;/em&gt; we even learn that she&amp;rsquo;s pled guilty to lying to investors. Combine that with the headline. If you didn&amp;rsquo;t know who she was, the headline and first two paragraphs read like a story about a political activist tripped up by a justice system that opposes what she&amp;rsquo;s trying to do.&lt;/p&gt;
&lt;p&gt;Are the paragraphs factually accurate? Yes. She &amp;ldquo;touted&amp;rdquo; effective altruism. I think the paragraph also implies that she &lt;em&gt;believed in&lt;/em&gt; effective altruism; I don&amp;rsquo;t think there&amp;rsquo;s evidence for that, and I don&amp;rsquo;t think the article interrogates her touting of it, contrasted with her actions.&lt;/p&gt;
&lt;p&gt;After a few paragraphs about her and Gary Wang&amp;rsquo;s guilty pleas, we get into the part that really got me going: &lt;em&gt;The math whiz&lt;/em&gt;. We get six paragraphs about this bright rising star who learned to read early, got into top universities, did internships at Jane Street. What relevance does this have to &lt;em&gt;why she&amp;rsquo;s facing prison&lt;/em&gt;? What does it tell us about &lt;em&gt;wanting to make a difference?&lt;/em&gt; If you want to write about a crypto criminal, you should do it the way &lt;a href=&#34;https://www.npr.org/sections/alltechconsidered/2013/10/03/228579712/the-man-behind-the-shadowy-illicit-drug-market-silk-road&#34;&gt;NPR did Dread Pirate Roberts&lt;/a&gt;. You write about the crimes first. You give us the person&amp;rsquo;s history in the middle end, and you don&amp;rsquo;t make it sound like a yearbook bio.&lt;/p&gt;
&lt;p&gt;More paragraphs. About how exciting it was to run Alameda Research. About how rich she got. About how she promoted effective altruism. (At no point does the article present evidence of charitable giving or its lack.)&lt;/p&gt;
&lt;p&gt;Over 3/4 of the way through the story, we finally get a mention that &amp;ldquo;FTX was allegedly breaking the law.&amp;rdquo; The only crime mentioned is that Ellison, Wang and Bankman-Fried lied to investors about &amp;ldquo;Alameda&amp;rsquo;s practices.&amp;rdquo; There&amp;rsquo;s ample evidence that the crimes are substantially more serious - not that customer funds were used to pay back loans, but that customer funds were likely embezzled and used to pay for that mansion in the Bahamas and the rest of the executives&amp;rsquo; luxurious lifestyle.&lt;/p&gt;
&lt;p&gt;So, there are two or four paragraphs that directly address the crimes she&amp;rsquo;s pleaded guilty to, and over 20 that discuss her early life, rise to wealth, etc in fairly glowing terms. If I get nicked for up to 110 years of crimes, I hope I&amp;rsquo;ll get such gentle treatment.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Unmarshaling JSON in Go: The weird parts</title>
      <link>/blog/2022_12_unmarshal/</link>
      <pubDate>Thu, 15 Dec 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_12_unmarshal/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Quick, what does this program print?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;package&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f92672&#34;&gt;import&lt;/span&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;encoding/json&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;fmt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;func&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; []byte(&lt;span style=&#34;color:#e6db74&#34;&gt;`{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	&amp;#34;fieldone&amp;#34;:&amp;#34;zero&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	&amp;#34;field_one&amp;#34;:&amp;#34;one&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	&amp;#34;fiELD_one&amp;#34;:&amp;#34;two&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	&amp;#34;field_One&amp;#34;:&amp;#34;three&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;	&amp;#34;field2&amp;#34;:123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;}`&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d0&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Fieldone&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field_One&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Fieldone&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;fiELD_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field_One&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_One&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Fieldone&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;fiELD_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field_One&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d3&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Fieldone&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;fiELD_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;		&lt;span style=&#34;color:#a6e22e&#34;&gt;Field_One&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;dZero&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d0&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;dOne&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d1&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;dTwo&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d2&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;dThree&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;:=&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;d3&lt;/span&gt;{}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dZero&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dZero&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dOne&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dOne&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dTwo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dTwo&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;_&lt;/span&gt; = &lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Unmarshal&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;content&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;dThree&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;fmt&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Println&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;dThree&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;gt; go run main.go
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;zero  three&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;one two three&lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt; three &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;amp;&lt;span style=&#34;color:#f92672&#34;&gt;{&lt;/span&gt;three two &lt;span style=&#34;color:#f92672&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What the heck!? What&amp;rsquo;s going on with &lt;code&gt;&amp;amp;{zero  three}&lt;/code&gt;? Go uses a variety of
hints to try and map JSON keys to struct names, and the rules, while
deterministic, aren&amp;rsquo;t always intuitive and can lead to some strange results.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s work out the rules from most specific to most general.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Try to map the JSON key directly to a struct tag, with case sensitivity. Call this &amp;ldquo;strict&amp;rdquo; matching.&lt;/li&gt;
&lt;li&gt;Try to map the JSON key directly to a struct tag, without case sensitivity. Call this &amp;ldquo;loose&amp;rdquo; matching.&lt;/li&gt;
&lt;li&gt;Try to map the JSON key to a field name, case sensitively.&lt;/li&gt;
&lt;li&gt;Try to map the JSON key to a field name, case insensitively.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you look back through the examples, you can see how this works, with one wrinkle: duplicated struct tags. That&amp;rsquo;s part of what causes &lt;code&gt;&amp;amp;{ three }&lt;/code&gt;: &lt;code&gt;Fieldone&lt;/code&gt; and &lt;code&gt;Field_One&lt;/code&gt; both have the same struct tags, so &lt;em&gt;neither of them wins&lt;/em&gt; and the data is discarded. Hm. But why is the value &lt;code&gt;three&lt;/code&gt; and not &lt;code&gt;two&lt;/code&gt;? Surely &lt;code&gt;&amp;quot;fiELD_one&amp;quot;&lt;/code&gt; is a better match than &lt;code&gt;&amp;quot;field_One&amp;quot;&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;The other tricky part is key ordering, and case sensitive matching. The JSON is processed a key at a time, mapped into struct fields. Whether a struct field was previously filled or not, doesn&amp;rsquo;t matter. So let&amp;rsquo;s look at the &lt;code&gt;d2&lt;/code&gt; struct:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d2&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Fieldone&lt;/span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Field1&lt;/span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;fiELD_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Field_One&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We already know the first and last fields won&amp;rsquo;t get any data because they have the same struct tag. The data is deserialized in this order:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;field_one&amp;#34;:&amp;#34;one&amp;#34;,
&amp;#34;fiELD_one&amp;#34;:&amp;#34;two&amp;#34;,
&amp;#34;field_One&amp;#34;:&amp;#34;three&amp;#34;,
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;ALL&lt;/strong&gt; of these match with &lt;code&gt;&amp;quot;fiELD_one&amp;quot;&lt;/code&gt;, and for each key that&amp;rsquo;s the field that matches best. If you shuffle the order of these keys, you&amp;rsquo;ll get a different outcome.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;&amp;#34;field_One&amp;#34;:&amp;#34;three&amp;#34;,
&amp;#34;fiELD_one&amp;#34;:&amp;#34;two&amp;#34;,
&amp;#34;field_one&amp;#34;:&amp;#34;one&amp;#34;,
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;gives us &lt;code&gt;&amp;amp;{ one }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And this is just with strings! What if you create a struct with two fields of different types that could be mapped to the data?&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;S&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;  &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_one&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;F&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field_One&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;try unmarshalling this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#f92672&#34;&gt;&amp;#34;field_ONE&amp;#34;&lt;/span&gt;: &lt;span style=&#34;color:#ae81ff&#34;&gt;123.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and you&amp;rsquo;ll get an error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;json&lt;/span&gt;: &lt;span style=&#34;color:#a6e22e&#34;&gt;cannot&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;unmarshal&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;number&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;into&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Go&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;field&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;d&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;field_one&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;of&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code isn&amp;rsquo;t kind enough to check for other fields that might have a field name that matches &amp;ldquo;loosely&amp;rdquo; AND has the right type, it just takes the first &amp;ldquo;loose&amp;rdquo; match it can find.&lt;/p&gt;
&lt;p&gt;Well, fine, but how often do problems like this actually happen? All you have to do is make sure that all the fields in your struct have different struct tags, right?&lt;/p&gt;
&lt;p&gt;Right?&lt;/p&gt;
&lt;p&gt;Yes, that&amp;rsquo;s true. But it&amp;rsquo;s not always obvious if you have unique struct tags, or struct tags that are unique in the &amp;ldquo;loose&amp;rdquo; sense. Consider struct embedding:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;float64&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;C&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;A&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;B&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Field&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt; &lt;span style=&#34;color:#e6db74&#34;&gt;`json:&amp;#34;field&amp;#34;`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s a perfectly legal struct - each of the &lt;code&gt;Field&lt;/code&gt;s is namespaced out of each other&amp;rsquo;s way. But that&amp;rsquo;s not the way the JSON decoder sees it. Try and deserialize into this and you&amp;rsquo;ll get an empty struct every time.&lt;/p&gt;
&lt;p&gt;So, some lessons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Normalize your JSON input, if that works with your API semantics. All lower case is just fine, keeping &lt;code&gt;_&lt;/code&gt; and &lt;code&gt;-&lt;/code&gt;. This can help with unexpected &amp;ldquo;loose&amp;rdquo; matching.&lt;/li&gt;
&lt;li&gt;Always set JSON struct tags for ser/des types. This also helps control &amp;ldquo;loose&amp;rdquo; matching and makes sure the data ends up in the field you expect it to go to.&lt;/li&gt;
&lt;li&gt;Avoid struct embedding for ser/des types. You never know when an embedded struct could change under you. It&amp;rsquo;s &amp;ldquo;spooky action at a distance&amp;rdquo; and you should try to avoid it.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Evaluating New Tools</title>
      <link>/blog/2022_09_tech_choices/</link>
      <pubDate>Tue, 27 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_09_tech_choices/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I was reading &lt;a href=&#34;https://phoenixframework.org&#34;&gt;about Phoenix&lt;/a&gt; today, looking at guides and documentation. It&amp;rsquo;s great to see in a guide when it&amp;rsquo;s easy to quickly set up a project, that makes it fun to get started and explore. The most exciting thing about Phoenix is that &amp;ldquo;reactivity&amp;rdquo;, or live updates, are a core part of the system, not an add-on. Nowadays I find any software without reactive updates frustrating and annoying to use.&lt;/p&gt;
&lt;p&gt;But even a feature as great as reactive updates is not enough to decide whether to use a tool for building web software. Now that I&amp;rsquo;m 12 years into a software engineering career, I have some nearly-subconscious evaluations I make when looking into new tools, critical things that can kill a project for me. The most prominent are:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;How Painful Will Working With a Database Be?&lt;/strong&gt;&lt;/em&gt; Working with databases (or almost any data storage) is painful. Reliable data storage is hard, but there&amp;rsquo;s a sweet spot between &amp;ldquo;looks easy&amp;rdquo; and &amp;ldquo;handle every little part of it.&amp;rdquo; If the by-default approach is to use a wrapper that says &amp;ldquo;just give us your types and we&amp;rsquo;ll store them!&amp;rdquo; I&amp;rsquo;m instantly suspicious. I know that in a few months I&amp;rsquo;m going to be digging around in debug and SQL server logs trying to understand why something is slow or broken instead of being able to inspect the code I wrote and understand what&amp;rsquo;s actually happening in the database as a first step.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;How Painful are Authentication and Authorization Tools?&lt;/strong&gt;&lt;/em&gt; Contra database tooling, auth systems should be extremely standardized in a tech stack. People mess up authorization &lt;em&gt;all the time&lt;/em&gt;, and it&amp;rsquo;s really hard to fix after the fact. If you screw up badly enough, you either have to keep horrible practices around or break all your clients and force them to upgrade. Don&amp;rsquo;t write your own cryptography, don&amp;rsquo;t write your own auth.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;How About Passing and Handling Encrypted Data?&lt;/strong&gt;&lt;/em&gt; Like I said, don&amp;rsquo;t write your own cryptography. It should be straightforward to move data back and forth between encrypted and decrypted domains. Key retrieval and storage should be a first-class part of data access layer, and the scope of access that keys have should be easy to determine.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;How are Errors Handled?&lt;/strong&gt;&lt;/em&gt; Can you easily ignore errors (boo! No!) Are there multiple error types that have to be handled differently? (Exceptions vs Error? boo! No!) Is it easy, at runtime, to discover the underlying type and cause of the error? Are there standards about what will be thrown where and when?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;How Much &amp;ldquo;Magic&amp;rdquo; is There? How Easy is it to Get Around?&lt;/strong&gt;&lt;/em&gt; Too much magic means difficult debugging. My friend Arya always uses the physics term &amp;ldquo;spooky action at a distance&amp;rdquo; to explain the issue. In my experience, more magic makes code &amp;ldquo;rot&amp;rdquo; faster. If you or your company lose experience with a codebase, magical ones take longer to brush back up on.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Podcasts At the End of the Indie Web</title>
      <link>/blog/2022_09_podcasts/</link>
      <pubDate>Sun, 25 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_09_podcasts/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;There&amp;rsquo;s been a lot of restrospecting lately, lamenting the loss of the &amp;ldquo;indie web&amp;rdquo; and its subsumption by content platforms like Facebook, Instagram, Reddit and Twitter. (I&amp;rsquo;ve always wondered where Tumblr fit in - more indie than any of these, but still - owned by Yahoo!). A few casualties that fell by the wayside: blogs, web comics, and independent, topic specific forums.&lt;/p&gt;
&lt;p&gt;All of these media still exist, much diminished and publishing social posts to route you to their sites, but they are still self-hosted, free of editorial control and in their author&amp;rsquo;s hands. To some degree, these forms will probably stay on the web until millenials die out.&lt;/p&gt;
&lt;p&gt;I want to highlight the real last bastion of the indie web, beeping on life support now: podcasts. Why does it feel like podcasts are drying up when there are more listeners than ever before? Well, why can&amp;rsquo;t indie musicians make a living anymore?&lt;/p&gt;
&lt;p&gt;Podcasts filled an interesting niche in the indie web ecosystem: harder to make and host than a blog, too small for the big media companies to notice or care about. It was powered for over a decade by an &lt;a href=&#34;maximumfun.org&#34;&gt;underground&lt;/a&gt; &lt;a href=&#34;http://nevernotfunny.com/&#34;&gt;comedy&lt;/a&gt; &lt;a href=&#34;http://www.wtfpod.com/podcast&#34;&gt;scene&lt;/a&gt; (okay &lt;a href=&#34;https://thebestshow.net/podcast/&#34;&gt;one more&lt;/a&gt;, a &lt;a href=&#34;https://daringfireball.net/thetalkshow/&#34;&gt;tech-enthusiast audience&lt;/a&gt;, &lt;a href=&#34;https://www.npr.org/podcasts-and-shows/&#34;&gt;public radio archives&lt;/a&gt;, and the benign neglect of Apple. I think there are so many interesting parallels and departures on the road of blogs and podcasts in their respective heydays.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RSS&lt;/strong&gt;: Thank the world for Aaron Swartz and Dave Winer. This foundational format laid the groundwork for blogs and podcasts, making it possible to pull updates from across the web and consume the whole thing in one place. It&amp;rsquo;s the underlying tech that makes general-purpose podcast players like &lt;a href=&#34;https://overcast.fm/&#34;&gt;Overcast&lt;/a&gt; and aggregators like &lt;a href=&#34;https://reederapp.com/&#34;&gt;Reeder&lt;/a&gt; possible. It gave you web users a feed before Twitter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Big Tech Platforms&lt;/strong&gt;: Over time, Google Reader became &lt;em&gt;the&lt;/em&gt; standard way to curate your RSS feed. It worked great, a thousand blogs bloomed. And then Google decided that the &lt;a href=&#34;https://www.wired.com/2013/06/why-google-reader-got-the-ax/&#34;&gt;rich stream of data it had flowing through its backends about millions of people&amp;rsquo;s reading habits was worthless&lt;/a&gt;. The app was shutdown and the blog ecosystem withered as a result. Twitter can take some of the blame, too - &lt;em&gt;micro&lt;/em&gt;blogging was embraced by the tech blogging set, with a power-law curve of rewards. The rich got richer and the long tail shrunk.&lt;/p&gt;
&lt;p&gt;Compare with podcasts. &lt;a href=&#34;https://en.wikipedia.org/wiki/History_of_podcasting#Apple_adds_podcasts_to_iTunes&#34;&gt;Apple added podcasts support to iTunes in 2005&lt;/a&gt; and then promptly forgot about them. There were awesome people like Scott Simpson working at Apple on podcasts, but it was a small part of the iTunes app that was left to flourish and diversify rather than be cultivated and harvested. Apple didn&amp;rsquo;t release a podcast-specific app for &lt;a href=&#34;https://www.theverge.com/2012/6/26/3118820/apple-podcasts-app-release&#34;&gt;seven more years&lt;/a&gt;, and Apple was smart enough to leave the ecosystem alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Big Media Arrives&lt;/strong&gt;: Blogs died (sort of), but podcasts &lt;strong&gt;consolidated&lt;/strong&gt;. A few years back, big media companies took notice, and there was blood in the water. &lt;a href=&#34;https://www.vox.com/2019/2/7/18214941/alex-blumberg-matt-lieber-gimlet-spotify-deal-acquisition-peter-kafka-media-podcast-audio-interview&#34;&gt;Gimlet Media at Spotify&lt;/a&gt;. &lt;a href=&#34;https://www.theverge.com/2021/4/26/22403852/siriusxm-roman-mars-99-percent-invisible-acquire-stitcher&#34;&gt;99% Invisible at Stitcher&lt;/a&gt;. &lt;a href=&#34;https://entertainment.inquirer.net/434225/true-crime-podcasts-are-as-popular-as-ever&#34;&gt;The Murder Podcast Industrial Complex&lt;/a&gt;. &lt;a href=&#34;https://www.nytimes.com/column/the-daily&#34;&gt;Every&lt;/a&gt; &lt;a href=&#34;https://www.washingtonpost.com/podcasts/post-reports/&#34;&gt;National&lt;/a&gt; News Organization. Let&amp;rsquo;s not even talk about Rogan. Popular shows have been snatched up, smaller shows starved of oxygen.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.vulture.com/2022/09/podcasting-is-just-radio-now.html&#34;&gt;Podcasts already fit a media model that the industry understood&lt;/a&gt;, and they had loyal audiences who could be analogized to radio or TV audiences. The podcast that started the gold rush came out of &lt;a href=&#34;https://serialpodcast.org/&#34;&gt;professional radio&lt;/a&gt;. Podcasting&amp;rsquo;s biggest star is a former TV actor and sports host. The entire business was legible to broadcast media, and they swooped in with exactly the same tactics as streaming apps: buy all the shows, lock them behind exclusive subscription models, cross your fingers that &lt;em&gt;your&lt;/em&gt; exclusivity is better than &lt;em&gt;theirs&lt;/em&gt;. How long until Disney starts a podcast network?&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I&amp;rsquo;m glad to see some real indie networks survive, and even thrive, in 2022, but with more listeners than ever, the ecosystem feels more consolidated than ever, and the playing field ever more tilted. I hope we&amp;rsquo;ll see a resurgence of indie media at some point, but video hosting is damned expensive, and it&amp;rsquo;s harder than every to pursue a passion project that brings in little money.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>GitHub Squash Merges are a Menace</title>
      <link>/blog/2022_github_is_a_menace/</link>
      <pubDate>Fri, 16 Sep 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_github_is_a_menace/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;a href=&#34;/blog/2022_02_squash_merge/&#34;&gt;I love squash merging&lt;/a&gt;. I think it&amp;rsquo;s the simplest way to maintain a legible commit history on &lt;code&gt;main&lt;/code&gt;, a shared &lt;code&gt;dev&lt;/code&gt; branch, etc. It&amp;rsquo;s easy for most people to follow, and it doesn&amp;rsquo;t require you to be too Big Brained about git. GitHub even provides a convenient interface for doing this, right in the &lt;a href=&#34;https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/incorporating-changes-from-a-pull-request/about-pull-request-merges#squash-and-merge-your-commits&#34;&gt;pull request UI&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;But GitHub&amp;rsquo;s squash merge workflow undermines the biggest benefits of squash merges: clear, simple, atomic commit messages that explain what each commit does.&lt;/p&gt;
&lt;p&gt;How many times have you seen a commit like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;commit ed0c641ce592e9cdaa5008264569df068e4e2ab4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Author: Joe Blubaugh &amp;lt;joe.blubaugh@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date:   Wed Aug &lt;span style=&#34;color:#ae81ff&#34;&gt;31&lt;/span&gt; 13:46:06 &lt;span style=&#34;color:#ae81ff&#34;&gt;2022&lt;/span&gt; +0800
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Fix a bug where we never commit a database transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* Fix a bug where we never commit a database transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* lint
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* pull request feedback
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;* lint AGAIN
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If your team squash merges in GitHub, this is the easiest commit message, the one that GitHub automatically fills in for you when generating the squashed commit. What we want is:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-sh&#34; data-lang=&#34;sh&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;commit ed0c641ce592e9cdaa5008264569df068e4e2ab4
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Author: Joe Blubaugh &amp;lt;joe.blubaugh@gmail.com&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Date:   Wed Aug &lt;span style=&#34;color:#ae81ff&#34;&gt;31&lt;/span&gt; 13:46:06 &lt;span style=&#34;color:#ae81ff&#34;&gt;2022&lt;/span&gt; +0800
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Fix a bug where we never commit a database transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;In db/users.go, the transaction that updates users&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39; subscription level in bulk
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#e6db74&#34;&gt;is never committed. Users don&amp;#39;&lt;/span&gt;t get access to the proper subscription content
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;as a result.
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;This change fixes the bug by using a &lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt;defer&lt;span style=&#34;color:#e6db74&#34;&gt;`&lt;/span&gt; to guarantee that the transaction
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;is committed &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; there is no error or rollback.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can promote this practice at your company person-by-person, one slog at a time, but I&amp;rsquo;ve not yet found a way to get GitHub to stop filling in terrible commit messages by default.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>How We Do Our Best Work</title>
      <link>/blog/2022_09_best_work/</link>
      <pubDate>Wed, 31 Aug 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_09_best_work/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Dan Luu tweeted about some great work interns that he has mentored accomplished at Twitter:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One intern did &lt;a href=&#34;https://t.co/nsFW20j9Hm&#34;&gt;&lt;a href=&#34;https://t.co/nsFW20j9Hm&#34;&gt;https://t.co/nsFW20j9Hm&lt;/a&gt;&lt;/a&gt; and another did interesting data analysis then built a working prototype for across the fleet profiling that others were able to use to find real inefficiencies.&lt;br&gt;&lt;br&gt;Those are things that could go into a staff promo packet as a major project.&lt;/p&gt;— Dan Luu (@danluu) &lt;a href=&#34;https://twitter.com/danluu/status/1564820873684860928?ref_src=twsrc%5Etfw&#34;&gt;August 31, 2022&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;It made me think of internships I had, and when I produced my best work. There are two outcomes I&amp;rsquo;m particularly proud of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I was an intern at the &lt;a href=&#34;https://lasers.llnl.gov/&#34;&gt;National Ignition Facility&lt;/a&gt; in 2008, and I proposed and wrote image processing code to detect and monitor flaws in laser lenses as they grow. That code was still in the system 5 years later, still doing its work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I built and programmed a battery qualification rig for rechargable &amp;ldquo;wet&amp;rdquo; batteries that GE Healthcare used in its &lt;a href=&#34;https://www.gehealthcare.com/en-sg/products/radiography/mobile-xray-systems&#34;&gt;mobile X-Rays&lt;/a&gt;. I found and bought equipment, built the rig in a lab with help from techs, programmed it, designed the test protocol and kicked off the first qualification run.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the other hand, I had a few experiences that were interesting enough, but didn&amp;rsquo;t stand out to me as particularly impressive results:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I did ESD testing for patient monitoring equipment. This was fun, in that I got to shock things with static guns and visit an anechoic chamber, but it was largely &amp;ldquo;assigned work&amp;rdquo; without a lot of autonomy on my part.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I worked for &lt;a href=&#34;https://www.gehealthcare.com/products/magnetic-resonance-imaging/3-0t&#34;&gt;GE Healthcare&lt;/a&gt; writing RF switching designs in VHDL. These were for small, fairly simple components and were almost a direct analog of microprocessor design work I did at school.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are qualities of the work I&amp;rsquo;m most proud of that are missing from the two &amp;ldquo;eh&amp;quot;s:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Autonomy&lt;/strong&gt;: Starting with a clear but high-level goal like &amp;ldquo;how do we make sure these batteries work?&amp;rdquo; and being responsible for everything that addresses the goal is a powerful motivator. You know that you&amp;rsquo;re being trusted to do something real, and do something right. If you&amp;rsquo;re just doing work that&amp;rsquo;s already speced and given to you, the motivation is considerably lower.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus&lt;/strong&gt;: This is something that interns get more than anyone else. Everyone knows that you&amp;rsquo;re a goner in 3 months. You don&amp;rsquo;t do on-call rotations. You don&amp;rsquo;t do roadmapping. You find your project and you work your project. That&amp;rsquo;s a productivity superpower. Context switches are expensive, shipping quickly is essential, and focus fixes that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mentorship&lt;/strong&gt;: The other side of the autonomy coin - you &lt;strong&gt;will&lt;/strong&gt; get stuck on a high level goal, at any phase of your career. You need people you can safely ask for advice, and a surprising percentage of people don&amp;rsquo;t get this. Their internship hosts may be too busy or uninterested, their manager may be managing too many people, &amp;ldquo;competing&amp;rdquo; teams at dysfunctional companies may be actively undermining it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These conditions won&amp;rsquo;t make up for a lack of skills or ambition, but they are critical enablers for skilled, ambitious people. When I think of professional frustrations of mine (dropping out of grad school, botched design proposals, having to fire a direct report), one of these was missing, or worse, being actively undermined.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;PS: I&amp;rsquo;ll write that &lt;a href=&#34;/blog/2022_05_okrs/&#34;&gt;second blog post about OKRs&lt;/a&gt;, I swear.&lt;/small&gt;&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Thoughts on OKRs</title>
      <link>/blog/2022_05_okrs/</link>
      <pubDate>Wed, 18 May 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_05_okrs/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;a href=&#34;https://www.whatmatters.com/&#34;&gt;OKRs&lt;/a&gt; are one of those business ideas that are just simple enough to be dangerous. You think you understand it in a day, and you can see where your company is falling short: lacking focus and underdelivering. You see how clear, measurable goals could improve the situation.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the one sentence version: You set &lt;strong&gt;objectives&lt;/strong&gt;, and for each objective come up with several &lt;strong&gt;key results&lt;/strong&gt; that you can measure to see if you met the objectives. &lt;a href=&#34;https://www.whatmatters.com/the-book&#34;&gt;There&amp;rsquo;s a whole book&lt;/a&gt; for implementing this process in large organizations, and a mystique because of its association with Google&amp;rsquo;s early and frightening effectiveness.&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve worked at four companies that used some variation of OKRs (including Google!). All of them set a hierarchical pyramid of OKRs that cascaded down, rather than a set of OKRs that worked bottom-up, even if the company wasn&amp;rsquo;t in a crisis and would have benefited from promoting innovation.&lt;/p&gt;
&lt;p&gt;I want to compare OKRs to Performance Reviews and Roadmapping. They&amp;rsquo;re all worthwhile ideas that can bring discipline and structure to the chaotic world of business, all dreaded by their participants for some reason. I think they have similar failure modes, and most companies get into a state of learned helplessness about them for similar reasons. For OKRs, here are the failure modes I saw repeatedly, and took part in. Each one of them leads to the next, and I bet you can think of the way these happen in performance reviews and product roadmaps, too.&lt;/p&gt;
&lt;h2 id=&#34;it-doesnt-take-up-enough-of-leaderships-time&#34;&gt;It doesn&amp;rsquo;t take up enough of leadership&amp;rsquo;s time.&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re a manager, you&amp;rsquo;re screaming right now. I know, I know, they feel like they take &lt;em&gt;forever&lt;/em&gt;. But if the process is going to be effective, then the connection between individuals&amp;rsquo;, teams&amp;rsquo;, groups&amp;rsquo;, divisions&amp;rsquo; and the company&amp;rsquo;s OKRs need to be tight and logical. That means a high-bandwidth, highly synchronized exercise with bidirectional feedback. That&amp;rsquo;s expensive and time consuming and it&amp;rsquo;s the most important job corporate leadership can have. Set the goals for the people who do the work. Focus less on execution as a leader and more on goal setting and measurement.&lt;sup id=&#34;fnref:2&#34;&gt;&lt;a href=&#34;#fn:2&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&#34;the-process-always-starts-too-late-and-runs-too-long&#34;&gt;The process always starts too late and runs too long.&lt;/h2&gt;
&lt;p&gt;Ironically, the process starts late and takes a long time because it isn&amp;rsquo;t taking up enough of leadership&amp;rsquo;s time in the first place. So the business sets up some cascading set of &amp;ldquo;due dates&amp;rdquo;: First, company OKRs, then a week later a department and on and on to an individual. Say the due dates are at least a week apart. You&amp;rsquo;re looking at a six-week-long process, and every step of the process is going to be a day or two late, so you&amp;rsquo;ll fudge until it&amp;rsquo;s an eight-week process. This is where OKRs end up in the same place as performance reviews. Projects are running late, so planning is running late because it&amp;rsquo;s getting in the way of projects. You have to pull out of the death spiral.&lt;/p&gt;
&lt;h2 id=&#34;okrs-arent-actually-committed-to&#34;&gt;OKRs aren&amp;rsquo;t actually committed to.&lt;/h2&gt;
&lt;p&gt;If it takes 3 weeks into the quarter to define your OKRs for the quarter, well, congratulations, you spent those 3 weeks choosing what you&amp;rsquo;ll &lt;em&gt;actually&lt;/em&gt; do this quarter. You&amp;rsquo;ll make the case that the things you&amp;rsquo;re working on are really important and already in progress, even if they&amp;rsquo;re not lined up exactly with the department OKRs. And they may be really important, and the OKRs may be wrong because we didn&amp;rsquo;t spend enough time working on them and took too long to deliver them, and we didn&amp;rsquo;t leave room for bottom-up objective setting. So we do the important things we&amp;rsquo;re already doing, and we&amp;rsquo;ll fudge the KRs a bit at the end of the quarter.&lt;/p&gt;
&lt;h2 id=&#34;there-are-rarely-consequences-for-failure-or-much-learning-from-it&#34;&gt;There are rarely consequences for failure, or much learning from it.&lt;/h2&gt;
&lt;p&gt;If the Objectives aren&amp;rsquo;t committed to, and the KRs don&amp;rsquo;t matter, then why fire someone for a huge miss, or change the whole way your department works? Just write new ones, carry late projects over, and here we go again.&lt;/p&gt;
&lt;h2 id=&#34;welp&#34;&gt;Welp.&lt;/h2&gt;
&lt;p&gt;How can you improve OKR definition and actually realize their benefits? Stick around for the next post.&lt;/p&gt;
&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id=&#34;fn:1&#34;&gt;
&lt;p&gt;Given Google&amp;rsquo;s business misses in everything &lt;em&gt;except&lt;/em&gt; ads, I wonder if this isn&amp;rsquo;t due for another look.&amp;#160;&lt;a href=&#34;#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&#34;fn:2&#34;&gt;
&lt;p&gt;This happens to jibe nicely with the lessons of &lt;a href=&#34;https://www.goodreads.com/book/show/324750.High_Output_Management&#34;&gt;High Output Management&lt;/a&gt;, my favorite book on business execution.&amp;#160;&lt;a href=&#34;#fnref:2&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;

        
        </description>
    </item>
    
    <item>
      <title>You Should Squash Merge to main</title>
      <link>/blog/2022_02_squash_merge/</link>
      <pubDate>Thu, 10 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>/blog/2022_02_squash_merge/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Every so often I&amp;rsquo;ll see a meme on Twitter like:
&lt;img src=&#34;/images/squashmerge.jpeg&#34; alt=&#34;Squash Merge meme&#34;&gt;&lt;/p&gt;
&lt;p&gt;and it makes me so mad. For good reason! OSS project repositories that support merge commits to main are usually littered with useless comments like: &amp;ldquo;Merge change from $USER, $PR&amp;rdquo;. It makes the commit history on main utterly useless and you get to check the various feature branches in a never ending snake of commits to find out what the hell changed.&lt;/p&gt;
&lt;p&gt;You should squash merge.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s why you should squash-merge:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Every single commit on main should be releasable. A chain of commits that are merged in one go are not necessarily releasable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The commit messages in feature branches are usually uninteresting, and that&amp;rsquo;s a good thing. It is absolutely OK for your commit log to look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$TICKET: Prepare for new service by creating configuration files and code skeleton.&lt;/li&gt;
&lt;li&gt;Fix missing environment variable&lt;/li&gt;
&lt;li&gt;checkpoint for EOD&lt;/li&gt;
&lt;li&gt;finished!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;as long as your merged commit on main is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$TICKET: Prepare for new service by creating configuration files and code skeleton.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I believe that advocates for keeping the commit history of a branch during merges think that you should re-write your feature branch commits to be more meaningful before actually merging. What a waste of time!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A messy feature branch history is good, actually. Messy branch history allows you to push to remote at any time without really thinking about it. Replication helps when you leave your laptop on the train, or to quickly share the state of your work with a coworker. After it&amp;rsquo;s on the remote you should try not to force push it, especially if you&amp;rsquo;ve shared it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You don&amp;rsquo;t lose anything interesting in squash merge. It doesn&amp;rsquo;t matter what the commit was where you forked from main. It only matters what the difference between main and your branch is when you merge. You can most cleanly see that change by comparing sequential commits to main that have nice clear messages.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Merge commit messages are useless out-of-the-box. Why write the same commit message twice - you have it somewhere in your feature branch and then again (hopefully) in your merge commit.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You &lt;em&gt;only&lt;/em&gt; need to view the commit log of main to understand what changed when. This is what you care about when you release software.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;My actual favorite solution to this problem is to use the Gerrit workflow that guarantees any merged piece of code is a single &amp;ldquo;change set&amp;rdquo; that&amp;rsquo;s merged atomically. Squash Merge is the closest thing Github provides in its UI, so do that.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Why I (Still) Use Vim</title>
      <link>/blog/2021_12_why_i_vim/</link>
      <pubDate>Wed, 15 Dec 2021 00:00:00 +0000</pubDate>
      
      <guid>/blog/2021_12_why_i_vim/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Every so often I wonder if I&amp;rsquo;m making trouble for myself by doing all of my typing in vim. Writing for this blog, writing engineering plans and design documents, and writing code. There are tools that are made especially for doing these jobs, and vim certainly doesn&amp;rsquo;t have any facility for making diagrams.&lt;/p&gt;
&lt;p&gt;All vim can really do is edit a bunch of bytes, trying to represent them as formatted text. It doesn&amp;rsquo;t even &lt;em&gt;really&lt;/em&gt; understand code, which is what most people use it for.&lt;/p&gt;
&lt;p&gt;But still, after I take a little trip over to &lt;a href=&#34;https://code.visualstudio.com&#34;&gt;VSCode&lt;/a&gt; or &lt;a href=&#34;https://www.jetbrains.com/go/&#34;&gt;Jetbrains&lt;/a&gt; or &lt;a href=&#34;https://nova.app&#34;&gt;Nova&lt;/a&gt; or &lt;a href=&#34;https://www.literatureandlatte.com/scrivener/overview&#34;&gt;Scrivener&lt;/a&gt;, I end up coming back to vim, finding a vim plugin that scratches my itch, and getting right back to it. Here&amp;rsquo;s what keeps me here, using this old, weird, but great tool.&lt;/p&gt;
&lt;h1 id=&#34;speed&#34;&gt;Speed&lt;/h1&gt;
&lt;p&gt;Opening a file in vim is fast. Jumping from place-to-place in vim is fast. Typing in vim is &lt;em&gt;fast&lt;/em&gt;. &lt;a href=&#34;/blog/slow_software_destroys_flow/&#34;&gt;Fast is a feature&lt;/a&gt;&lt;/p&gt;
&lt;h1 id=&#34;splits&#34;&gt;Splits&lt;/h1&gt;
&lt;p&gt;Every good idea has ways to split windows, but in vim it&amp;rsquo;s stupid fast, and stupid easy to manipulate. Here&amp;rsquo;s magic spell for splitting the window vertically, then the left one horizontally, then navigating to the right-hand one.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;:vs|sp
^+W k
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You end up with:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;----------------
| foo   | baz_ |
|       |      |
| ----- |      |
| bar   |      |
|       |      |
|       |      |
----------------
&lt;/code&gt;&lt;/pre&gt;&lt;h1 id=&#34;ubiquity&#34;&gt;Ubiquity&lt;/h1&gt;
&lt;p&gt;Have you ever been on a Linux box without at least vi? Even Arch and Alpine ship with vi installed.&lt;/p&gt;
&lt;h1 id=&#34;coc&#34;&gt;CoC&lt;/h1&gt;
&lt;p&gt;With the advent of the Language Server Protocol, many language-specific features like navigation, highlighting, and completion have been decoupled from editors. &lt;a href=&#34;https://github.com/neoclide/coc.nvim&#34;&gt;Commander-of-Completion&lt;/a&gt; is a great vim plugin that adds LSP support to vim and makes it less arduous to add various new languages to your toolkit.&lt;/p&gt;
&lt;h1 id=&#34;multi-modal&#34;&gt;Multi-modal&lt;/h1&gt;
&lt;p&gt;Modal interfaces like vim&amp;rsquo;s are generally regarded poorly in the UX community. The other pre-modern text editor, emacs, avoids modes but relies heavily on modifiers to enable non-typing modes.&lt;/p&gt;
&lt;h1 id=&#34;keyboard-oriented&#34;&gt;Keyboard-Oriented&lt;/h1&gt;
&lt;p&gt;I never touch my mouse using vim. In many cases, you simply can&amp;rsquo;t use the mouse to get anything done. This takes some getting used to, but it&amp;rsquo;s a blessing for your wrists and shoulders.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>My Country, Tis of Thee</title>
      <link>/blog/202007-my-country/</link>
      <pubDate>Tue, 21 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>/blog/202007-my-country/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;ve spent a lot of time the last 3 years watching &lt;a href=&#34;https://www.pegc.us/archive/Articles/eco_ur-fascism.pdf&#34;&gt;Umberto Eco&amp;rsquo;s sign&amp;rsquo;s of Ur-Facism&lt;/a&gt; manifest, and manifest, and manifest. I&amp;rsquo;ve seen a lot of things that I was raised to believe &lt;del&gt;wouldn&amp;rsquo;t&lt;/del&gt; couldn&amp;rsquo;t happen here happen in sequence, with frightening speed. Out-of-control (willfully so) police beating and gassing people in the street, night after night after night. Destruction of public schools, along with requiring states fund religious schools. A Justice Department &lt;a href=&#34;https://www.newyorker.com/magazine/2020/01/20/william-barr-trumps-sword-and-shield&#34;&gt;hell-bent on revenge for the President&lt;/a&gt;. Open xenophobia, brazen racism by members of congress. I thought we left that behind when Strom Thurmond finally died.&lt;/p&gt;
&lt;p&gt;I worry about my wife, who holds a green card, and is thereby subject to the whims of immigration officers and state police who have an almost infinite number of crimes at their disposal. I worry for my co-worker Dan, a DACA kid who&amp;rsquo;s never done a thing wrong and still lives on unstable ground. I worry for everyone I know who opposes Donald Trump as his DoJ bends every law to its cruelest angle.&lt;/p&gt;
&lt;p&gt;Looking back, you can see the seeds of these problems planted. Occupy Wall Street &amp;amp; Occupy Oakland happened during Barack Obama&amp;rsquo;s presidency, and protestors (including yours truly) were &lt;a href=&#34;https://en.wikipedia.org/wiki/Occupy_Oakland#Scott_Olsen_head_injury_on_October_25&#34;&gt;gassed and beaten by cops&lt;/a&gt; on more than one occasion. Expansive theories of executive power go back at least to &lt;a href=&#34;https://en.wikipedia.org/wiki/Torture_Memos&#34;&gt;John Yoo and the war on terror&lt;/a&gt;. John Yoo&amp;rsquo;s now sitting pretty at Berkeley and I wonder how he doesn&amp;rsquo;t get his car egged every day. Public Schools have been under assault for decades as standardized testing extends a widening grip on the entire system.&lt;/p&gt;
&lt;p&gt;All of these seeds have been fertilized and nurtured by the fascists who prop up the President, and who will doubtless get sinecures at think tanks when they should be expelled from polite society and wind up unemployable. Even if we beat back the kudzu in November, I worry that we&amp;rsquo;ve passed a point of no return. Re-building after this can&amp;rsquo;t be about going back to before a mistake - it has to be going forward and changing our society.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Font Measurements</title>
      <link>/blog/202007-font-measures/</link>
      <pubDate>Mon, 06 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>/blog/202007-font-measures/</guid>
      <description>
        
          
          
          
        
        
        &lt;h2 id=&#34;tldr&#34;&gt;TLDR;&lt;/h2&gt;
&lt;p&gt;I learned a bunch about rendering fonts and I thought it would be interesting to read about them from a programmer&amp;rsquo;s perspective. I gained a ton of empathy for type design and type-setting developers, as they work with a dizzying variety of screen resolutions, font styles, and a wide variety of device speeds to produce type that looks as good as it possibly can under harsh conditions.&lt;/p&gt;
&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been working on a project involving a two-color e-ink screen. I&amp;rsquo;m drawing on the screen and that involves drawing text. The project is written in &lt;a href=&#34;https://golang.org&#34;&gt;Go&lt;/a&gt;. There are common font-rendering librares like &lt;a href=&#34;https://www.cairographics.org/&#34;&gt;Cairo&lt;/a&gt;, which are written in C. I find compiling against C libraries like using cgo difficult and complex, so I was really interested in a Go-only solution.&lt;/p&gt;
&lt;p&gt;Fortunately, Go supplies a decent &lt;a href=&#34;https://pkg.go.dev/image/draw&#34;&gt;image package&lt;/a&gt; right out of the box. Combined with the &amp;ldquo;semi official&amp;rdquo; &lt;a href=&#34;https://pkg.go.dev/golang.org/x/image/font&#34;&gt;font package&lt;/a&gt;, it seemed I could do everything I needed.&lt;/p&gt;
&lt;p&gt;Unfortunately, I&amp;rsquo;ve selected some fonts that the most popular font-parsing package can&amp;rsquo;t handle. So I&amp;rsquo;m using a &lt;a href=&#34;https://pkg.go.dev/golang.org/x/image/font/sfnt&#34;&gt;different package&lt;/a&gt; that can parse many more fonts, but &lt;a href=&#34;https://go-review.googlesource.com/c/image/&amp;#43;/240897&#34;&gt;until yesterday&lt;/a&gt; didn&amp;rsquo;t support any of the rendering functions required for drawing on images. In writing and submitting that code, I ended up learning a lot about how fonts are represented in files and what happens when they&amp;rsquo;re rendered.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m going to focus on laying out text left-to-right below. However, there are many languages that use right-to-left horizontal layouts, vertical layouts that go either left-to-right or right-to-left, and languages with alternating line directions. Many of the measurement concepts below apply but may need to be rotated or flipped from a horizontal left-to-right perspective.&lt;/p&gt;
&lt;h2 id=&#34;font-y-terms&#34;&gt;Font-y terms&lt;/h2&gt;
&lt;p&gt;A &lt;strong&gt;font&lt;/strong&gt; is a collection of glyphs - letters, numbers, symbols, ideograms, emojis (😭) that the font is able to draw.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;typeface&lt;/strong&gt; is a collection of fonts that share a style. This can include italic, bold, or even serif &amp;amp; sans-serif variants.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;serif&lt;/strong&gt; is that little slab at the bottom or top of a glyph. &lt;a href=&#34;https://en.wikipedia.org/wiki/Serif&#34;&gt;Here&amp;rsquo;s an example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fonts then, may be &lt;strong&gt;serif&lt;/strong&gt; or &lt;strong&gt;sans-serif&lt;/strong&gt; fonts, though there are other styles like &lt;a href=&#34;https://en.wikipedia.org/wiki/Blackletter&#34;&gt;Blackletter&lt;/a&gt;. This primarily applies to the Roman alphabet, and Roman-influenced alphabets. Chinese fonts have their own styles that are similar - &lt;a href=&#34;https://webdesign.tutsplus.com/articles/the-complete-beginners-guide-to-chinese-fonts--cms-23444&#34;&gt;Songti and Heiti&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/font/serif-sans-serif.png&#34; alt=&#34;Serif/ Sans serif&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-metrics&#34;&gt;Font metrics&lt;/h2&gt;
&lt;p&gt;The measurements described here are common to both manual typesetting and computer typesetting. Manual typesetting is very rare these days, but the terms originate from that practice.&lt;/p&gt;
&lt;p&gt;The capital Q and lower-case e below both show their measurements.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/font/measure.png&#34; alt=&#34;Font measurements&#34;&gt;&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;baseline&lt;/strong&gt; is the line that all text on the line is laid-out relative to. For each rendered glyph there&amp;rsquo;s an &amp;ldquo;origin&amp;rdquo; point on the baseline.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;advance width&lt;/strong&gt; is the space from a glyphs origin until the origin of the next glyph.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;ascent&lt;/strong&gt; is the height of a glyph above the basline and the &lt;strong&gt;descent&lt;/strong&gt; is the depth below the baseline. Many fonts also provide &lt;strong&gt;ascent&lt;/strong&gt; and &lt;strong&gt;descent&lt;/strong&gt; measurements that are the maximum of these values for all glyphs. This can help with line spacing.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;bearing&lt;/strong&gt; of a glyph is the space between the glyph and its origin, or the origin of the next glyph.&lt;/p&gt;
&lt;p&gt;Fonts are sized in units called &lt;strong&gt;points&lt;/strong&gt;. There are 72 points per inch.&lt;/p&gt;
&lt;h3 id=&#34;special-topic-kerning&#34;&gt;&lt;strong&gt;Special topic: Kerning&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Kerning&lt;/strong&gt; is the adjustment of glyph spacing based on glyph pairs. For example, WA is often kerned &lt;strong&gt;tighter&lt;/strong&gt; than the default spacing to reduce the large diagonal line that you&amp;rsquo;d otherwise have between the two.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/font/kern.png&#34; alt=&#34;Kerning&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;special-topic-ligatures&#34;&gt;&lt;strong&gt;Special topic: ligatures&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;In some fonts, multiple letters may be represented by a single glyph. In some fonts, fl or ff may be joined together so that the characters overlap.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/images/font/ligature.png&#34; alt=&#34;Ligature&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;font-file-formats&#34;&gt;Font file formats&lt;/h2&gt;
&lt;p&gt;A very common text-rendering system on modern computers is &lt;a href=&#34;https://www.freetype.org/&#34;&gt;Freetype&lt;/a&gt;. Windows is &lt;strong&gt;not&lt;/strong&gt; included in that family. The common windows font-rendering system is &lt;a href=&#34;https://en.wikipedia.org/wiki/ClearType&#34;&gt;ClearType&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;coordinate-system&#34;&gt;Coordinate system&lt;/h2&gt;
&lt;p&gt;Font sizes themselves are expressed in &amp;ldquo;points&amp;rdquo;, and there are 72 of them in an inch. So you should expect a 72 pt font to take up about an inch from line-to-line.&lt;/p&gt;
&lt;p&gt;However, computers &lt;strong&gt;don&amp;rsquo;t&lt;/strong&gt; represent fonts that way. Each glyph in a font is represented by a set of &amp;ldquo;drawing instructions.&amp;rdquo; The instructions may specify &amp;ldquo;draw a line from A to B, then draw a cubic bezier from B to C, then draw another line from D to E.&amp;rdquo; These drawing instructions are represented on a single coordinate system, which is then scaled by the point size to create the final set of coordinates.&lt;/p&gt;
&lt;p&gt;Font file coordinate systems use a special representation called &amp;ldquo;fixed point.&amp;rdquo; Because fonts need to represent sub-pixels, we can&amp;rsquo;t use whole integers. Why not use floating point numbers? At different scales, floating point numbers have different levels of precision. So font files use fixed-point representations for their various numbers. You can think of these as floats with a fixed precision. Several fixed-point formats are used in font files for kerning, glyphs etc. The glyph coordinates are written using &amp;ldquo;26.6&amp;rdquo; The integer part has twenty-six bits, and the fractional part has 6 bits, to fill the space of a 32-bit integer. This means that the resolution of the fractional part is 1/64. Here are some binary representations of numbers in fixed point:&lt;/p&gt;
&lt;style&gt;
table {
  border-collapse: collapse; 
	border: 2px solid black;
	margin: 3em;
	margin-top: 1em;
}

td,th {
  padding: .25em 0.5em;
}

td {
  font-family: monospace;
}

th:nth-child(2),td:nth-child(2) {
  text-align: right;
}

th {
  border-bottom: 1px solid black;
}
&lt;/style&gt;
&lt;table&gt;
&lt;thead&gt;&lt;tr&gt;
&lt;th&gt;Number&lt;/th&gt;
&lt;th&gt;Fixed-point&lt;/th&gt;
&lt;/tr&gt;&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1      &lt;/td&gt;&lt;td&gt;   1.000000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1.5    &lt;/td&gt;&lt;td&gt;   1.100000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2.25   &lt;/td&gt;&lt;td&gt;  10.010000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5.75   &lt;/td&gt;&lt;td&gt; 101.110000&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 63/64 &lt;/td&gt;&lt;td&gt;   1.111111&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;So, to render a font on-screen, we need to put all of these pieces together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Take the string you need to render and get the coordinates for each glyph.&lt;/li&gt;
&lt;li&gt;Substitute ligatures where necessary, and adjust the spacing based on the kerning between glyphts.&lt;/li&gt;
&lt;li&gt;Create a pixel coordinate system based on the dots-per-inch (DPI) of the screen you&amp;rsquo;re rendering to. iPhone 11s have a DPI of 463 pixels per inch.&lt;/li&gt;
&lt;li&gt;Scale all the font coordinates (at 72 points per inch) by your screen grid (463 pixels per inch), using fixed-point math.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You may do all the above glyph-by-glyph, though you may have to &amp;ldquo;go back&amp;rdquo; to deal with ligatures.&lt;/p&gt;
&lt;h2 id=&#34;rasterization&#34;&gt;Rasterization&lt;/h2&gt;
&lt;p&gt;Once you have the set of all font coordinates, you need to actually draw those dots on-screen. Some coordinates will be for fractional pixels, but we have an integer number of pixels. This is where &amp;ldquo;rasterization&amp;rdquo; comes in. Rasterization is done for just about anything displayed on a computer screen, from video games to text, to photos. In font rendering it involves shifting items to fill pixels, and adjusting colors so some pixels on the edge of letters are a lighter gray so the pixels appear partially filled.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;http://rastertragedy.com/RTRCh4.htm&#34;&gt;Here&amp;rsquo;s an excellent site&lt;/a&gt; that explains a rasterizer for TrueType in greater detail.&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;p&gt;Type rendering is a topic filled with nuance and artistry - the above only provides a simple summary. Some of the computer industry&amp;rsquo;s most talented graphics engineers have worked in this field for decades. Computer displays are now the place where westerners spend the majority of their reading time, so clear and attractive type on screens remains incredibly important.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Influence</title>
      <link>/blog/202005-influence-politics/</link>
      <pubDate>Sat, 30 May 2020 00:00:00 +0000</pubDate>
      
      <guid>/blog/202005-influence-politics/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I got an email from an old co-worker the other day:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I was thinking about you and how you are a person I see as not being
particularly embroiled in office politics, but also as having a lot of
influence and knowing how and where to leverage it. I was curious if you had
any words of advice for how you got to that place.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Of course I was happy to hear that I come off this way, and as I thought about
how to reply, I found myself writing an essay.&lt;/p&gt;
&lt;h2 id=&#34;so-about-influence-and-politics-and-etc&#34;&gt;So, about influence, and politics, and etc.&lt;/h2&gt;
&lt;p&gt;When most people say &amp;ldquo;office politics&amp;rdquo;, they seem to mean the &amp;ldquo;dark side&amp;rdquo; of
relationships at a company. Who gets the boss&amp;rsquo;s ear? Who &amp;ldquo;wins&amp;rdquo; when there are
decisions to be made? Who has their ear to the ground?&lt;/p&gt;
&lt;p&gt;When people talk about influence and leveraging it, they&amp;rsquo;re talking about the
&amp;ldquo;light side&amp;rdquo; of the same thing. Who shapes decisions? Who &amp;ldquo;makes things
happen&amp;rdquo;? Who shifts behavior, changes the tone of the company?&lt;/p&gt;
&lt;p&gt;Office politics and influence function in really similar ways:  building
relationships with your co-workers, building trust with them, and keeping lines
of communication with them open. Most decisions are made before the decision
gets made, so being around for those early conversations is where you can
exercise leverage.&lt;/p&gt;
&lt;p&gt;Some ways that I build relationships with co-workers:&lt;/p&gt;
&lt;div class=&#34;content&#34;&gt;
&lt;ul&gt;
&lt;li&gt; private messages when they do something great.&lt;/li&gt;
&lt;li&gt; always show up for those coffee chats and lunches. This is how you get to know people way outside your group. I have one lined up with our director of finance and a developer who lives in Europe. It&#39;s rare to get to talk with them both. If there isn&#39;t something like this at a job, it&#39;s usually pretty easy to kind-of start one.&lt;/li&gt;
&lt;li&gt; not-too-frequent 1:1s with people in other teams. I have about 5 of these going, and I do them every 3 weeks. I try to vary the rotation enough from time-to-time. They&#39;re hard to drop, though! Be judicious.&lt;/li&gt;
&lt;li&gt; ask people for help! most people love to feel helpful.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;All of these have the advantage of being genuinely nice things to do! They make
me feel good, they make my co-worker feel good. The hardest thing about them is
making them happen when I have a lot of other stuff to do, or I&amp;rsquo;m feeling
stressed out or distracted. But it really is a part of your job, especially as
you grow into a leader in your team, organization, company.&lt;/p&gt;
&lt;p&gt;I also work pretty hard to build trust. The best way I know to do this is to do
your job as best as you can, and to be transparent with people. When you&amp;rsquo;re
working on something hard, getting people&amp;rsquo;s opinion when the idea isn&amp;rsquo;t fully
baked does a lot - you can get better ideas, and you show that you&amp;rsquo;re trusting
a co-worker with something delicate, that you need and value their
contribution. When things aren&amp;rsquo;t going smoothly, being straight with your boss&lt;/p&gt;
&lt;p&gt;Another way that I build trust and credibility is calling attention to issues
that affect &amp;ldquo;everybody&amp;rdquo; - but always offering to help make the improvement
happen, or even do initial legwork to offer an idea for a solution. This makes
it clear that you&amp;rsquo;re doing more than complaining; that you genuinely want
things to improve and you want to be part of that.&lt;/p&gt;
&lt;p&gt;Finally, asking for hard feedback, and giving hard feedback are important
skills for building trust. The &lt;em&gt;asking&lt;/em&gt; is much harder to do, and. I don&amp;rsquo;t
agree with everything in the book - even maybe less than half of it - but
&lt;a href=&#34;https://www.radicalcandor.com&#34;&gt;Radical Candor&lt;/a&gt; helped reshape my ideas about
trust. I think the book goes too far, but being able to give criticism honestly
and with respect is crucia. Even more important is being ready to receive it
without rejecting it or saying &amp;ldquo;yea, but&amp;rdquo;. Living with it. Thinking about it.
Then accepting the parts that are true and rejecting the parts that are
undermining you. If you get sexist feedback, throw it in the garbage and note
how much you can trust that person.&lt;/p&gt;
&lt;p&gt;So, how is that different from office politics? It&amp;rsquo;s not &lt;em&gt;just&lt;/em&gt; tone, but tone
is a big part of it. It&amp;rsquo;s about genuinely wanting good for other people and for
yourself. It&amp;rsquo;s about character, and about honesty. It&amp;rsquo;s about genuinely wanting
good things for yourself, your co-workers and your company, and striving to
make that happen.&lt;/p&gt;
&lt;p&gt;Influence is definitely related to competence, but also to prioritization.
You&amp;rsquo;ll be more influential if you&amp;rsquo;re addressing real needs that people care
about - &lt;em&gt;especially&lt;/em&gt; your bosses. If they&amp;rsquo;re good bosses, this is both easy and
not morally perilous.&lt;/p&gt;
&lt;p&gt;Unfortunately, &lt;em&gt;none&lt;/em&gt; of this holds up when you&amp;rsquo;re surrounded by people of ill will. In that sort of situation, find the other people of good character in your workplace and keep your head down. If you can get out, get out. Don&amp;rsquo;t spend your energy trying to change people who show you they can&amp;rsquo;t be trusted.&lt;/p&gt;
&lt;h2 id=&#34;an-aside-about-conflict&#34;&gt;An aside about &lt;em&gt;&lt;em&gt;conflict&lt;/em&gt;&lt;/em&gt;:&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Every so often, influence doesn&amp;rsquo;t work this way and you have to stick your neck
out. Again, the tone is really important here. In our two big meetings after
the layoffs, I directly asked our CEO and my VP some uncomfortable questions
about how decisions about who was being laid off and how the layoffs were
communicated. I followed up with more direct feedback in private conversations.
I think this made a difference in how decisions will happen in the future and
in gave my co-workers a better picture of what happened. The way I made those
conversations work was by communicating respect for my bosses as people as
early as I could in the conversation, and assuming good will by them.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;About assuming good will - it&amp;rsquo;s complicated, but important. The key point is
that by assuming good will, you&amp;rsquo;re able to get more honest and less-defensive
communication with someone, even if they&amp;rsquo;re acting with ill will. That doesn&amp;rsquo;t
mean avoid confrontation, but it means approaching confrontation as though it&amp;rsquo;s
based on either conflicting priorities or mistakes rather than malice. This is
a key conflict skill that I wish I&amp;rsquo;d developed so much sooner! Life would have
been better. After the conversation you draw your own conclusions about whether
you&amp;rsquo;ve been dealt with honestly and you can choose your next steps with good
information. When good will is real, things will often change for the better as
a result of the conflict.&lt;/p&gt;
&lt;p&gt;This all sounds very high minded. In writing it, I&amp;rsquo;ve noticed many ways that I
fall short of these ideals. But it feels right as a roadmap.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Lucky</title>
      <link>/blog/luck/</link>
      <pubDate>Fri, 22 May 2020 00:00:00 +0000</pubDate>
      
      <guid>/blog/luck/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;We went through layoffs at work recently. Like a lot of companies, the belt is tightening, and the bathtub drain is getting blugged. I&amp;rsquo;m lucky to have not been laid off. Lucky again - this is the third &amp;ldquo;surprise&amp;rdquo; layoff where I&amp;rsquo;ve kept my job. I&amp;rsquo;m batting 1.000 but I know my number has to come up sometime.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I graduated from college in 2009, 9 months into the Great Financial Crisis. I was lucky to get into a good graduate school on a stipend that paid my rent, and even luckier to be leaving undergraduate without student debt. I was lucky that when I needed to quit graduate school I could find a government job.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I was lucky to go to a particular concert, meet a particular person, and lucky that we fell in love. Lucky that the US Government didn&amp;rsquo;t fuck around with her life and we were able to stay here in California.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I&amp;rsquo;m lucky to be living through this epidemic in good health, with plenty of food and a functioning society.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Count your blessings.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Fast is a Feature</title>
      <link>/blog/slow_software_destroys_flow/</link>
      <pubDate>Tue, 19 May 2020 00:00:00 +0000</pubDate>
      
      <guid>/blog/slow_software_destroys_flow/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;We all use dozens of pieces of software a day - email clients, web browser, email clients &lt;em&gt;inside&lt;/em&gt; web browsers. Cameras and chat, digital art tools and spreadsheets. We switch between them dozens of times, and when we really get into a task, when can sometimes achieve a state of &lt;a href=&#34;wikipedia_link&#34;&gt;flow&lt;/a&gt;, a state of satisfying and effective effort that lets us accomplish more than we thought and enjoy it all the way.&lt;/p&gt;
&lt;p&gt;But then you add another layer to your Photoshop project, or attach a picture to an email. The beachball starts spinning, the text box gets laggy, and you start thinking about opening that Twitter tab again.&lt;/p&gt;
&lt;p&gt;This is one of the reasons I still use &lt;a href=&#34;_vim_link_&#34;&gt;vim&lt;/a&gt; after all these years. Even in large software projects with plugins for syntax highlighting, autocomplete and build-on-save, the navigation and input remain quick. But not always - vim on my home computer feels faster and more responsive than my work computer, because I&amp;rsquo;m asking it to do less. When compared to a product like IntelliJ/GoLand, it&amp;rsquo;s like a car against a horse. Very few editors are both as fast and as capable, and I can thereby keep going as long as the work requires.&lt;/p&gt;
&lt;p&gt;Every time your software lags, it&amp;rsquo;s a chance for your user to lose their state. Every time you make them move the mouse to open a menu, they have to go away from their task. Put your menus in context with the work, not in a toolbar. Make your software as quick and as smooth as possible. This is what makes people love software, not flashy features.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Beef Barley Mushroom Soup</title>
      <link>/blog/beefbarley/</link>
      <pubDate>Mon, 15 Jan 2018 14:57:58 -0800</pubDate>
      
      <guid>/blog/beefbarley/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;This is adapted from Tom Lacalamita&amp;rsquo;s &lt;em&gt;Pressure Cookers for Dummies&lt;/em&gt;, but you should use the &lt;a href=&#34;https://seriouseats.com&#34;&gt;Serious Eats&lt;/a&gt; method of browning stew beef in one large piece, then cubing it before cooking, for better results.&lt;/p&gt;
&lt;h2 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1/2 lb stew meat&lt;/li&gt;
&lt;li&gt;3 carrots, thinly sliced&lt;/li&gt;
&lt;li&gt;3 stalks of celery, sliced&lt;/li&gt;
&lt;li&gt;1/2 lb of mixed mushrooms, trimmed and quartered&lt;/li&gt;
&lt;li&gt;1 cup barley&lt;/li&gt;
&lt;li&gt;2 or 3 bay leaves&lt;/li&gt;
&lt;li&gt;8 cups of liquid, stock preferred&lt;/li&gt;
&lt;li&gt;Chopped parsley&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;steps&#34;&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Cook onion in oil until fragrant&lt;/li&gt;
&lt;li&gt;Add the meat and cook until browned. Remove and chop into small cubes.&lt;/li&gt;
&lt;li&gt;Add the carrots, celery, barley, mushrooms, meat, stock, and bay leaf to the pressure cooker.&lt;/li&gt;
&lt;li&gt;Bring o high heat and cook for 20 minutes. Release pressure with the quick release method.&lt;/li&gt;
&lt;li&gt;Add parsley, and season the soup with salt and pepper before serving.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Ricotta Chicken</title>
      <link>/blog/ricottachicken/</link>
      <pubDate>Mon, 15 Jan 2018 14:57:33 -0800</pubDate>
      
      <guid>/blog/ricottachicken/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;The secret here is that you can replace the Butter in Butter Chicken with just about any high-fat dairy product. Ricotta, sour cream, greek yogurt will all work. They change the flavor of the dish, but they&amp;rsquo;re all good variations.&lt;/p&gt;
&lt;p&gt;This is adapted from a recipe in Tom Lacalamita&amp;rsquo;s &lt;em&gt;Pressure Cookers for Dummies&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;1.5 lbs chicken, cubed&lt;/li&gt;
&lt;li&gt;1 onion, minced&lt;/li&gt;
&lt;li&gt;1 Tbsp minced ginger&lt;/li&gt;
&lt;li&gt;2 tsp garam masala&lt;/li&gt;
&lt;li&gt;1 small chili, minced&lt;/li&gt;
&lt;li&gt;6 oz tomato paste&lt;/li&gt;
&lt;li&gt;2 cups chicken broth&lt;/li&gt;
&lt;li&gt;2 Tbsp minced cilantro or parsley&lt;/li&gt;
&lt;li&gt;1/2 - 2/3 cup high-fat dairy. Butter chicken is 1/2 cup cream, 3 tbsp butter.
Limes for serving.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;steps&#34;&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Salt and pepper the chicken.&lt;/li&gt;
&lt;li&gt;Cook onion, ginger, chili and garam over high heat in oil, until browned.&lt;/li&gt;
&lt;li&gt;Add the chicken and brown in spices. Add the broth and tomato paste, stir to deglaze the pan.&lt;/li&gt;
&lt;li&gt;Bring the pressure cooker to high heat and cook for 8 minutes. Release pressure with a quick-release method.&lt;/li&gt;
&lt;li&gt;Over low hit, stir in the dairy. Serve over white rice, and squeeze fresh lime juice just before serving.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Pressure Cooker Red Beans and Rice</title>
      <link>/blog/redbeansandrice/</link>
      <pubDate>Mon, 15 Jan 2018 14:38:51 -0800</pubDate>
      
      <guid>/blog/redbeansandrice/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Red beans and rice is simple, fast and good. Use a good hot sauce, use more seasoning than you think you&amp;rsquo;ll need. This recipe scales well - 2 or 3 cups of beans will fill most normal pressure cookers.&lt;/p&gt;
&lt;h2 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2 cups of red beans&lt;/li&gt;
&lt;li&gt;1 large onion&lt;/li&gt;
&lt;li&gt;1 head garlic&lt;/li&gt;
&lt;li&gt;1 cup chopped celery&lt;/li&gt;
&lt;li&gt;1 cup chopped carrots&lt;/li&gt;
&lt;li&gt;1 14.5 oz can of diced tomatoes&lt;/li&gt;
&lt;li&gt;2 tsp hot sauce&lt;/li&gt;
&lt;li&gt;2 or 3 bay leaves&lt;/li&gt;
&lt;li&gt;1 smoked ham hock or 3 strips of bacon or 1 can of (gasp!) spam, etc&lt;/li&gt;
&lt;li&gt;4 cups of water&lt;/li&gt;
&lt;li&gt;1/2 lb of andouille or other smoked sausage&lt;/li&gt;
&lt;li&gt;salt to taste (around 1 or 2 Tbsp)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;steps&#34;&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Rinse the beans, and let them soak while you do the other prep.&lt;/li&gt;
&lt;li&gt;Chop the onion and garlic. Saute in olive oil until fragrant. Add the carrots, tomatoes, hot sauce, and bay leaf. Cook for 2 or 3 minutes. Add the ham hock, beans, water.&lt;/li&gt;
&lt;li&gt;Bring pressure cooker to high pressure, cook for 20 minutes. Release pressure via &amp;ldquo;natural release&amp;rdquo;. If beans are still hard, cook another 5 minutes.&lt;/li&gt;
&lt;li&gt;Add sausage, salt and pepper. Cook with cover off until desired thickness is reached, about 5 to 10 minutes.&lt;/li&gt;
&lt;li&gt;Serve over white rice.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Michelada</title>
      <link>/blog/michelada/</link>
      <pubDate>Sun, 10 Dec 2017 18:52:49 -0800</pubDate>
      
      <guid>/blog/michelada/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Originally from &lt;a href=&#34;http://www.everintransit.com/mexican-michelada-recipe/&#34;&gt;Ever in Transit&lt;/a&gt;, this is the best tasting version of the drink to me. I like to use the tomato juice from a can of peeled plum tomatoes. I use the whole tomatoes to make sauce, and keep the juice to make two micheladas with.&lt;/p&gt;
&lt;h1 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Lager beer&lt;/li&gt;
&lt;li&gt;Tomato Juice&lt;/li&gt;
&lt;li&gt;3-4 splashes Tapatio&lt;/li&gt;
&lt;li&gt;splash of worcestershire sauce&lt;/li&gt;
&lt;li&gt;splash of soy sauce&lt;/li&gt;
&lt;li&gt;juice of one lime or lemon&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Roberta&#39;s Pizza Dough</title>
      <link>/blog/pizzadough/</link>
      <pubDate>Sun, 10 Dec 2017 18:52:38 -0800</pubDate>
      
      <guid>/blog/pizzadough/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;&lt;img src=&#34;/images/pizza.jpg&#34; alt=&#34;Pizza&#34;&gt;&lt;/p&gt;
&lt;p&gt;Originally from &lt;a href=&#34;https://cooking.nytimes.com/recipes/1016230-robertas-pizza-dough&#34;&gt;the NYT&lt;/a&gt;, makes enough for two pizzas.&lt;/p&gt;
&lt;h1 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;306 grams of flour (50/50 00 and bread, or 100% AP)&lt;/li&gt;
&lt;li&gt;2 grams of active dry yeast&lt;/li&gt;
&lt;li&gt;4 grams olive oil&lt;/li&gt;
&lt;li&gt;8 grams of fine sea salt&lt;/li&gt;
&lt;li&gt;200 g of tap water&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&#34;steps&#34;&gt;Steps&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Combine flour and salt in mixing bowl&lt;/li&gt;
&lt;li&gt;Combine water, oil, and yeast in mixing cup. Add to dry ingredients and knead until incorporated. Let rest 15 minutes.&lt;/li&gt;
&lt;li&gt;Knead dough about 3 minutes. Works well with a dough hook.&lt;/li&gt;
&lt;li&gt;Divide dough in half and shape into two balls. Cover with a damp kitchen towel.&lt;/li&gt;
&lt;li&gt;Let rise. 3 to 4 hours at room temperature, or 8 to 24 hours in the fridge.&lt;/li&gt;
&lt;li&gt;Shape, top, and cook.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Persimmon Roses</title>
      <link>/blog/persimmonrose/</link>
      <pubDate>Mon, 04 Dec 2017 19:58:51 -0800</pubDate>
      
      <guid>/blog/persimmonrose/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;My wife found this recipe on &lt;a href=&#34;https://www.tastemade.com/videos/savory-persimmon-roses&#34;&gt;tastemade.com&lt;/a&gt;. These are fatty, sweet, and delicious. Great brunch food. Makes 4 roses.&lt;/p&gt;
&lt;h2 id=&#34;ingredients&#34;&gt;Ingredients&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;2 persimmons&lt;/li&gt;
&lt;li&gt;1 sheet of puff pastry&lt;/li&gt;
&lt;li&gt;1/4 of a wedge of brie&lt;/li&gt;
&lt;li&gt;4 slices of prosciutto&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;equipment&#34;&gt;Equipment&lt;/h2&gt;
&lt;p&gt;Muffin tin&lt;/p&gt;
&lt;h2 id=&#34;steps&#34;&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Cut the persimmons in half from the top, then cut the tops off of each half. Slice the halves from the bottom to the top, 1/8&amp;quot; or less thick.&lt;/li&gt;
&lt;li&gt;Split each slice of prosciutto long-ways.&lt;/li&gt;
&lt;li&gt;Cut the brie into thin chunks.&lt;/li&gt;
&lt;li&gt;Lay out the puff pastry and roll it gently to increase the size. Cut the sheet into four pieces.&lt;/li&gt;
&lt;li&gt;For each piece of puff pastry, lay the prosciutto, then the brie, then the persimmons down a long edge, overlapping about half of the piece.&lt;/li&gt;
&lt;li&gt;Fold up the uncovered part of the pastry to form a sealed bottom crust of the rose.&lt;/li&gt;
&lt;li&gt;Roll up the pastry to create the &amp;ldquo;rose&amp;rdquo;, then place in the pan.&lt;/li&gt;
&lt;li&gt;Bake for 15 minutes at 400 degrees F, then 30 minutes at 350 degrees F.&lt;/li&gt;
&lt;/ol&gt;

        
        </description>
    </item>
    
    <item>
      <title>Garage Shelves</title>
      <link>/blog/garageshelves/</link>
      <pubDate>Sun, 15 Oct 2017 22:18:16 -0800</pubDate>
      
      <guid>/blog/garageshelves/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I started taking paternity leave on my first daughter&amp;rsquo;s due date. (Un?)fortunately, she was born late, and I found myself with both a lot of free time, and a lot of pent-up anticipatory energy.&lt;/p&gt;
&lt;p&gt;My workshop was a bit of a mess, since I&amp;rsquo;d acquired more tools - including a heavy miter saw and grinder. My mother-in-law was staying with us and helping with chores, so I decided to focus on cleaning up the shop. I built a sturdy set of shelves from scrap lumber and cheap sheathing plywood to hold my bench tools when they&amp;rsquo;re not in use.&lt;/p&gt;
&lt;h2 id=&#34;approach&#34;&gt;Approach&lt;/h2&gt;
&lt;p&gt;The legs were made from two 2x4&amp;rsquo;s screwed together. The cross bars for each shelf were also made of spare 2x4s. Each shelf is made of a single thickness of 15/32&amp;quot; sheathing plywood that I picked up cheap from a big box store.&lt;/p&gt;
&lt;p&gt;Similarly to my &lt;a href=&#34;/blog/workbench/&#34;&gt;workbench&lt;/a&gt;, the inner shelf is cut with gaps for the legs, then slid into the frame at an angle before being set down. Instead of fixing the shelf tops like a table top, I chose to nail them directly to the frame.&lt;/p&gt;
&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;
&lt;p&gt;Ugly, and very functional. I made the upper shelves around my shoulder height, and the lower shelves around waist height, which means each shelf and the floor have plenty of clearance for large storage boxes, tall tools, etc.&lt;/p&gt;
&lt;p&gt;This was way cheaper than the much less-good plastic shelves I bought from a big box store. Make your own garage shelves!&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Guitar Shelf</title>
      <link>/blog/guitarshelf/</link>
      <pubDate>Sun, 13 Aug 2017 22:39:34 -0800</pubDate>
      
      <guid>/blog/guitarshelf/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I have a lot of guitars and ukuleles, and I was also feeling like I hadn&amp;rsquo;t played them enough. When we moved at one point, I sold all my guitars but one, but that honeslty didn&amp;rsquo;t last too long. Now that I own a home, it was high time to build myself a real place to keep them. Keeping instruments close at hand reduces my barriers to practice and play.&lt;/p&gt;
&lt;h2 id=&#34;approach&#34;&gt;Approach&lt;/h2&gt;
&lt;p&gt;I bought some shelf brackets and used some spare 1/2&amp;quot; baltic birch plywood. I measured each of my instruments&amp;rsquo; necks at the nut and added additional space for the thickness of some felt tape that I&amp;rsquo;d be lining the slots with. I left space for two guitars - a steel string and a classical guitar. I also cut space for three smaller instruments - two ukuleles and one &lt;a href=&#34;https://loogguitars.com/collections/loog-pro/products/loog-pro-acoustic&#34;&gt;Loog Pro&lt;/a&gt; three string guitar. Finished with wipe-on polyurethane, which is pretty much how I finish any piece of furniture.&lt;/p&gt;
&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;
&lt;p&gt;&lt;img src=&#34;/images/guitarshelf.jpg&#34; alt=&#34;guitar shelf&#34;&gt;&lt;/p&gt;
&lt;p&gt;A good-looking shelf that I&amp;rsquo;m happy to have in my living room. The studs in our walls are 18&amp;quot; apart, which makes shelves a little awkward. I didn&amp;rsquo;t feel OK going 36&amp;quot; between brackets with such a thin piece of plywood, so the shelf looks kind of over-built.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m playing guitar more often :-)&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Workbench</title>
      <link>/blog/workbench/</link>
      <pubDate>Tue, 03 Nov 2015 22:13:59 -0800</pubDate>
      
      <guid>/blog/workbench/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;Every workshop needs a good work bench. A good bench is stable and flat,
letting you estimate the flatness of your workpiece, and with plenty of space
to hold the work and clamp it down.&lt;/p&gt;
&lt;p&gt;You can buy nice workbenches for many hundreds of dollars, and then you can buy
nice clamps for many more. I wanted to use this project as a learning
experience, though. So I spent my time researching table plans, trying to pick
one that would give me a good surface without being a big expense.&lt;/p&gt;
&lt;p&gt;Since I knew my next project would be a dining table , I needed to make a bench
big enough to build a table top on.&lt;/p&gt;
&lt;h2 id=&#34;plan&#34;&gt;Plan&lt;/h2&gt;
&lt;p&gt;I ended up choosing &lt;a href=&#34;http://www.finewoodworking.com/2009/09/16/still-dont-have-a-workbench-this-one-is-easy&#34;&gt;this plan from Fine
Woodworking&lt;/a&gt;
because it&amp;rsquo;s easy, includes guidelines for a vice, and holes for
&lt;a href=&#34;http://www.rockler.com/workshop-accessories/bench-dogs-and-holddowns&#34;&gt;bench dogs and holddowns&lt;/a&gt;,
which I&amp;rsquo;ve found to be very useful for hand work and routing.&lt;/p&gt;
&lt;h2 id=&#34;results&#34;&gt;Results&lt;/h2&gt;
&lt;p&gt;This plan was truly pretty easy to build, but I think it has a few flaws. The
threaded rod that holds the legs in tension can pop out of tracks in the
stretchers. I found a rubber mallet essential to getting this done. Fastening
the table top with clips and slots feels like overkill to me. I had a lot of
trouble with that, and MDF doesn&amp;rsquo;t really shrink and expand much. I think you
could get away with simple L-brackets, under-tightened.&lt;/p&gt;
&lt;p&gt;The bench is incredibly sturdy, and can be easily tightened up with the
threaded rod. The top is flat and solid - one panel of MDF is also cheap enough
that if you ever totally screw up the top with a tool or some weird chemical,
it&amp;rsquo;s not going to break your wallet to just get more and replace it.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title>Cardsharp</title>
      <link>/blog/cardsharp/</link>
      <pubDate>Thu, 03 Sep 2015 14:57:04 -0800</pubDate>
      
      <guid>/blog/cardsharp/</guid>
      <description>
        
          
          
          
        
        
        &lt;p&gt;I&amp;rsquo;m a big fan of &lt;a href=&#34;http://www.stfj.net&#34;&gt;Zach Gage&amp;rsquo;s&lt;/a&gt; games. He recently released
a game called &amp;ldquo;Sage Solitaire&amp;rdquo; in which solitaire is played with poker hands.
You can make a Full House, Straight, Straight Flush, etc, to clear cards from
the board. To choose what hands to make, it&amp;rsquo;s pretty important to know what
cards remain in the deck, much as it can be in poker.&lt;/p&gt;
&lt;p&gt;I wrote &lt;a href=&#34;https://github.com/joeblubaugh/cardsharp&#34;&gt;Cardsharp&lt;/a&gt; to help me keep
track of remaining cards in a Sage Solitaire deck. It&amp;rsquo;s helped improve my score
quite a bit, and gave me some recent Python experience before I started looking
again for backend engineering jobs.&lt;/p&gt;
&lt;p&gt;Cardsharp is build on &lt;a href=&#34;https://bitbucket.org/npcole/npyscreen&#34;&gt;npyscreen&lt;/a&gt;, an ncurses library for
creating interactive Python terminal applications. It&amp;rsquo;s got some great basic layouts like &amp;ldquo;Simple Grid&amp;rdquo;
and a easy-to-understand interaction model, with Action and Data Controllers that interact with shell input.&lt;/p&gt;

        
        </description>
    </item>
    
    <item>
      <title></title>
      <link>/blog/pankaj-notes/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      
      <guid>/blog/pankaj-notes/</guid>
      <description>
        
          
          
          
        
        
        &lt;h1 id=&#34;pankaj-chat&#34;&gt;Pankaj Chat&lt;/h1&gt;
&lt;p&gt;Networking guy at VMWare. Looking into Obs space&lt;/p&gt;
&lt;p&gt;Notes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Honeycomb vs Lightstep&lt;/li&gt;
&lt;li&gt;Observe vs Lightstep&lt;/li&gt;
&lt;li&gt;&amp;ldquo;not the first tool&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;how come you guys didn&amp;rsquo;t start putting things together&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;when people are debugging things, they&amp;rsquo;re looking at everything&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Application-Side observability.&lt;/p&gt;

        
        </description>
    </item>
    
  </channel>
</rss>