<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://montasaurus.com</id>
    <title>Adam Montgomery</title>
    <updated>2025-08-29T22:09:58.118Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <author>
        <name>Adam Montgomery</name>
        <uri>https://montasaurus.com</uri>
    </author>
    <link rel="alternate" href="https://montasaurus.com"/>
    <subtitle>{x} by Adam Montgomery</subtitle>
    <logo>https://montasaurus.com/icon.png</logo>
    <icon>https://montasaurus.com/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Engineer's Guide to the 14th Amendment: Skrmetti]]></title>
        <id>https://montasaurus.com/posts/eng-equal-protection</id>
        <link href="https://montasaurus.com/posts/eng-equal-protection"/>
        <updated>2025-07-29T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[<p>I&#x27;m not a lawyer, but I find US constitutional law fascinating. I&#x27;ve been listening to the Supreme Court oral arguments and reading their decisions for years. Many of the cases are surprisingly easy for a non-lawyer to follow along with.</p>
<p>The Supreme Court issued a <a href="https://www.supremecourt.gov/opinions/24pdf/23-477_2cp3.pdf">decision</a> recently in <span class="small-caps">United States v. Skrmetti</span>, a case challenging the constitutionality of a restriction on puberty-altering drugs. Here&#x27;s a layman / engineer&#x27;s guide to the constitutional concepts in the case and the reasoning in the decision.</p>
<h2>Equal Protection Clause</h2>
<p>The law enacting the restriction was challenged under the Equal Protection Clause of the 14th Amendment, which states:</p>
<blockquote>
<p>[states may not] deny to any person within its jurisdiction the equal protection of the laws</p>
</blockquote>
<p>The 14th Amendment was ratified following the Civil War, and ensured that people could not (legally) be denied rights based on race.</p>
<p>While racial discrimination was a clear motivator for passing the amendment, race is not mentioned in it. Consequently, Equal Protection arguments are used to challenge laws that treat people differently based on characteristics other than race.</p>
<p>Guaranteeing equal treatment sounds uncontroversial, but our laws categorize and treat people differently all the time:</p>
<ul>
<li>We discriminate based on age for access to Medicare</li>
<li>We discriminate based on felony status for access to guns</li>
<li>We discriminate based on sex when registering for the draft</li>
<li>We discriminate based on finances for access to certain investment opportunities as accredited investors</li>
</ul>
<p>In a series of cases since the amendment&#x27;s ratification, the Court has had to grapple with how exactly laws are are able to treat people differently. In addition to ruling on the individual cases, much of the Court&#x27;s role is to provide clear guidance to potential litigants and the hundreds of US Federal Judges that could be involved in these disputes. This guidance gets fleshed out piecemeal as cases are brought before the Court and new edge cases are dealt with.</p>
<p>The result of this process is a complex system where the Court has recognized certain <em>suspect</em> and <em>quasi-suspect</em> classes of characteristics that the Court treats differently. When those classes are implicated, the law being challenged is held to a higher standard of review (also known as the <em>level of scrutiny</em>). The Court has not closed the door to adding new characteristics to the suspect or quasi-suspect classes, but it has not recognized any new ones in over 40 years.</p>
<h2>Levels of Scrutiny</h2>
<p>As a practical matter, the Court&#x27;s choice of which level of scrutiny to apply largely dictates the outcome of the case.</p>
<p>The lowest level is called <strong>Rational Basis</strong>, which means that the legislature just has to have some logical reason that the law will help achieve some legitimate goal (even if only partially, and even if it&#x27;s unfair, arbitrary, or inefficient). It&#x27;s exceptionally unusual for a law to not reach this bar. Laws that do NOT implicate any suspect or quasi-suspect classes get this level of scrutiny.</p>
<p>The highest level is called <strong>Strict Scrutiny</strong>. This requires that the law is necessary to achieve a &quot;compelling state interest&quot; and is the least restrictive way possible to achieve it. The common saying is &quot;strict in theory, fatal in fact&quot; because almost no law passes this bar. The law is presumed unconstitutional from the outset, and the burden of proof is on the government to prove it can&#x27;t be done any better way. Laws implicating the suspect classes get this level of scrutiny. The suspect classes recognized by the Court that apply to federal laws are race and national origin.</p>
<p>A middle level, called <strong>Intermediate Scrutiny</strong> was added later. It&#x27;s a much fuzzier and subjective standard, under which the government must show that the &quot;classification serves important governmental objectives&quot; and that &quot;the discriminatory means employed are substantially related to the achievement of those objectives&quot;. Laws implicating quasi-suspect classes get this level of treatment. Only sex and illegitimacy (children born out of wedlock) have been treated as a quasi-suspect classes by the Court.</p>
<p>You won&#x27;t find any mention of scrutiny levels in the Constitution. They&#x27;re a purely judicial creation, along with the concept of suspect and quasi-suspect classes. The concept of scrutiny standards was first mentioned in a (now famous) footnote of a Supreme Court <a href="https://www.oyez.org/cases/1900-1940/304us144">decision</a> about an interstate milk shipping requirement in <span class="small-caps">United States v. Carolene Products Company</span>.</p>
<h2>Skrmetti Argument</h2>
<p>The Skrmetti case dealt with a Tennessee law banning the prescription of puberty blockers to minors experiencing gender dysphoria. As with most cases, it comes to the Supreme Court on appeal after a District Court and appellate court (Sixth Circuit, in this case) have issued judgements and opinions.</p>
<p>The District Court found that transgender status should be considered a new quasi-suspect class alongside sex. Since laws implicating quasi-suspect classes receive Intermediate Scrutiny, the District Court applied that standard. It determined that the law was unlikely to pass that heightened bar of review, and granted a preliminary injunction to prevent the law from going into effect.</p>
<p>On appeal, the Sixth Circuit reversed the District Court. It found that since neither the Supreme Court nor the Sixth Circuit has recognized transgender status as a quasi-suspect class, there was no basis for a heightened standard of review. The Sixth Circuit looked at 2 considerations for adding suspect classes that the Supreme Court had previously mentioned: whether the characteristic is immutable and whether the group in question is &quot;politically powerless&quot;. The court ruled that transgenderism was not immutable, pointing to cases of detransitioners that the law&#x27;s challengers did not contest. The court also held that transgender people were not politically powerless, pointing to the fact that the then-President, Department of Justice, and major medical associations supported the law&#x27;s challengers.</p>
<p>The Supreme Court agreed to hear an appeal of the Sixth Circuit&#x27;s decision, which is called <strong>granting certiorari</strong>. The Court reviews legal reasoning in the courts below it <em>de novo</em>, meaning they proceed through their own analysis of the law from scratch.</p>
<p>Starting fresh, the Court first considered the obvious discrimination based on age—this restriction applies only to children under the age of 18. Since age is not a suspect or quasi-suspect class, this differentiation does not trigger heightened review.</p>
<p>Next, the Court considered whether the law discriminates based on sex. The law&#x27;s challengers argued that there is de-facto sex discrimination because the law makes it legal to provide a boy testosterone to live as a boy, but illegal to provide a girl testosterone to live as a boy. They tried to draw parallels to the Courts ruling in <span class="small-caps"><a href="https://www.oyez.org/cases/1966/395">Loving v. Virginia</a></span>, which banned laws restricting inter-racial marriage. The argument there was similar—it&#x27;s an Equal Protection violation to allow a white person to marry a white person but prohibit a black person from marrying a white person.</p>
<p>In this case, the Court found that the law did not discriminate based on sex, but on the medical diagnosis. Since the law bans prescribing testosterone to anyone (boy or girl) to treat gender dysphoria, and allows prescribing it to anyone for any other diagnosis, the Court found that the diagnosis is the actual differentiator. The Court had previously found that regulating sex-specific medical conditions does not necessarily entail sex-based discrimination in a 1974 case called <span class="small-caps"><a href="https://www.oyez.org/cases/1973/73-640">Geduldig v. Aiello</a></span> regarding pregnancy coverage. Since medical diagnosis is not a suspect or quasi-suspect class, this also does not trigger heightened review.</p>
<p>There was some language in the legislative record about the legislature&#x27;s desire to &quot;[encourage] minors to appreciate their sex&quot;. Laws seeking to enforce sex stereotypes or that conceal an intent to discriminate based on sex can be treated as discriminatory and subjected to Intermediate Scrutiny. The Court looked at this and decided there was not convincing evidence of this, pointing to additional language showing that the legislature had concerns about the safety and potential harms of the procedures. Since those are permissible intents, this also did not trigger heightened review.</p>
<p>Applying the low bar of Rational Basis, the Court affirmed the decision of the Sixth Circuit and allowed the law to go into effect. Because the Court did not &quot;reach&quot; consideration of what differentiation is allowed based on transgender status, that question remains unresolved.</p>]]></summary>
    </entry>
    <entry>
        <title type="html"><![CDATA[How I Build]]></title>
        <id>https://montasaurus.com/posts/how-i-build</id>
        <link href="https://montasaurus.com/posts/how-i-build"/>
        <updated>2025-01-21T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[<p>This is a guide to my tools and processes for building software. I&#x27;m writing this both to share what I&#x27;ve found with other humans, and as guidance for LLMs that interact with me &amp; share my tools.</p>
<p>I&#x27;ve found these tools helpful building &quot;pre-LLM&quot; software in small and large startups, and for LLM-enabled applications like I&#x27;m working on <a href="https://eatgordon.com">now</a>. My tool choices are largely informed by how I <a href="#structure">structure software</a>. I don&#x27;t spend much time justifying my tools here. That&#x27;s both for brevity, and because I think tools either resonate with a person or they don&#x27;t . It&#x27;s also not exhaustive—these are what I keep in the tool chest, not the attic.<sup><a href="#user-content-fn-other" id="user-content-fnref-other" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup></p>
<h2><span id="laptop">My Laptop</span></h2>
<p>My primary development environment. Working roughly from the bottom up:</p>
<ul>
<li>
<p>I use a MacBook Air 15&quot;. I used to always go with a beefed up MacBook Pro, but switched and haven&#x27;t missed it. If I do anything compute intensive, I usually just use <a href="#servers">another computer</a>.</p>
</li>
<li>
<p>I use <a href="https://karabiner-elements.pqrs.org/">Karabiner-Elements</a> to map <code>caps_lock -&gt; left-ctrl</code>, <code>left-ctrl -&gt; esc</code>, and <code>right-cmd + hjkl -&gt; arrow keys</code>. This makes <code>ctrl+{x}</code> more ergonomic, <code>esc</code> more accessible, and gives me an alternative to the arrow keys. The first two can be done with system preferences alone.</p>
</li>
<li>
<p>I use <a href="https://rectangleapp.com/">Rectangle</a> as a window manager. It&#x27;s lightweight and fast. I used <a href="https://github.com/nikitabobko/AeroSpace">AeroSpace</a> before, but had some <a href="https://github.com/nikitabobko/AeroSpace/issues/131">issues</a> with it and switched. I used <a href="https://mizage.com/divvy/">divvy</a> before that, and it was ok as well.</p>
</li>
<li>
<p>I use <a href="https://getdrafts.com/">Drafts</a> to save notes, and as a general scratchpad. The quick capture is great, and I have it globally bound to <code>ctrl+d</code>. I have it on my phone as well, and use it as a simple workout tracker. It&#x27;s got a lot more functionality that I barely scratch the surface of.</p>
</li>
<li>
<p>I use <a href="https://orbstack.dev/">OrbStack</a> for Docker containers and local long-running services. I&#x27;ve <a href="https://x.com/montasaurus_rex/status/1866519145472270831">sung it&#x27;s praises</a> before.</p>
</li>
<li>
<p>I use <a href="https://wezfurlong.org/wezterm/index.html">Wezterm</a> as my terminal. I like it because I can write custom functions in Lua to e.g. run a command when I press a key or set up a specific layout per project. I used <a href="https://sw.kovidgoyal.net/kitty/">kitty</a> before, which is also great but didn&#x27;t have as much flexibility.</p>
</li>
<li>
<p>I use <a href="https://github.com/jordanbaird/Ice">Ice</a> to manage my menu bar. I used Bartender until they <a href="https://www.reddit.com/r/macapps/comments/1d7zjv8/bartender_5_not_safe_anymore_warning_from/">changed control without notifying users</a>.</p>
</li>
<li>
<p>I use <a href="https://cleanshot.com/">CleanShot</a> for screenshots and screen recording. It&#x27;s simple and easy to use.</p>
</li>
<li>
<p>I use <a href="https://www.alfredapp.com/">Alfred</a> as a more powerful Spotlight replacement and launcher.</p>
</li>
</ul>
<h2><span id="servers">My Servers</span></h2>
<p>By this, I mean computers that I own or rent to run services on.</p>
<ul>
<li>
<p>I use <a href="https://tailscale.com/">Tailscale</a> for a private network between my devices and services. It simplifies connecting to locally running services, and exit nodes are handy when traveling. I run a <a href="https://tailscale.com/kb/1282/docker">tailscale router in docker</a> on other subnets that I want to connect to privately.</p>
</li>
<li>
<p>I use <a href="https://fly.io/">fly.io</a> for hosting internal services and prototypes. It&#x27;s simple to spin up an app that sleeps when not in use, and has private networking for running internal services. It has somewhat frequent small scale service interruptions, but I don&#x27;t have to spend time dealing with IAM roles or enabling GCP APIs.</p>
</li>
<li>
<p>I use AWS when I can&#x27;t use fly.</p>
</li>
<li>
<p>I use GCP when I can&#x27;t use AWS.</p>
</li>
<li>
<p>I run <a href="https://github.com/mtlynch/picoshare">PicoShare</a> to share links to files, like product demo videos that I email to larger groups.<sup><a href="#user-content-fn-mtlynch" id="user-content-fnref-mtlynch" data-footnote-ref="true" aria-describedby="footnote-label">2</a></sup></p>
</li>
</ul>
<ul>
<li>
<p>I run <a href="https://archivebox.io/">ArchiveBox</a> by the talented <a href="https://sweeting.me/">Nick Sweeting</a> to save webpages and prevent link rot.</p>
</li>
<li>
<p>I use <a href="https://www.cloudflare.com/">Cloudflare</a> for DNS and SSL.</p>
</li>
<li>
<p>I handle most provisioning across providers with <a href="https://www.hashicorp.com/products/terraform">Terraform Cloud</a>. This includes VPS, DNS, buckets, managed services, and anything else I configure +/- once.</p>
</li>
</ul>
<h2><span id="devenv">Development Environment</span></h2>
<ul>
<li>I use <a href="https://github.com/direnv/direnv">direnv</a> to configure my env per project.</li>
<li>I use a consistent repo structure whenever I can. I have a <code>mkrepo</code> alias configured to create it from a <a href="https://copier.readthedocs.io/en/stable/">copier</a> template. It includes:<!-- -->
<ul>
<li><code>/bin</code> to store binaries. I add it to PATH via <code>direnv</code>.</li>
<li><code>/etc</code> for configuration. I store non-sensitive config in <code>shared.env</code>, test specific config in <code>test.env</code>, and sensitive config in <code>secret.env</code>. <code>secret.env</code> is gitignored. <code>shared.env</code> and <code>secret.env</code> are sourced via <code>direnv</code>.</li>
<li><code>/pkgs</code> for code if it&#x27;s a monorepo, which I prefer.</li>
<li><code>/var</code> for runtime data. It&#x27;s gitignored, and can be cleaned to reset my dev environment.</li>
</ul>
</li>
<li>I use <a href="https://github.com/casey/just">just</a> as a task runner. It&#x27;s similar to <code>Make</code>, with some improvements (e.g. no <code>.PHONY</code>).</li>
<li>I use <a href="https://en.wikipedia.org/wiki/Tab_key">tabs</a> wherever possible because I&#x27;m a craftsman and have an opinion about such matters.</li>
<li>I use <a href="https://editorconfig.org/">EditorConfig</a> to codify my spacing opinions.</li>
<li>I use VS Code as my IDE.</li>
<li>I switch AI assistants often in search of nirvana.</li>
</ul>
<h2><span id="cli">Command Line Tools</span></h2>
<p>Mostly installed through Homebrew or <a href="https://docs.astral.sh/uv/">uv</a>.</p>
<ul>
<li>
<p>I use <code>zsh</code> as my shell with <a href="https://ohmyz.sh/">oh my zsh</a>. I switched from <code>bash</code> so long ago that I don&#x27;t remember why. I tried <code>fish</code> but didn&#x27;t see a reason to pay the switching cost.</p>
</li>
<li>
<p>I use <a href="https://www.chezmoi.io/">chezmoi</a> to manage my dotfiles. I don&#x27;t love the workflow I use with it, especially once I started using templates. It&#x27;s a bit manual to keep updated, but I like having my config in git. I use <code>brew bundle dump --global --force</code> to include my brew installs, which also picks up my VSCode extensions.</p>
</li>
<li>
<p>I use <a href="https://llm.datasette.io/en/stable/">llm</a><sup><a href="#user-content-fn-simon" id="user-content-fnref-simon" data-footnote-ref="true" aria-describedby="footnote-label">3</a></sup> to prompt via the command line and pipe to and from my other tools.<sup><a href="#user-content-fn-llmtools" id="user-content-fnref-llmtools" data-footnote-ref="true" aria-describedby="footnote-label">4</a></sup> I use <a href="https://openrouter.ai/">OpenRouter</a> to use different models depending on the task.<sup><a href="#user-content-fn-openrouter" id="user-content-fnref-openrouter" data-footnote-ref="true" aria-describedby="footnote-label">5</a></sup></p>
</li>
</ul>
<ul>
<li>I use <a href="https://github.com/extrawurst/gitui">gitui</a> for git. It&#x27;s simple and fast with good keyboard shortcuts.</li>
<li>I use <a href="https://github.com/sharkdp/bat">bat</a> as a replacement for <code>cat</code>. It gives me syntax color and paging.</li>
<li>I use <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a> for searching in files.</li>
<li>I use <a href="https://github.com/sharkdp/fd">fd</a> for finding files.</li>
<li>I use <a href="https://github.com/junegunn/fzf">fzf</a> for finding <em>things</em> (like git branches) and for command history. I used <a href="https://atuin.sh/">atuin</a> for command history before, but the search wasn&#x27;t as good.</li>
<li>I use <a href="https://github.com/lsd-rs/lsd">lsd</a> for listing directories.</li>
<li>I use <a href="https://github.com/mr-karan/doggo">doggo</a> as a better <code>dig</code>.</li>
<li>I use <a href="https://github.com/dundee/gdu">gdu</a> for disk usage.</li>
<li>I use <a href="https://github.com/httpie/cli">httpie</a> for HTTP requests.</li>
<li>I use <a href="https://github.com/brona/iproute2mac">iproute2mac</a> for better networking commands, like <code>ip address show</code> and <code>ip route get 8.8.8.8</code>.</li>
<li>I use <a href="https://jqlang.github.io/jq/">jq</a> for JSON, paired with <a href="https://github.com/simonw/llm-jq">llm-jq</a> for generating filters.</li>
<li>I use <a href="https://github.com/ajeetdsouza/zoxide">zoxide</a> for jumping to directories.</li>
<li>I use <a href="https://github.com/shssoichiro/oxipng">oxipng</a> for optimizing PNGs.</li>
</ul>
<h2><span id="backend-services">Backend Services</span></h2>
<p>I like &quot;boring&quot; and reliable here. These are the most commonly used.</p>
<ul>
<li>I use <a href="https://www.postgresql.org/">Postgres</a> for persistence unless there&#x27;s a compelling reason not to.</li>
<li>I&#x27;ve used <code>redis</code> for simple pub/sub, key/value, and caching. I started trying out <a href="https://github.com/valkey/valkey">valkey</a> after the <a href="https://redis.io/blog/what-redis-license-change-means-for-our-managed-service-providers/">license change</a>.</li>
<li>I use <a href="https://nginx.org/">nginx</a> for serving static sites.</li>
<li>I use <a href="https://www.rabbitmq.com/">RabbitMQ</a> if I need more complicated queueing.</li>
<li>I use <a href="https://www.elastic.co/">Elasticsearch</a> if I must.</li>
</ul>
<h2><span id="python">Backend (Python)</span></h2>
<p>I mostly write backends in Python, unless there&#x27;s a good reason not to.</p>
<ul>
<li>
<p>I use <a href="https://docs.astral.sh/uv/">uv</a> for virtual environments, dependency management, tool management, building packages, and running Python scripts. It&#x27;s a step function improvement over what we had before.</p>
</li>
<li>
<p>I use <a href="https://docs.astral.sh/ruff/">ruff</a> for linting and formatting. Like everything from <a href="https://astral.sh/">Astral</a>, it&#x27;s so fast that I introduce errors to verify it&#x27;s working.</p>
</li>
<li>
<p>I use <a href="https://github.com/microsoft/pylance-release">Pylance</a> for type checking in <code>strict</code> mode. Microsoft restricts it from being used outside of VS Code, and it&#x27;s a big reason I still use VS Code. Astral is working on a type checker, and with their track record, I&#x27;m very optimistic about it.<sup><a href="#user-content-fn-astral" id="user-content-fnref-astral" data-footnote-ref="true" aria-describedby="footnote-label">6</a></sup></p>
</li>
</ul>
<ul>
<li>
<p>I use <a href="https://docs.pydantic.dev/latest/">Pydantic</a> and <a href="https://www.attrs.org/en/stable/">attrs</a> for defining types. I use <code>pydantic</code> for serializing and parsing across boundaries (like the API), and <code>attrs</code> for internal types. Having multiple models feels pedantic at times, but it&#x27;s largely born of my philosophy around types. I used <code>dataclass</code> before, but <code>attrs</code> is <a href="https://www.attrs.org/en/stable/why.html#data-classes">effectively a superset</a>.</p>
</li>
<li>
<p>I use <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/">Pydantic Settings</a> to strongly type environment variables.</p>
</li>
<li>
<p>I use <a href="https://fastapi.tiangolo.com/">fastapi</a> as a web framework. I generate an OpenAPI spec from it that feeds the <a href="#frontend">frontend</a>.</p>
</li>
<li>
<p>I use <a href="https://github.com/prisma/prisma">prisma</a> as an ORM. Unusual choice for Python, but I like the DSL and the types that the <a href="https://github.com/RobertCraigie/prisma-client-py">unofficial Python port</a> generates are better than the other ORMs I&#x27;ve tried.</p>
</li>
<li>
<p>I use <a href="https://ai.pydantic.dev/">PydanticAI</a> to call AI models. It&#x27;s simple, intuitive, and let me rip out many lines of boilerplate around tool calling. I&#x27;d highly suggest starting with this library instead of a more expansive &quot;framework&quot;. I intially assumed that interacting with LLMs was complicated, and would benefit from a framework the same way HTTP services do. I now think of it as more of a <a href="https://fs.blog/mental-model-complex-adaptive-systems/">complex</a> interaction, where the framework is adding a layer of indirection and distracting you from your usual tools. PydanticAI is simple enough to read and even modify—I use a custom fork where I&#x27;ve hacked in support for passing images.</p>
</li>
<li>
<p>I use <a href="https://github.com/hynek/stamina">stamina</a> to retry things that fail intermittently.<sup><a href="#user-content-fn-hynek" id="user-content-fnref-hynek" data-footnote-ref="true" aria-describedby="footnote-label">7</a></sup> It&#x27;s <a href="https://tenacity.readthedocs.io/en/latest/">tenacity</a> with the right defaults set.</p>
</li>
</ul>
<ul>
<li>
<p>I use my fork of <a href="https://github.com/montasaurus/result">result</a> when I want to bubble up nested <a href="#exceptions">error states</a>.</p>
</li>
<li>
<p>I use <a href="https://github.com/encode/httpx">httpx</a> as an async HTTP client.</p>
</li>
<li>
<p>I use <a href="https://github.com/talkiq/gcloud-aio">gcloud-aio</a> for better typing when using GCP.</p>
</li>
<li>
<p>I use <a href="https://github.com/fern-api/fern">fern</a> to generate Python SDKs for APIs that don&#x27;t have an SDK or have a clunky / untyped one. I usually track down an OpenAPI spec and generate an SDK locally.</p>
</li>
<li>
<p>I use <a href="https://www.structlog.org/en/stable/">structlog</a> as a logging library. Nicely formatted, stuctured, and eliminates the need for string interpolation in log messages.</p>
</li>
<li>
<p>I use <a href="https://logfire.pydantic.dev/docs/">Logfire</a> for observability. It&#x27;s a cleaner and less expensive Datadog from the strong <a href="https://pydantic.dev/">Pydantic</a> team.<sup><a href="#user-content-fn-pydantic" id="user-content-fnref-pydantic" data-footnote-ref="true" aria-describedby="footnote-label">8</a></sup></p>
</li>
</ul>
<ul>
<li>
<p>I use <a href="https://sentry.io/">Sentry</a> for error reporting.</p>
</li>
<li>
<p>I use <a href="https://vcrpy.readthedocs.io/en/latest/">VCR.py</a> to record and replay HTTP interactions—mainly for testing with <a href="https://github.com/kiwicom/pytest-recording">pytest-recording</a> but also for evaluating tool calling models.</p>
</li>
<li>
<p>I use <a href="https://15r10nk.github.io/inline-snapshot/latest/">inline-snapshot</a> to automate assertions in tests. It works really well in conjuction with VCR.py when testing LLM output, like I <a href="https://github.com/simonw/llm-openrouter/commit/8fbcfec92ec84f95e4125f89dbb9949efcab7975#diff-2443e35ac2970c672eb7de7682ee9d6d3d05a1c622706a6022301d1540e2b7e8">added here</a>.</p>
</li>
</ul>
<h2><span id="frontend">Frontend</span></h2>
<p>Mainly web apps, mobile apps, and static landing pages.</p>
<ul>
<li>I use <a href="https://www.typescriptlang.org/">Typescript</a> and enjoy it.</li>
<li>I use <a href="https://react.dev/">React</a> and have never felt a need to switch.</li>
<li>I use <a href="https://nextjs.org/">Next.js</a>, but I don&#x27;t enjoy it. It&#x27;s largely convention based, which isn&#x27;t <a href="#configuration">my style</a>. I work around it by using <code>output: export</code> and hosting the static files with <code>nginx</code> if I can.</li>
<li>I build REST-ish APIs to feed the frontend. I&#x27;ve used <a href="https://graphql.org/">GraphQL</a> before, but it feels more like a tradeoff than a strict improvement.</li>
<li>I use <a href="https://tanstack.com/query/latest/docs/framework/react/overview">React Query</a> for fetching, mutating, and caching. Some of the defaults are surprising.</li>
<li>I use <a href="https://heyapi.dev/">Hey API</a> to generate types and React Query config from the backend OpenAPI specs. It&#x27;s been in flux a lot since it forked from <a href="https://github.com/ferdikoomen/openapi-typescript-codegen">openapi-typescript-codegen</a>, but it&#x27;s simple and well-typed.</li>
<li>I use <a href="https://expo.dev/accounts/montasaurus">Expo</a> for building React Native apps. For someone with very little native mobile experience, it&#x27;s been great.<sup><a href="#user-content-fn-twir" id="user-content-fnref-twir" data-footnote-ref="true" aria-describedby="footnote-label">9</a></sup></li>
</ul>
<ul>
<li>I use <a href="https://www.refactoringui.com/">Refactoring UI</a> as a resource for designing UIs. I was much worse at worse at laying out interfaces before reading it.<sup><a href="#user-content-fn-litt" id="user-content-fnref-litt" data-footnote-ref="true" aria-describedby="footnote-label">10</a></sup></li>
</ul>
<h2><span id="structure">How I Structure Software</span></h2>
<p>These are my opinions and beliefs on effectively structuring software. They&#x27;re born of my experience and style of building. I believe they are &quot;right&quot; in a sense, but not the only right way. They are mainly in the context of building B2B/B2C application code, and not e.g. firmware for pacemakers.</p>
<h3>Types</h3>
<ul>
<li>
<p>Type checking is a great superpower of building software. A good type system allows us to translate our understanding of a domain into a set of precise rules, and the type checker ensures our that understanding remains consistent as we evolve the system. It&#x27;s a powerful tool to ensure that we are in control of the complexity in our projects. I use strong typing wherever possible, and highly favor libraries that do as well.</p>
</li>
<li>
<p>It&#x27;s ok to have duplicative-looking types for different layers of a project—e.g. ORM, API schema, and business logic. Types need to be easy to change and reason about so we can adapt them to our evolving requirements. Types near the edge (like API schemas and database tables) are inherently less flexible. It&#x27;s often better to explicitly map types between these layers rather than create a &quot;franken-type&quot; and allow the least flexible layer to calcify the rest.<sup><a href="#user-content-fn-dry" id="user-content-fnref-dry" data-footnote-ref="true" aria-describedby="footnote-label">11</a></sup></p>
</li>
</ul>
<ul>
<li><a href="https://benhoyt.com/writings/python-pattern-matching/">Pattern matching</a> is my favorite control flow tool. It&#x27;s versatile because the concept maps well to human decision making, where we categorize something and then have rules based on that category. It&#x27;s powerful because we can use a type system to ensure we&#x27;re exhaustively handling every category at every decision point, especially as we add new categories. I use union types, discriminated unions, and <code>match</code> statements liberally.<sup><a href="#user-content-fn-match" id="user-content-fnref-match" data-footnote-ref="true" aria-describedby="footnote-label">12</a></sup></li>
</ul>
<h3><span id="exceptions">Exceptions</span></h3>
<ul>
<li>Current generation LLMs have a bad habit of catching exceptions, logging them, and returning a default value or object. That usually compounds the issue. Good software handles the exceptions it anticipates, and loudly reports the ones it doesn&#x27;t. A <code>500 Internal Server Error</code> response is loud and clear. A <code>200 OK</code> with an empty list is just confusing. Exceptions we anticipate can be incorporated in the type system with a result type<sup><a href="#user-content-fn-resulterror" id="user-content-fnref-resulterror" data-footnote-ref="true" aria-describedby="footnote-label">13</a></sup>, allowing callers to opt in to more specific error handling (e.g. a more descriptive error message).</li>
</ul>
<h3><span id="configuration">Configuration</span></h3>
<ul>
<li>
<p>I don&#x27;t like &quot;convention over configuration&quot; and generally avoid libraries/platforms that use a lot of &quot;magic&quot; like metaprogramming. The magic is a liability when learning or coming back to a technology, and the pain of explicitly writing configuration is going to 0 with LLMs. Good libraries expose their capabilities in their function signatures and don&#x27;t require regularly referencing a book of incantations.</p>
</li>
<li>
<p>I like understanding how the tools I use work. I almost never use a starter template or automated install script. If it&#x27;s complicated enough to warrant automating configuration, it&#x27;s worth taking a few minutes to understand what tradeoffs it&#x27;s making.</p>
</li>
</ul>
<h3>Organization</h3>
<ul>
<li>I&#x27;m not picky about code organization below the module level. Files and folders are skeumorphic and feel like an antiquated way to organize code, anyway.</li>
</ul>
<h3>Comments</h3>
<ul>
<li>Current generation LLMs are way too comment happy. Comments should be reserved for decisions that are not intuitively obvious from the code. These include decisions to omit more robust handling (i.e. <code>TODO</code>s), and any algorithmic choices that would make a good leetcode question.</li>
</ul>
<h3>Testing</h3>
<ul>
<li>Tests should be added for non-trivial algorithms that are easy to test in isolation, for functionality that is likely to regress, and for functionality that <em>has</em> regressed. Historically, automated testing and other quality assurance methods have had significant tradeoffs and a mixed return on investment. I think we&#x27;ll see a lot of improvements to the effectiveness of QA as LLMs get more involved.</li>
</ul>
<h2><span id="updates">Updates</span></h2>
<ul>
<li>March 2025: I switched back to <a href="https://github.com/junegunn/fzf">fzf</a> for command history, and to <a href="https://rectangleapp.com/">Rectangle</a> as a window manager.</li>
</ul>
<section data-footnotes="true" class="footnotes"><h2 class="sr-only" id="footnote-label">Footnotes</h2>
<ol>
<li id="user-content-fn-other">
<p>This guide mostly applies to B2B/B2C &quot;web&quot;-based applications—my tools for e.g. PKI and announcing BGP are kept in the attic. <a href="#user-content-fnref-other" data-footnote-backref="" aria-label="Back to reference 1" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-mtlynch">
<p>PicoShare&#x27;s creator <a href="https://mtlynch.io/">Michael Lynch</a> has a very honest and transparent blog about building products / businesses that I enjoy. <a href="#user-content-fnref-mtlynch" data-footnote-backref="" aria-label="Back to reference 2" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-simon">
<p><code>llm</code>&#x27;s creator <a href="https://simonwillison.net/">Simon Willison</a> has a high-signal/high-volume blog that I read every day. It&#x27;s a great resource, particularly for working with LLMs. His style of working in the open is inspiring, and sparks a lot of ideas for me. <a href="https://www.danielcorin.com/">Dan Corin</a> is another great example of building in the open like this. <a href="#user-content-fnref-simon" data-footnote-backref="" aria-label="Back to reference 3" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-llmtools">
<p>There are a bunch of great plugins for <code>llm</code>, and you can write your own. I use a wrapper I wrote to <a href="https://gist.github.com/montasaurus/5ccbe453ef863f702291e763b1b63daf">generate terminal commands</a> and my sister&#x27;s <a href="https://github.com/marissamarym/commitm">commitm</a> to generate commit messages. It pairs well with <a href="https://github.com/simonw/files-to-prompt">files-to-prompt</a> for including file context. <a href="#user-content-fnref-llmtools" data-footnote-backref="" aria-label="Back to reference 4" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-openrouter">
<p><a href="https://openrouter.ai/">OpenRouter</a>&#x27;s got a stacked team. I&#x27;m very bullish on them &amp; happy to call them friends. <a href="#user-content-fnref-openrouter" data-footnote-backref="" aria-label="Back to reference 5" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-astral">
<p>Obviously, I&#x27;m bullish on Astral. <a href="https://x.com/charliermarsh">Charlie Marsh</a> is building a team of wizards, including <a href="https://github.com/sharkdp">sharkdp</a> and <a href="https://github.com/burntsushi">BurntSushi</a>, who authored tools on this list. <a href="#user-content-fnref-astral" data-footnote-backref="" aria-label="Back to reference 6" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-hynek">
<p><a href="https://hynek.me/">Hynek Schlawack</a> created <code>attrs</code>, <code>stamina</code>, and <code>structlog</code> on this list. I resonate well with his approach to structuring software. His blog and the documentation on his projects are excellent resources, especially on Python and Docker. <a href="#user-content-fnref-hynek" data-footnote-backref="" aria-label="Back to reference 7" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-pydantic">
<p><a href="https://pydantic.dev/">Pydantic</a> is another great team pushing the Python ecosystem forward. <a href="https://github.com/samuelcolvin">Samuel Colvin</a> and the team have great technical taste that shows across their projects. <a href="#user-content-fnref-pydantic" data-footnote-backref="" aria-label="Back to reference 8" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-twir">
<p>The <a href="https://thisweekinreact.com/newsletter">This Week in React</a> newsletter is a good source for keeping up to date with React Native news. <a href="#user-content-fnref-twir" data-footnote-backref="" aria-label="Back to reference 9" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-litt">
<p>I think UIs are going to change a lot as AI improves. <a href="https://www.geoffreylitt.com/">Geoffrey Litt</a> does insightful work on the topic of &quot;malleable software&quot; that I suggest reading. <a href="#user-content-fnref-litt" data-footnote-backref="" aria-label="Back to reference 10" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-dry">
<p>I see a lot of natural hesitation to do this because of a misapplication of the <a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself">DRY Principle</a>. There&#x27;s a difference between being explicit and being duplicative. The cost of being explicit is going to 0 with LLMs, and we can harness the benefits for free. <a href="#user-content-fnref-dry" data-footnote-backref="" aria-label="Back to reference 11" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-match">
<p>In Python, pattern matching is newer and a bit unintuitive. <a href="https://x.com/raymondh">Raymond Hettinger</a> has a talk explaining <a href="https://www.youtube.com/watch?v=ZTvwxXL37XI">the quirks</a> and some complicated workarounds. I use it more simply for matching types. I always check for exhaustiveness, and exclude a default case (or use <code>assert_never</code>) so the type checker tells me when I am missing a case. <a href="#user-content-fnref-match" data-footnote-backref="" aria-label="Back to reference 12" class="data-footnote-backref">↩︎</a></p>
</li>
<li id="user-content-fn-resulterror">
<p>I use my fork of <a href="https://github.com/montasaurus/result">result</a> for this in Python, along with <a href="https://github.com/hynek/stamina">stamina</a> to retry things that fail intermittently. I prefer decorators over a bunch of <code>if (err)</code> style logic. <a href="#user-content-fnref-resulterror" data-footnote-backref="" aria-label="Back to reference 13" class="data-footnote-backref">↩︎</a></p>
</li>
</ol>
</section>]]></summary>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docker Bake + Github Actions + Private Repository + uv]]></title>
        <id>https://montasaurus.com/posts/uv-docker-bake-actions</id>
        <link href="https://montasaurus.com/posts/uv-docker-bake-actions"/>
        <updated>2025-04-15T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[<link rel="preload" as="image" href="/i/uv-docker-bake-actions/gh-pat.png"/><link rel="preload" as="image" href="/i/uv-docker-bake-actions/secret-set.png"/><h2>Symptom</h2>
<p>You try to install Python dependencies in Github Actions using a private Github repository as a source. You receive an error like:</p>
<pre><code>fatal: could not read Username for &#x27;https://github.com&#x27;: No such device
</code></pre>
<pre><code>remote: Repository not found.
fatal: repository &#x27;https://github.com/example/repo/&#x27; not found
</code></pre>
<h2>Issue</h2>
<p>The <code>GITHUB_TOKEN</code> secret that is available in Github Actions environments does NOT have access to <a href="https://github.com/orgs/community/discussions/46566">other private repositories</a>.</p>
<p>Here&#x27;s a sample <code>pyproject.toml</code> that can break, where <code>example/otherproj</code> is an example private repository:</p>
<pre><code class="language-toml">[project]
name = &quot;myproj&quot;
version = &quot;0.1.0&quot;
dependencies = [
    &quot;otherproj&quot;,
]

[tool.uv.sources]
otherproj = { git = &quot;https://github.com/example/otherproj&quot; } # my private repo
</code></pre>
<p>This will work fine locally, but fail when you <code>uv sync</code> it in Github Actions.</p>
<h2>Solution</h2>
<p>You need to create a Github PAT with permissions to access the private repository and pipe it through to the <code>uv sync</code> command.</p>
<p>Here&#x27;s an example of one way to do it:</p>
<ol>
<li>Create a Github PAT with <code>repo</code> permissions at <a href="https://github.com/settings/tokens">https://github.com/settings/tokens</a>.</li>
</ol>
<p><img src="/i/uv-docker-bake-actions/gh-pat.png" alt="gh-pat"/></p>
<ol start="2">
<li>Add that token as a &quot;repository secret&quot; in the repo that you&#x27;re running the build action in. This is under &quot;Settings&quot; &gt; &quot;Secrets and variables&quot; &gt; &quot;Actions&quot; &gt; &quot;New repository secret&quot;. I&#x27;ll use <code>UV_GH_TOKEN</code> as the name below.</li>
</ol>
<p><img src="/i/uv-docker-bake-actions/secret-set.png" alt="secret-set"/></p>
<ol start="3">
<li>Update your Github Action workflow to pass the token into the <code>docker/bake-action</code> step. I left the permissions section in the example below so that other steps can use the limited token.</li>
</ol>
<p><code>.github/workflows/docker.yaml</code>:</p>
<pre><code class="language-yaml">name: Build Docker Images

on:
  push:

jobs:
  build-and-push:
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read

    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3

      - name: Build and push
        uses: docker/bake-action@v6
        env:
          UV_GH_TOKEN: ${{ secrets.UV_GH_TOKEN }}
        with:
          files: docker-bake.json
          set: |
            *.platform=linux/amd64
            *.secrets+=id=github_token,env=UV_GH_TOKEN
</code></pre>
<ol start="4">
<li>Set the token in the Docker image&#x27;s <code>.netrc</code> file during <code>uv sync</code>, deleting it after to avoid leaking it in the image. <code>uv</code> will <a href="https://docs.astral.sh/uv/configuration/authentication/#http-authentication">respect this</a> during installation.</li>
</ol>
<pre><code class="language-dockerfile">RUN --mount=type=secret,id=github_token \
    echo &quot;machine github.com login x-access-token password $(cat /run/secrets/github_token)&quot; &gt; ~/.netrc \
    uv sync \
    &amp;&amp; rm ~/.netrc
</code></pre>
<h2>Better Option?</h2>
<p>Let me know if you find a better way to do this!</p>]]></summary>
    </entry>
</feed>
