Bridgetown2026-04-05T21:52:58-04:00https://www.danhorst.com/feedDan Brubaker Horst | WritingDan Brubaker HorstDash It All2025-10-18T13:26:01-04:002025-10-18T13:26:01-04:00repo://writing.collection/_writing/dash-it-all.md<p>I learned the difference between a dash and a semicolon—and when to use them—from my English teacher, <a href="https://www.linkedin.com/in/suzanne-ehst-816730a2">Suzanne Ehst</a>, back in 1998, my junior year of high school.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> I learned <a href="https://practicaltypography.com/hyphens-and-dashes.html">the difference between an en-dash and an em-dash</a> from <a href="https://matthewbutterick.com/">Matthew Butterick</a> in 2013.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p> <p>I use dashes in my writing to adjust the cadence so it feels more like spoken word or transcribed thought.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> I type them without thinking on my preferred computing platforms (macOS: <code class="highlighter-rouge">option</code> + <code class="highlighter-rouge">shift</code> + <code class="highlighter-rouge">-</code>; iOS: long-press <code class="highlighter-rouge">-</code> then select the longest dash). And, if I have to, I copy them from <a href="/lists/punctuation/">my list of useful punctuation</a>.</p> <p>Some Angry Internet People claim that <em>any</em> text written with em dashes <em>must</em> have been composed by an LLM because <em>no one</em> uses dashes in their writing. I disagree. I’ve used the right punctuation in my prose—dashes included—for decades. I’ll keep using them appropriately, even if I risk getting falsely accused of passing off GenAI slop as my own work.</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1"> <p>A clause appended to a sentence with a semicolon should be a complete sentence on its own. A sentence fragment must be appended to a sentence with an em-dash. Sue explained it like this: a semicolon is an RV pulling a car; a dash is an RV pulling a trailer. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:2"> <p>An en dash “–“ is used to in a range e.g. 10–20. An em dash “—“ is the right punctuation for appending a clause to a sentence or for a parenthetical statement. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> <li id="fn:3"> <p>I was once chided for using em dashes too often in scientific writing in 2004—the only editorial feedback I remember from college. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div>Dan Brubaker HorstBuilding Custom PCs2025-05-29T12:20:21-04:002025-05-29T12:20:21-04:00repo://writing.collection/_writing/building-custom-pcs.md<p>So, you want to build your first gaming PC? Great! This can be fun—especially if you understand how to make appropriate tradeoffs and avoid common pitfalls.</p> <p>There is an endless supply of information about the minutia of PC building and the benchmarks of the latest tech. It’s easy to get lost in the details if you can’t evaluate this information critically. The good news is that the fundamentals aren’t <em>that</em> complicated and have remained largely unchanged since <a href="https://en.wikipedia.org/wiki/3dfx">3dfx</a> created the market for PC gaming graphics cards in the late 90s.</p> <p>Computer performance is a <em>balance</em> between CPU, memory (RAM), GPU, and disk (SSD). Over-power—or under-power—one or two of these core components and you’ll be wasting money on unrealized potential. These essential parts all communicate with each other via different buses and connectors on the motherboard—which is designed for a specific type of CPU. Once assembled, everything must get enough power—from an appropriately-sized power supply—and be able to get rid of the heat that is generated under load; cool computers run better. Oh, and you need to put all of this into some kind of enclosure—like a case or, in a pinch, a cardboard box.</p> <p>The key design constraints are budget, availability, and the time you’re willing to spend on the endeavor.</p> <ul> <li><em>Budget</em>: The amount of money you’re willing and able to spend <em>now</em> determines the areas and degrees of investment you can make. Unless you have thousands of dollars to spend on a PC build, you will have to compromise on <em>something</em>. <ul> <li>Prioritize your spending on the things that are <em>most difficult to change later</em>. The CPU and motherboard are the <em>core</em> of the machine—buying a good set <em>first</em> gives you a foundation you can build on for 6 years—or, possibly, even longer.</li> <li>If you can’t afford all the RAM you want <em>now</em> consider spending a little extra to get a <em>single</em> large DIMM—say 16GB—and adding a second (or more) later. That way you won’t end up with a pair of small DIMMs taking up your available RAM slots.</li> <li>GPUs are the most expensive single component—and the fastest to get outdated. Don’t blow your budget on something that will diminish in value quickly.</li> </ul> </li> <li><em>Availability</em>: The crypto boom, COVID supply shocks, and the AI boom have turbocharged demand for GPUs. In this supply-constrained environment, scalpers have made high-end GPUs at MSRP essentially unobtainable on the open market. Someday—maybe—the GPU market will return to a more “normal” state for PC gamers but, until then, sourcing a GPU will remain a major constraint.</li> <li><em>Time</em>: If you can devote a lot of time to researching specific components and tinkering with the results—overclocking etc.—you can achieve the best possible result for your money. <ul> <li>Focus on the core—CPU and motherboard—first then, work your way out. <ul> <li>For the CPU, find the price/performance cutoff point of the current offerings and decide if it’s worth it to you to go above that point, if not, select a solid CPU that offers a good value. <ul> <li>Motherboards are a bit more complicated. The chipset features, memory configuration, voltage regulator design, and the number and speed of the peripheral busses of a motherboard play a crucial role in how capabable and expandable your machine will be during its functional life. Get a good quality one—but you probably don’t need an ultra-premium gaming-focused one to get the results you want. Don’t get one with integrated graphics for a gaming machine.</li> </ul> </li> </ul> </li> <li>If you don’t have a lot of time, just buy a pre-made machine. At the low end, OEMs have economies of scale and supplier arrangements that you don’t have access to as an individual. At the high end, say <a href="https://www.falcon-nw.com/">Falcon Northwest</a>, you’ll get a more powerful, polished result than can be achieved by all but the most dedicated PC builder.</li> </ul> </li> </ul> <p>Other things to avoid:</p> <ul> <li><em>Tempered glass case windows.</em> It’s common for inexperienced PC builders to push something a <em>little too hard</em> and end up with a bunch of broken glass everywhere. It’s not worth it.</li> <li><em>Pushing on</em> anything <em>too hard.</em> Apply <em>just enough</em> force to seat components with good electrical contact. More than that risks damaging or outright breaking something important.</li> <li><em>Using too much—or too cheap—thermal compound.</em> You need <em>just enough</em> thermal compound to ensure the physical and thermal coupling between components—like CPU and cooler. Any more thermal compound than <em>absolutely necessary</em> to fill the surface imperfections of the materials will <em>decrease</em> the thermal performance of the joint. Spending a small amount of money on a high-performance thermal compound is a sound investment. Prep the surfaces to remove any skin oils—isopropyl alcohol wipes are fine—and use a squeegee—like an old credit card—to ensure it is applied in a thin, consistent layer.</li> <li><em>Liquid cooling.</em> Don’t start with this; get a good heatsink and add more case fans if necessary.</li> <li><em>Tiny cases.</em> I’ve always been fond of small computers but it makes fitting everything together and getting the right airflow a lot harder. If you want a small, quiet computer buy a Mac mini or something.</li> <li><em>Thinking this will save you money.</em> PC building is less of a money pit than some hobbies—like racing cars or owning a boat or whatever—but you’re not going to get the cheapest possible computing experience.</li> </ul> <p>Go forth and be glorious!</p>Dan Brubaker HorstCalendars2024-11-07T17:54:03-05:002024-11-07T17:54:03-05:00repo://writing.collection/_writing/calendars.md<p>I am a recovering productivity nerd with a keen sense of my own mortality that spends time with computer systems professionally. Because of this, I’ve accumulated opinions about dates and calendars.</p> <ul> <li>Dates should be written in <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601 format</a> e.g. 2024-12-31. <ul> <li>The <code class="highlighter-rouge">year-month-day</code> order eliminates any ambiguity between <code class="highlighter-rouge">month/day/year</code> and <code class="highlighter-rouge">day/month/year</code>.</li> <li><code class="highlighter-rouge">year-month-day</code> strings are sorted the same both alphabetically and chronologically; n.b. beginning filenames with dates in this format is a great way organize time-bound information.</li> </ul> </li> <li>Weeks are the most useful unit of time.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> Months are too long to go without a recap. Days are too short to identify any trends. The <em>week number</em> helps anchor it in context; I track two: <ul> <li>The current year e.g. 2024 WK 45 <ul> <li>This can be displayed in calendaring software: <ul> <li>MacOS Calendar: Settings &gt; Advanced &gt; Show week numbers</li> <li>Google Calendar: Gear Menu &gt; Settings &gt; General &gt; Language and region &gt; View options &gt; Show week numbers</li> <li>Outlook (Mac): Outlook Settings &gt; Other: Calendar &gt; Calendar Options: Show week numbers</li> <li>Fantastical (iOS): Settings &gt; Calendar Views &gt; Calendar Weeks</li> </ul> </li> <li>You can configure the <a href="https://github.com/liamcain/obsidian-periodic-notes">Periodic Notes plugin</a> for <a href="https://obsidian.md/">Obsidian</a> to create weekly notes using this format by setting the Format for Weekly Notes to <code class="highlighter-rouge">YYYY/MM/YYYY [WK]ww</code>.</li> </ul> </li> <li>The week of my life e.g. 2167 (of <a href="https://www.oliverburkeman.com/fourthousandweeks">about 4000</a>). I was born on a Friday so I increment my “death clock” week number at the end of the workweek.</li> </ul> </li> <li>Weeks start on <em>Monday</em>. <ul> <li>This best reflects the rhythms of school, 9–5 office work, and similar job schedules.</li> <li>It keeps the days of the weekend together on the right hand side of a 7-day calendar view making it easier to plan your weekend as a contiguous chunk of time. This reduces context switching when toggling between 5-day (work) and 7-day (personal) week views.</li> <li>There is wide support for starting the week on Monday in modern computer and phone operating systems. <ul> <li>MacOS: System Settings &gt; Language &amp; Region &gt; First day of the week</li> <li>iOS: Settings &gt; General &gt; Language &amp; Region &gt; First day of the week</li> <li>Google Calendar: Gear Menu &gt; Settings &gt; General &gt; Language and region &gt; View options &gt; Start week on</li> <li>Outlook (Mac): Outlook Settings &gt; Other: Calendar &gt; Work Schedule: First day of week</li> <li>Fantastical (iOS): Settings &gt; Calendar Views &gt; Start Week On</li> </ul> </li> </ul> </li> <li>Months are not long enough for long-term planning. If you need to execute a strategy or manage a long-running project, use a <a href="https://davidseah.com/node/compact-calendar/">one-page year-long compact calendar</a>. <ul> <li>There is an Excel template that does the heavy lifting. I customize mine to include both types my week numbers and work-specific times e.g. quarters or <a href="https://v5.scaledagileframework.com/program-increment/">program increments</a>.</li> </ul> </li> <li>Calendaring, like note-taking, benefits from being <em>physically tangible</em>. You don’t have to <a href="https://uxdesign.cc/drawing-the-calendar-2bfc9612bb04">draw your calendars from scratch</a> (although that can be satisfying); printing out a <a href="https://davidseah.com/node/compact-calendar/">compact calendar</a> and annotating it by hand works well. <ul> <li>Even though it doesn’t employ my preferred calendaring conventions, I have a soft spot for the <a href="https://fieldnotesbrand.com/products/workstation-wall-calendar">Field Notes 15-Month Work Station Calendar</a>.</li> <li>If your <em>days</em> are extremely busy, then I’ve had good results using the <a href="https://davidseah.com/node/the-emergent-task-planner/">Emergent Task Planner</a> (sadly out of print; you can print your own).</li> </ul> </li> </ul> <p>I’ll leave thoughts on <em>time</em> and <em>task management</em> for another day.</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1"> <p>For time management anyway; the most useful unit of time for <em>doing</em> is <a href="https://en.wikipedia.org/wiki/Pomodoro_Technique">20 minutes</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div>Dan Brubaker HorstPhotography2024-08-23T17:31:10-04:002024-08-23T17:31:10-04:00repo://writing.collection/_writing/photography.md<p>I bought my first digital camera in July of 2005. I have taken over 39,000 photos with it—and a series of increasingly sophisticated devices<label for="my-devices" class="margin-toggle sidenote-number"></label> <input type="checkbox" id="my-devices" class="margin-toggle" /> <span class="sidenote"> Canon PowerShot A95, Canon Digital Rebel XTi, iPhone 4s, Canon SL1, Nexus 6P, Pixel 3, iPhone 13 Mini, Fuji XT-5 </span>—over the ensuing nineteen years. The act of taking photos is woven into how I experience holidays, family gatherings, vacations, and—at times—my daily life. It’s something that I enjoy doing.</p> <p>The appeal of photography is multifaceted:</p> <ul> <li>It’s an accessible form of artistic expression.</li> <li>Photographs are windows into the past; they reflect the life we’ve lived.</li> <li>It is a technical puzzle: How do you get the results you want by understanding and manipulating your equipment and the environment?</li> <li>There is <a href="https://www.youtube.com/watch?v=R3SFqV0hMyo"><em>gear</em></a> to be researched, evaluated, purchased, and lusted after. <ul> <li>Cameras &amp; camera bodies of every degree of quality and sophistication.</li> <li>Lenses in every shape, size, and manufacturer—don’t forget vintage glass!</li> <li>All sorts of bags, straps, holsters, and clips to keep everything organized and at the ready.</li> <li>Filters—glass filters—that abate or modify the light entering the lens in all kinds of ways or stay out of the way and act as protection.</li> </ul> </li> <li>There are a host of workflow and digital asset management concerns. <ul> <li>Let’s talk about RAW files… (hours pass).</li> <li>How to I merge all the photos I take on my phone with the ones I take with my “real” camera?</li> <li>What replication strategy across cloud providers makes sense for my needs?</li> <li>How much digital refinement or modification to the images do you want to be able to do?</li> <li>How can you adjust your camera’s settings to get the best results without having to do as much (or any) post-processing?</li> </ul> </li> </ul> <p>Last week, after much indecision, I purchased a full version of <a href="https://www.captureone.com/en/products/capture-one-pro">Capture One Pro</a>. Now I have a desktop application where I can manage my entire collection of photos for the first time since Google stopped development on <a href="https://picasa.google.com/">Picasa</a> in 2016. I’ve been reviewing the photos I’ve taken—all 39,000—to see which ones I like enough to share on <a href="https://glass.photo">Glass</a> (<a href="https://glass.photo/dbh">@dbh</a>) and, in the processes, I’ve come to the conclusion that I’m not a particularly good photographer. I’m an unremarkable amateur that hasn’t meaningfully improved in over a decade.</p> <figure class="picture"> <figcaption class="picture__caption marginnote"> Why did I take this photo?<br /> Does it make you feel anything? </figcaption> <div class="lightbox"> <a class="picture__anchor r2x3" href="/photo/unremarkable-photo-2048.jpg"> <div class="placeholder"></div> <img loading="lazy" class="picture__content" src="/photo/unremarkable-photo-1024.jpg" alt="Photograph with muted colors of the top of an office building with a limestone façade and art deco ornamentation stained dark by acid rain in the foreground and a larger, darker, more modern office building extending skyward in the background." /> </a> </div> </figure> <p>This isn’t surprising. My once-fervent enthusiasm has waned over the last decade as the pressures of adult life and disillusionment with my results encroached upon my creative drive. I don’t look at the photo books or instructional texts I’ve purchased or asked for as gifts. I haven’t taken the online photography courses I’ve purchased. I’ve stopped listening to photography podcasts. I don’t watch any photography content on YouTube other than gear reviews.</p> <p>I can get better through directed effort or languish as I am now. Prompted by this newly-acknowledged reality, I finally cracked open my copy of <a href="https://rockynook.com/shop/photography/the-art-of-photography-2nd-edition/">The Art of Photography</a>.<label for="taking-my-time" class="margin-toggle sidenote-number"></label> <input type="checkbox" id="taking-my-time" class="margin-toggle" /> <span class="sidenote"> The second edition was released in 2017; my unread copy predates that. </span> These points from the <em>first chapter</em> elucidate why I am struggling:</p> <ul> <li>Photography is visual communication.</li> <li>Photographs are manifestations of the photographer’s interests and how they feel about them; they convey <em>emotion</em>.</li> <li>Great photographers know their strengths and weaknesses; they leverage their strengths to explore their interests.</li> </ul> <p>Over 99% of the photos I have taken say:</p> <blockquote> <p>I saw a thing, I positioned the thing in the center of the frame (or, sometimes, on one third of the frame), I checked focus and exposure compensation, I triggered the shutter.</p> </blockquote> <p>There is no emotional weight to the scene. Lines, shape, and form are limited to rudimentary expression. The <em>direction</em> or <em>intensity</em> of light is barely considered beyond exposure compensation and (rarely) metering mode. Visually striking results occur rarely and randomly because I’m not <em>deliberately making</em> images—I’m taking <a href="https://en.wikipedia.org/wiki/Snapshot_(photography)">snapshots</a>. I’d like to be able to do better than that.</p>Dan Brubaker HorstTechnical Debt2024-07-31T13:18:44-04:002024-07-31T13:18:44-04:00repo://writing.collection/_writing/tech-debt.md<p><a href="https://en.wikipedia.org/wiki/Technical_debt">Technical debt</a> is both pervasive and disruptive to the discipline of software engineering. No software<label for="not-all-software" class="margin-toggle sidenote-number"></label> <input type="checkbox" id="not-all-software" class="margin-toggle" /> <span class="sidenote"> Aside from ephemeral software that is discarded after use. </span> is free from technical debt. Despite this, it’s hard for organizations to make productive, impactful changes to the processes and practices that cause technical debt to arise.</p> <p>It is tempting—and common—to draw parallels between technical debt and <em>financial</em> debt. You can try to apply lessons from <a href="https://www.ramseysolutions.com/ramseyplus/financial-peace">Financial Peace University</a> to your code base and technical systems but I think it is more useful to think about of technical debt in terms of its impact on marginal cost.</p> <p>One of core responsibilities of software engineers is to control the marginal cost<label for="marginal-cost" class="margin-toggle sidenote-number"></label> <input type="checkbox" id="marginal-cost" class="margin-toggle" /> <span class="sidenote"> Marginal cost, by definition, is the change in the total cost when the quantity produced is increased. The marginal costs of software increase in a nonlinear fashion as the complexity of the system increases i.e. it is <em>much</em> easier to change a simpler thing than a complicated thing. The biggest cost centers are engineering time, cloud computing resources, and licensing fees. </span> of:</p> <ul> <li>Extending a system with new functionality</li> <li>Scaling a system for broader use</li> </ul> <p>This helps keep the cost of change low over time.<label for="cost-of-change" class="margin-toggle sidenote-number"></label> <input type="checkbox" id="cost-of-change" class="margin-toggle" /> <span class="sidenote"> Keeping marginal costs low was one of the claimed benefits of <a href="https://en.wikipedia.org/wiki/Extreme_programming">Extreme Programming (XP)</a>; a set of practices that predated Agile. There are good ideas in XP but the methodology has fallen out of favor over time. Agile software development <em>should</em> have similar outcomes but often doesn’t. </span> Marginal costs are influenced by:</p> <ul> <li>Architecture decisions</li> <li>Process design</li> <li>Implementation choices</li> </ul> <p>Thus “paying down” technical debt involves <em>remediating</em> one of these aspects of the <a href="https://en.wikipedia.org/wiki/Systems_development_life_cycle">software development life cycle (SDLC)</a>. This isn’t a simple problem space but it is prevalent enough that patterns have emerged. That’s why <a href="https://martinfowler.com/articles/bottlenecks-of-scaleups/01-tech-debt.html">accumulation of technical debt</a> is one of the <a href="https://martinfowler.com/articles/bottlenecks-of-scaleups/">Bottlenecks of Scaleups</a>.</p> <p>The only effective way to manage these marginal costs is to periodically and rigorously <em>simplify</em> standing systems through methods like introducing new layers of abstraction, breaking services into smaller component pieces, and depreciating anything you can get away with.</p>Dan Brubaker HorstFirst Ruck2024-06-16T10:13:47-04:002024-06-16T10:13:47-04:00repo://writing.collection/_writing/first-ruck.md<p>I went for my first ruck yesterday. <a href="https://www.goruck.com/pages/what-is-rucking">Rucking</a> is not a new idea for me—I have just been avoiding actually doing it for over a decade. This year, I walked around the backyard once or twice with a weighted pack for a little bit but that was it. Yesterday, did 1.5 miles around the neighborhood in 26 minutes with a 10-pound weight in my pack. Today, I can feel it in my upper back and a little bit in my left ankle.</p> <p>My partner got me a <a href="https://www.goruck.com/products/gr0">GoRuck GR0</a>, which is a 16L minimalist assault-style pack, for Christmas in 2013 but I’ve just been using it as a normal backpack. A few months ago I listened to <a href="https://peterattiamd.com/jasonmccarthy/">a podcast with Jason McCarthy, the founder of GoRuck</a>, and I learned a lot about how and why to ruck from that conversation. His recommendation was to carry the pack high up on your shoulders—he doesn’t use the waist belt or the sternum strap. Dr. Peter Attia uses both of those straps but also carries up to a 90 pound load. Since I’m starting with a light load (less than 10% body weight), I removed my waist belt but kept the sternum strap.</p> <p>To pack my weight, I put two synthetic foam yoga blocks at the bottom of my bag then wrapped a beach towel around a <a href="https://www.roguefitness.com/rogue-lb-change-plates">10-pound Rogue change plate</a> so that there was one layer of towel between the plate and the back of the pack. I cinched a <a href="https://gunner.com/products/tie-down-strap-kit">Gunner tie-down strap</a>—<a href="https://www.nrs.com/nrs-15-heavy-duty-straps/pywr">one from NRS would be fine too</a>—around the towel bundle to hold everything in place. This kept the weight high and close to my shoulders. It also filled the pack so nothing bounced around.</p> <p>It was hot and sunny when I went out. There’s a lot of shade in my neighborhood but there are stretches of road that get a good amount of sunlight. My subdivision was designed in the 50s and the roads look like a doughnut where if you follow the outer loop, then the inner loop, it’s exactly one mile long. This layout was deliberate—they used to race horses around the “track” formed by the roads in the early years. It’s really convenient for measuring exercise. There’s also some hills—which aren’t too common around here—which helps keep it interesting.</p>Dan Brubaker Horst402023-04-29T22:28:58-04:002023-04-29T22:28:58-04:00repo://writing.collection/_writing/2023-04-29-40.md<p>I was born on Friday, April 29th, 1983. It was unseasonably warm that year. Spring came and went quickly; the trees were no longer blooming when my mother left the hospital.</p> <p>Now, 2080 weeks later, it doesn’t feel like I’ve passed a life milestone today. But, within the next decade, I expect to reach the inflection point where I have less time remaining than I have already spent on the earth.</p> <p>I want to be healthy and happy enough to make the best of the second half of my life. To do that, I need to invest in my overall fitness now. A frank self-assessment of my physical health reveals:</p> <ul> <li>I am not metabolically healthy.</li> <li>I don’t have good enough stability.</li> <li>My musculature is both underdeveloped and too weak.</li> <li>I have poor cardiovascular endurance.</li> <li>I have abysmal peak aerobic output.</li> </ul> <p>Thankfully, I can change all of these things. It will take directed effort and setting aside far more time every week to care for my body than I have over the last twenty years.</p> <p>I have also underinvested in my mental health in my adult life. To rectify the situation, I’m working with my care team to relieve anxiety, rebuff depression, and improve emotional regulation. Taking care of your mind is hard work too.</p> <p>Overall, I’m optimistic about the future. One thing concerns me: my 30s feel like they were gone in a flash. The same thing will happen to my 40s if I’m not careful. I can’t slow the passage of time, but I can try to be more mindful of the present.</p>Dan Brubaker HorstTen Years of DevOps Obstacles2022-02-24T13:44:20-05:002022-02-24T13:44:20-05:00repo://writing.collection/_writing/ten-years-of-devops-obstacles.md<p>I started <a href="/writing/infrastructure-as-code/">writing about DevOps</a> in 2012. The conversations I’m having in the OIT now sound suspiciously similar to ones I had in the Hesburgh Libraries ten years ago. I am not pleased with this sense of déjà vu.</p> <p>Today I attended a higher-ed meetup about “DevOps.” The organizers asked a panel to define what that term means and how it worked–or didn’t–at their institution. In response, I said:</p> <blockquote> <p>DevOps–and “splat”-Ops in general like DevSecOps, GitOps, ChatOps, etc.–attempts to change organizational culture, structure, and practices to improve software delivery performance. <a href="/writing/software-delivery/">Software delivery performance</a> has industry-defined measures, indicators, and associated practices. It asserts that making fast, frequent changes to your services and infrastructure without compromising their integrity is a foundational capability for successful organizations.</p> </blockquote> <p>I also said that we’re having a hard time doing these things. There are lots of pitfalls when you’re starting:</p> <ul> <li>DevOps enthusiasts want to talk about tools. <ul> <li>Many DevOps tools adopt a “move fast and break things” mentality; caveat emptor.</li> <li>Don’t start a conversation about <a href="https://kubernetes.io/">kuberneties</a><sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>.</li> </ul> </li> <li>It is hard to change the behavior of large, established organizations. <ul> <li>Organization change management–even when done successfully–takes <em>years</em>.</li> <li>Be mindful of <a href="https://en.wikipedia.org/wiki/Conway%27s_law">Conway’s Law</a>. Reimagining your work requires changing your organizational structure. There are <a href="https://teamtopologies.com/">existing</a> <a href="https://web.devopstopologies.com/">resources</a> to help with this.</li> </ul> </li> <li>Leadership needs to believe in the benefits of these organizational capabilities <ul> <li>Lack of buy-in from upper management limits the potential scope &amp; impact of changes.</li> <li>Increasing the pace of change is threatening instead of exciting to organizations that have adopted strict ITIL or another lumbering change-management system.</li> <li>InfoSec and your auditors need to be allies or you won’t get far</li> </ul> </li> </ul> <p>Many of the tools and practices in this space have been around for years. Unless you have novel use cases or operate on a grand scale, you’ll only run into modest technical hurdles. The hard part of changing an organization is re-skilling, reorienting, and realigning the people.</p> <p>Organizations do not change overnight. It takes a lot of hard work from knowledgeable, well-intentioned people to make a meaningful difference. I take some solace from the fact that our aspirational peers are quick to acknowledge they’re working on issues too.</p> <div class="footnotes" role="doc-endnotes"> <ol> <li id="fn:1"> <p>I’m being a bit facetious here. There’s a <a href="https://www.cncf.io/">vast and powerful array of tooling</a> that takes advantage of the APIs provided by K8s. Even so, avoid using it directly unless you are building <em>platforms</em> instead of <em>solutions</em>. Avoid building platforms unless they provide key business capabilities you can’t get another way–otherwise, have an IaaS partner run them for you. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p> </li> </ol> </div>Dan Brubaker HorstSoftware Delivery2020-10-15T14:45:28-04:002020-10-15T14:45:28-04:00repo://writing.collection/_writing/software-delivery.md<blockquote> <p>This was extracted from an internal document I wrote for work but I reference it so often I wanted to give it a more permanent home.</p> </blockquote> <p>Efficient, reliable, and reproducible software delivery keeps the cost of introducing change low over the lifetime of a service offering. With this as a foundation, organizations can iterate toward adopting and leveraging enterprise integrations that reduce maintenance burden and decrease time to delivery for new work.</p> <h2 id="key-measures">Key Measures</h2> <p>From <a href="https://itrevolution.com/book/accelerate/">Accelerate</a>, Appendix B and <a href="https://itrevolution.com/book/accelerate/">The Key to High Performance (free PDF e-book; email sign-up required) p3</a> (<a href="https://www.youtube.com/watch?v=RBuPlMTXuFc">YouTube</a>). The more painful code deployments are, the poorer the software delivery performance and <a href="/lists/organizational-culture/">culture</a>.</p> <ul> <li><em>Deploy frequency:</em> How often are changes made to production? Favor small, frequent changes. <ul> <li><a href="#continuous-delivery">Continuous Delivery</a></li> <li>Version control</li> </ul> </li> <li><em>Lead time:</em> How long does it take to go from commit to running in production? <ul> <li>Version control</li> <li>Test automation</li> </ul> </li> <li><em>Mean time to restore (MTTR):</em> How long does it take to restore a failed service? <ul> <li>Version control</li> <li>Monitoring</li> </ul> </li> <li><em>Change fail percentage:</em> How often do changes cause an outage or another negative outcome? <ul> <li>Strongly correlated with overall software delivery performance</li> </ul> </li> </ul> <h2 id="key-indicators">Key Indicators</h2> <p>From <a href="https://itrevolution.com/book/accelerate/">Accelerate</a>, Technical Practices and <a href="https://itrevolution.com/book/accelerate/">The Key to High Performance (free PDF e-book; email sign-up required) p14</a> (<a href="https://www.youtube.com/watch?v=RBuPlMTXuFc">YouTube</a>).</p> <h3 id="continuous-delivery">Continuous Delivery</h3> <ul> <li><em>Test automation:</em> automated tests give quick feedback and create a virtuous cycle of quality</li> <li><em>Deployment automation:</em> <a href="#characteristics-of-cloud-computing">see Characteristics of Cloud Computing below</a></li> <li><em>Trunk-based development:</em> keep master deployable, favor short-lived feature branches</li> <li><em>Shift left on security:</em> include InfoSec early and often in the SDLC</li> <li><em>Loosely coupled architecture:</em> <a href="#key-outcomes-for-system-architecture">see Key Outcomes for System Architecture below</a></li> <li><em>Empowered teams:</em> teams choose the tools they use, favor performance over standards</li> <li><em>Continuous integration:</em> run tests on every commit, merge, and deploy</li> </ul> <h3 id="other-indicators">Other Indicators</h3> <ul> <li>Version control: track everything—not just code, configuration is just as important</li> <li>Test data management: automated test suites need access to appropriate data</li> <li>Monitoring: measure the <a href="https://landing.google.com/sre/sre-book/chapters/monitoring-distributed-systems/#xref_monitoring_golden-signals">four golden signals</a>: latency, traffic, errors, saturation</li> </ul> <h2 id="characteristics-of-cloud-computing">Characteristics of Cloud Computing</h2> <p>From <a href="https://doi.org/10.6028/NIST.SP.800-145">NIST 800-145</a>. Implementing systems that display these characteristics is strongly correlated with high-performance organizations.</p> <ol> <li><em>On-demand self-service.</em> Allow automatic provisioning of computing resources, storage, etc. without requiring human interaction with the service provider.</li> <li><em>Broad network access.</em> Services are available over the network and accessed through standard mechanisms that are device agnostic.</li> <li><em>Resource pooling.</em> Storage, processing, memory, network bandwidth, and other resources are pooled, shared, and dynamically assigned using a multi-tenant model.</li> <li><em>Rapid elasticity.</em> Capabilities can be elastically provisioned and released, in some cases automatically, to scale rapidly outward and inward commensurate with demand.</li> <li><em>Measured service.</em> Resource usage can be monitored, controlled, and reported, providing transparency for both the provider and consumer of the utilized service.</li> </ol> <h2 id="key-outcomes-for-system-architecture">Key Outcomes for System Architecture</h2> <p>Decoupled systems are crucial for lowering the cost of change. This evaluation is from <a href="https://itrevolution.com/book/accelerate/">The Key to High Performance (free PDF e-book; email sign-up required) p3</a> (<a href="https://www.youtube.com/watch?v=RBuPlMTXuFc">YouTube</a>).</p> <ul> <li>Can your team make large-scale changes to the design of your systems without the permission of someone outside of your team or without depending on other teams?</li> <li>Can your team complete its work with neither fine-grained communication nor fine-grained coordination?</li> <li>Can your team deploy and release your product on-demand independently of other services?</li> <li>Can your team do most of your testing on-demand without requiring an integrated test environment?</li> <li>Can your team perform deployments during normal business hours and with negligible downtime?</li> </ul>Dan Brubaker HorstWhat Happens When You Type a URL Into a Browser2020-09-11T11:13:47-04:002020-09-11T11:13:47-04:00repo://writing.collection/_writing/what-happens-when-you-type-a-url-into-a-browser.md<p>UPDATE 2021-08-30: Jenna Pederson over on the <a href="https://aws.amazon.com/blogs/mobile/what-happens-when-you-type-a-url-into-your-browser/">AWS blog wrote a much better response to the question I try to answer here.</a></p> <hr /> <p>I started listening to the <a href="https://www.ladybug.dev/episodes/how-the-internet-works">“How the Internet Works” episode</a> of the Ladybug <a href="/lists/podcasts/">podcast</a> today. They set out to answer the technical interview question:</p> <blockquote> <p>What happens when you type a URL into a browser?</p> </blockquote> <p>Before I listened to their explanation I pressed pause. This is my explanation of the process without doing any research–just like I wouldn’t be able to do in an interview setting. (Yes, I did type the example commands into my terminal to get the output.)</p> <p>EDIT: Never mind. I spent way more time on this and went into greater detail than I would if I were speaking off the cuff (I still didn’t do any fact checking or fill in any gaps in my working knowledge so take all of this it with a grain of salt). This is one of the pitfalls of technical interviews. I don’t have a lot of professional experience standing in front of a white board and explaining things extemporaneously and if you asked me to do just that it wouldn’t have gone very well but that doesn’t mean I don’t understand what’s going on. Instead, I write documents that thoroughly explain the required information with an eye towards a less technical audience.</p> <hr /> <p>Typing a URL into a browser is something we do all the time and, although it is appears to be simple, a lot of things happen before we get to the web page we’re looking for.</p> <p>Browsers perform a lot of actions in the address/combo bar itself. As you are typing it tries to guess what you want to do by:</p> <ul> <li>Matching patterns of URLs or words you enter with pages from your browsing history</li> <li>Querying your default search provider and return the top results</li> <li>Querying DNS for domain names that match what you have typed so far</li> </ul> <p>The combination and execution of these behaviors vary from browser to browser but they all do something like this. The results of these queries show up as options for you to choose from. Let’s say you don’t select any of these suggestions and press enter when you finish typing. Next, the browser will try to make a request to a web server based on what you typed. To do that, it goes through another two processes:</p> <ol> <li>URL resolution</li> <li>Content negotiation<label for="content-negotiation" class="margin-toggle sidenote-number"></label><input type="checkbox" id="content-negotiation" class="margin-toggle" /><span class="sidenote">I'm using the term "content negotiation" loosely here. The narrowest technical definition of content negotiation is the process where the client and the server use accepts headers and content types&mdash;HTML, txt, JSON, XML, application/octet stream (binary), etc.&mdash;to determine the encoding and/or the content of the response body. I am using it to mean "the client and the server figure out what they need from each other" before the response body is processed by the client.</span></li> </ol> <p>URLs have a structure:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&lt;protocol&gt;://&lt;OPTIONAL subdomain&gt;.&lt;apex domain&gt;.&lt;toplevel domain (TLD)&gt;:&lt;OPTIONAL port&gt;/&lt;OPTIONAL path&gt;?&lt;OPTIONAL query&gt;=&lt;OPTIONAL paramter&gt; </code></pre></div></div> <p>The steps a browser takes depends on the parts of the URL you entered.</p> <p>For example:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>danhorst.com </code></pre></div></div> <p>Has a few more steps than:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://www.danhorst.com </code></pre></div></div> <p>URL resolution starts by querying DNS to find an IP address that maps to the address that was entered. DNS (domain name service) is a globally distributed network of servers that store what are essentially big lookup tables for all the domains that are registered on the Internet. Internet infrastructure companies run DNS servers as a part of their operations. For example, CloudFlare runs 1.1.1.1 and Google runs 8.8.8.8. When you create a domain using a registrar like, Hover.com or NameCheap, it registers it with the global lists on servers blessed by ICANN (ICANN governs the Internet and I think the acronym is French so I don’t know what it stands for exactly). The records get picked up by other DNS providers from there. That is why it takes a while for DNS changes to propagate.</p> <p>The DNS provider your browser uses depends on the network settings of your device. Those settings can are configured per network adapter so they vary based on which one you are using to access the Internet. For most people, the DNS service is provided by your Internet Service Provider (ISP). It can also be manually set to specific provider. Companies may run internal DNS if they want to control how name resolution works on their internal network. For example, if you have a VPN for work it may allow you to resolve DNS records that don’t exist on the public Internet.</p> <p>There are different kinds of DNS records: A, CNAME, TXT, MX, etc. For name resolution, we’re primary looking at A (address) records. CNAME records can be used to specify additional addresses that resolve to a server (I’m not familiar enough with how this works to be able to explain how this works in detail). MX records have to do with mail routing. TXT records are used for other metadata. For example, Google Webmaster Tools asks you to add a unique TXT record to your domain to prove that you control it.</p> <p>You can query DNS outside of the browser too. I do this when I need to troubleshoot a connectivity issue.</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ $ dig danhorst.com +short 104.198.14.52 </code></pre></div></div> <p>(I use the <code class="highlighter-rouge">+short</code> flag to limit the output almost all the time. The default output for <code class="highlighter-rouge">dig</code> has way more information than I need for checking name resolution.)</p> <p>Once the browser has an IP address that maps to the domain it initiates a TCP/IP connection to that IP address. IP stands for “Internet Protocol” and we typically use IPv4 address which take the form of four period-delimited blocks of 1-3 integers e.g. 129.0.0.1, 8.8.8.8<label for="cert-chain" class="margin-toggle sidenote-number"></label><input type="checkbox" id="cert-chain" class="margin-toggle" /><span class="sidenote">IPv6 is also a thing but those addresses are so long I never remember what they look like exactly. IPv6 doesn’t have a lot of support in the Internet at large.</span>. If no protocol is specified in the URL, the browser assumes that you meant HTTP (Hypertext Transfer Protocol) and attempts to connect to port 80 on the IP address of the server.</p> <p>You can use <code class="highlighter-rouge">telnet</code> to check to see if it is possible to connect to a specific port for a given IP address:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ $ telnet 104.198.14.52 80 Trying 104.198.14.52... Connected to 104.198.14.52. Escape character is '^]'. </code></pre></div></div> <p>Or you can use <code class="highlighter-rouge">curl</code> to check how the server responds:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl -I danhorst.com ~ $ curl -I danhorst.com HTTP/1.1 301 Moved Permanently Cache-Control: public, max-age=0, must-revalidate Content-Length: 37 Content-Type: text/plain Date: Fri, 11 Sep 2020 07:13:49 GMT Location: https://danhorst.com/ Age: 32302 Connection: keep-alive Server: Netlify X-NF-Request-ID: bd036650-87b5-4e74-8033-06731acad39a-25552555 </code></pre></div></div> <p>The <code class="highlighter-rouge">-I</code> parameter tells <code class="highlighter-rouge">curl</code> to make a HEAD request. HTTP defines <em>verbs</em> that clients can use to make requests from servers. The common ones are: GET, POST, and HEAD (PUT, PATCH, and DELETE are valid too but they don’t have universal support). GET requests retrieve content from a web server. POST requests send content to a web server. HEAD requests get only the HTTP <em>headers</em> for a given URL.</p> <p>A web server’s response to a client request has three parts: status code, headers, and body. A HTTP status code is a 3-digit number that describe the response at a high level. HTTP headers are colon-delimited key-value pairs that contain metadata about the response that the client uses to process the response and, in some cases, make additional requests. The response body is the data that is processed and/or displayed by the client.</p> <p>See <code class="highlighter-rouge">HTTP/1.1 301 Moved Permanently</code>? That’s an HTTP status code. We’ve moved from DNS resolution, which happens at the network layer to content negotiation that happens between the web server and the client. <code class="highlighter-rouge">301</code> is an HTTP status code that has specifically means “moved permanently”. The 3XX status codes are <em>redirects</em> which means that they tell the client “you need to get the content you are looking for somewhere else”. Thankfully it tells you where to look next via the <code class="highlighter-rouge">Location</code> header: <code class="highlighter-rouge">https://danhorst.com/</code>.</p> <p>This redirect also changed the protocol from HTTP to HTTPS (the S stands for “secure”, or it may have originally stood for SSL, Secure Socket Layer, but that means of encryption was deprecated in favor of TLS, Transport Layer Security, years ago.) HTTPS uses a pair of public/private encryption keys to ensure that the messages sent between the client and the server are kept private and unaltered in transit. HTTPS communication is sent over port 443 instead of port 80.</p> <p>There are two key parts of securing web traffic: a means of validating that the server can be trusted to respond to the requested URL and a means of encrypting and decrypting the messages being sent. Only a few organizations, called Certificate Authorities, can establish that a server is trusted. They issue what is called a “root certificate” that forms the base of the chain of trust between organizations to certify that a server is legit. This takes the form of a “certificate chain” that starts with the root certificate, may include one or more intermediate certificates, and ends with the certificate for the domain associated with a web server. The important things about a certificate chain are that it:</p> <ul> <li>Traces back to a known-good root CA, possibly through one or more intermediate certificates from trusted sources</li> <li>Is valid for a fixed window of time (typically one year from when it was issued)</li> <li>Is valid for a specific domain be it wildcard e.g. <code class="highlighter-rouge">*.danhorst.com</code> or specific e.g. <code class="highlighter-rouge">www.danhorst.com</code></li> </ul> <p>To establish a chain of trust from a web server that will serve traffic for a specific domain you start by generating a Certificate Signing Request (CSR)<label for="cert-management" class="margin-toggle sidenote-number"></label><input type="checkbox" id="cert-management" class="margin-toggle" /><span class="sidenote">Certificate management can be a complicated manual process. That’s why a lot of people prefer setting up something like Let’s Encrypt to auto-renew certificates or put web servers behind a load balancer that integrates with Amazon Certificate Manager (or it’s equivalent outside of AWS) to avoid this whole mess. The only reason I can describe it in detail now is that I had to this two weeks ago.</span>. Generating CSRs, and almost everything else you might want to do with TLS, it handled with specific incantations of <code class="highlighter-rouge">openssl</code>. I do this so infrequently I don’t remember the details and have written scripts to take the guesswork out of it. You submit the CSR a certificate authority and they give you back a <code class="highlighter-rouge">&lt;certificate-name&gt;.key</code> file that contains the private key to store securely on the server and a certificate chain to share with the world. There are lots of ways to encode the certificate chain but I like to use plain text (X509) so the certificate chain is just a text file with a list of public keys in it (I use a <code class="highlighter-rouge">&lt;certificate-name&gt;.pem</code> file to do this). Some web servers expect the root CA to be first and the domain certificate to be last and others are the reverse (don’t ask me why). You may have to experiment to figure how to go from what the certificate authority gave you to what you need for the server to work.</p> <p>The list of trusted root certificate authorities is managed by the operating system or language runtime of the client<label for="java-certs" class="margin-toggle sidenote-number"></label><input type="checkbox" id="java-certs" class="margin-toggle" /><span class="sidenote">There have been times where I needed to add certificates to a certificate store and explicitly point a Java process to that certificate to avoid errors (this isn’t a great time).</span>. This only becomes a problems when you have a self-signed certificated (not blessed by a root CA) or if you use a more obscure root CA (why would you do this?).</p> <p>Once a chain of trust is established using the certificate chain<label for="cert-chain" class="margin-toggle sidenote-number"></label><input type="checkbox" id="cert-chain" class="margin-toggle" /><span class="sidenote">It’s important to include the <em>entire</em> certificate chain, from the root CA up to the specific cert, or else some clients won’t trust it. There are also ways a certificate can be cross-signed to other authorities, but the specifics are lost on me.</span>, the browser uses the public key provided by the certificate chain to encrypt the communication with the server. It does this using one of many methods. There is both the protocol–SSL (deprecated), TLS 1.0 (deprecated), TLS 1.1 (deprecated), TLS 1.2, TLS 1.3 (emerging)–as well as the “cypher suite”–the specific encryption algorithms–used to perform the encryption. The client and the server compare lists of available protocols and cypher suites for those protocols and try to find one they both have in common. It is up to the server administrator to disable weak or defunct protocols and cypher suites on the web server in order to ensure that the connection is secure<label for="cypher-punk" class="margin-toggle sidenote-number"></label><input type="checkbox" id="cypher-punk" class="margin-toggle" /><span class="sidenote">Most of the Internet that we use day to day is served over HTTPS. Overall, this is a good thing for privacy and security, but it comes with a price: older clients and machines get left behind. A few weeks ago I started up a netbook for the first time in years and it couldn’t make valid HTTPS connections to anything so it was unable to even update it’s packages.</span>. If both the browser and the web server can agree on a supported means of encryption then the request/response cycle can continue.</p> <p>Let’s try accessing the “Location” header value it returns:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ $ curl -I https://danhorst.com/ HTTP/2 301 cache-control: public, max-age=0, must-revalidate content-length: 41 content-type: text/plain date: Fri, 11 Sep 2020 07:13:50 GMT location: https://www.danhorst.com/ strict-transport-security: max-age=31536000 age: 40924 server: Netlify x-nf-request-id: 160d69b1-c6eb-44e5-af2b-64c14a83d621-3083499 </code></pre></div></div> <p>Another 301! I’ve configured my website to <em>never</em> serve content from the apex domain <code class="highlighter-rouge">danhorst.com</code>. The way apex domains are resolved means that there are fewer options for managing how name resolution works for large, complicated, websites. At least, that’s the way it <em>used</em> to be. I’m short on the specifics, but there was an RFC<label for="rfc" class="margin-toggle sidenote-number"></label><input type="checkbox" id="rfc" class="margin-toggle" /><span class="sidenote">The nuts and bolts of how <em>exactly</em> the Internet is described in specs that are referred to as RFCs (I <i>think</i> that stands for Request for Change but that isn’t a great name for a standard that governs something this important.) I have looked at a few of these but they are dry and difficult to read. The formatting doesn’t help either–it’s 80 column fixed width text as if it were old-school terminal output. I’ve had better luck reading them via <a href="http://pretty-rfc.herokuapp.com/">Pretty RFC</a>.</span>, I believe it was originally proposed by Amazon–years ago, made to address this problem and it now has broad support. I chose to follow the outdated guidance of not serving content from apex domains for my personal site but I didn’t for <a href="https://rocketlabdelta.com">rocketlabdelta.com</a> and I haven’t had any issues (not that either site really gets any traffic).</p> <p>Following the <code class="highlighter-rouge">location</code> header one more time we get:</p> <div class="highlighter-rouge"><div class="highlight"><pre class="highlight"><code>~ $ curl -I https://www.danhorst.com/ HTTP/2 200 cache-control: public, max-age=0, must-revalidate content-length: 0 content-type: text/html; charset=UTF-8 date: Fri, 11 Sep 2020 20:09:09 GMT etag: "53d06608fda472d36b8dae0280615192-ssl" strict-transport-security: max-age=31536000 age: 1 server: Netlify x-nf-request-id: 325c0416-d223-4844-9452-7f7737e97a73-8669214 </code></pre></div></div> <p>We’ve made it to a page! 2xx status codes mean things are working as expected (4xx are client errors, 5xx are server errors). Now the browser can process the HTML that is returned (you know it’s HTML because of the value of the <code class="highlighter-rouge">content-type</code> header). How the browser goes from the HTML document to a web page that you can see and interact with is complicated enough that it deserves it’s own article.</p>Dan Brubaker Horst