<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Exploration · Loosely Decoupled</title>
    <link>https://looselydecoupled.com/shape/exploration/</link>
    <description>Answers to PowerShell, Windows Server and OpenSSH questions the docs skip, with the reasoning behind them. Answer first, discussion after.</description>
    <language>en-us</language>
    
    <copyright>© 2026 Emmanuel Tsouris</copyright>
    <lastBuildDate>Fri, 02 Oct 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://looselydecoupled.com/shape/exploration/index.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Finding a Bridge in 2 TB of Dashcam Video by the Color of Tunnel Lights</title>
      <link>https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/</guid>
      <pubDate>Fri, 02 Oct 2026 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>Automation</category>
      <category>Python</category>
      
      <description>A red/blue ratio found the sodium-lit tunnel on 71 of 79 bridge drives after auto-exposure hid it from every brightness check.</description>
      <content:encoded><![CDATA[<figure><img src="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/interchange-curve-sunset_hu_6d94b69d2acd59a0.jpg" alt="A side-camera fisheye frame on the I-5/I-90 interchange curve at sunset: the low sun flares at the left edge, and the downtown Seattle skyline stands above the ramps and trees." width="1600" height="1280" loading="lazy" decoding="async">
</figure>
<p>I wanted every crossing of the I-90 floating bridge in 240 drive folders of
dashcam footage, 1,958 GB, for a montage. I wasn&rsquo;t going to watch the footage
to find them, and my Rivian R1T&rsquo;s GPS was too coarse to. What found them was the
color of the lights in the Mount Baker tunnel, which sits at the Seattle end
of the bridge. Brightness couldn&rsquo;t see that
tunnel. A red/blue ratio found it on 71 of the 79 bridge drives
scanned, and a person looking at one frame per candidate confirmed 68 crossings.
<a href="https://looselydecoupled.com/triage-dashcam-footage-claude-code/">How to sort months of dashcam footage with Claude
Code</a> has the rest of the job this was
one stage of.</p>
<h2 id="signals-instead-of-pixels">Signals instead of pixels<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#signals-instead-of-pixels"
     aria-label="Link to section: Signals instead of pixels" data-pagefind-ignore></a>
</h2>
<p>The footage already describes itself if you read the right parts of it. Each
15-minute segment carries <a href="https://looselydecoupled.com/rivian-dashcam-gps-location-ffprobe/">one GPS fix, at its start, in the container&rsquo;s
<code>location</code> tag</a>. That sorts drives by area, but one fix per
15 minutes can&rsquo;t place a bridge crossing to the minute, let alone the second.</p>
<p>The finer signal is in the keyframes. The truck writes one keyframe per second.
Decoding only those, downscaled to 64 × 48, gives a per-second record of a
drive and skips the 29 frames between them. Each
second becomes four numbers: the mean change in green from the previous
keyframe, which I called motion, and the mean red, green and blue. That record
was computed once, cached as CSV, and every detector afterward read the cache
instead of the video.</p>
<p>The first calibration read a motion of zero everywhere, parked or moving.
The cause was ffmpeg duplicating the keyframes back up to 30 fps, so every
frame was compared with a copy of itself. <code>-fps_mode passthrough</code> fixed it. The bug
showed up only because the check ran on a known parked segment and a known
moving one before it ran on anything else. A detector that reads zero
on both cases is measuring itself.</p>
<h2 id="why-brightness-failed">Why brightness failed<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#why-brightness-failed"
     aria-label="Link to section: Why brightness failed" data-pagefind-ignore></a>
</h2>
<p>A tunnel should be the easiest thing in a drive to find: the outside goes away
and the picture gets darker. In this footage it doesn&rsquo;t get darker. The camera&rsquo;s
auto-exposure raises the gain as the light drops and holds the frame&rsquo;s
brightness close to what it was in daylight. That&rsquo;s what the exposure control
is for, and it erases the one property a brightness threshold depends on.</p>
<h2 id="why-color-worked">Why color worked<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#why-color-worked"
     aria-label="Link to section: Why color worked" data-pagefind-ignore></a>
</h2>
<p>Exposure scales brightness, and the hue of the lamps comes through. The Mount
Baker tunnel is lit by sodium lamps, and sodium light is orange. Inside the
tunnel, the front camera&rsquo;s mean red rises above its mean blue. That ratio is
the whole detector.</p>
<p>It runs with three guards:</p>
<ul>
<li>The red/blue ratio has to stay above 1.0 for 25–150 seconds, so a single
orange frame or a long orange stretch doesn&rsquo;t count.</li>
<li>A rolling median smooths the ratio first, so one bright frame inside a tunnel
doesn&rsquo;t break the run in two.</li>
<li>The first and last 3 minutes of every drive are ignored, because parking
garages look the same to the ratio.</li>
</ul>
<figure>
  <img src="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/red-blue-ratio-tunnel.svg" alt="Two stacked charts over a 1,600-second window of one drive. The top chart is the smoothed red/blue ratio: it sits near 0.9 and crosses the dashed 1.0 threshold in five shaded runs, and the run at 381 seconds, labeled as the Mount Baker tunnel, peaks near 1.3. The bottom chart is mean green over the same seconds: it wanders between about 50 and 75 and stays in that range through the tunnel." loading="lazy" decoding="async"><figcaption>Drive 07_16_26_120000, front camera, from the cached per-second signals. The ratio is red / (blue + 1), smoothed by the detector&rsquo;s rolling median.</figcaption>
</figure>
<p>On the 79 bridge drives scanned, it found a tunnel on 71. Seven of
the eight misses were after dark. My reading is that the ratio describes
light, and once the light outside is artificial too, a tunnel stops standing
out from the road around it.</p>
<h2 id="detector-proposes-person-confirms">Detector proposes, person confirms<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#detector-proposes-person-confirms"
     aria-label="Link to section: Detector proposes, person confirms" data-pagefind-ignore></a>
</h2>
<p>Finding a tunnel isn&rsquo;t the same as finding the right one. A drive on I-90
passes more than one stretch that reads as a tunnel, including the Mercer
Island lid, and most drives had several candidates. The obvious rule, pick one
tunnel by its position in the drive, was right about two times in three.</p>
<p>I could have spent the effort on a better rule. Instead the tool writes a guess
for every drive and a contact sheet to check it against, then stops. Each row
of the sheet is one drive, and each tile is a frame from the bridge side of one
candidate. The bridge is unmistakable in a frame, so the right
column is obvious, and the edit is one number in a picks file, which <a href="https://looselydecoupled.com/ffmpeg-cut-multi-camera-dashcam-clips-in-sync/">the cut</a>
turns into synced clips.
Reviewing that way confirmed 68 crossings. Two night drives couldn&rsquo;t be
confirmed, because a dark frame of a dark bridge proves nothing, and one drive
had no crossing among its candidates.</p>
<figure><img src="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/review-sheet-row_hu_42d4d411e91d9c33.jpg" alt="One row of the review sheet: the drive from the chart above, with one tile per candidate. The first tile is a freeway lined with trees, the second is the floating bridge, and the last three are replaced by gray blocks." width="1600" height="256" loading="lazy" decoding="async"><figcaption>The second tile is the bridge, so the pick is candidate 2. Three frames removed.</figcaption>
</figure>
<p>The layout of the sheet mattered as much as having one. The first version was
a flat grid of every candidate frame, and it was easy to match a tile to the
wrong drive. One row per drive makes that mistake visible rather than merely
possible.</p>
<p>The sheet is also an instrument, and an instrument can be wrong. At one point I
announced that the picks for some drives were the Mercer Island lid, not the Mount
Baker tunnel. The sheets had been built with a seek that ffmpeg applied after
tiling, so they showed a different window than the one they claimed. Extracting
the same frame two ways exposed it, and the picks had been right all along.
When a result contradicts work that was already verified, check the instrument
before the conclusion.</p>
<p>The review step reads like an admission that the detector wasn&rsquo;t good enough.
I think it&rsquo;s the reason the detector could stay simple. Take a rule that&rsquo;s
right two times in three, and add a sheet a person reads at a glance. That
beats a rule that&rsquo;s right more often and still needs someone to check it.</p>
<h2 id="signals-that-could-not-separate-the-cases">Signals that could not separate the cases<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#signals-that-could-not-separate-the-cases"
     aria-label="Link to section: Signals that could not separate the cases" data-pagefind-ignore></a>
</h2>
<p>The same per-second record answered four other questions, and each answer was
no for the same reason: the signal moved for the thing I wanted and for
something I didn&rsquo;t.</p>
<ul>
<li><strong>Parked footage.</strong> Near-zero motion found parked time reliably, but there
was little to find: 23 GB of the 1,760 GB scanned, because with Gear Guard turned off,
the truck stops recording seconds after it parks.</li>
<li><strong>Hard stops.</strong> A sudden change in the picture marks braking, but turning
changes the picture as much. Of 875 candidates, all 12 at the top were turns
into parking lots or ordinary stops.</li>
<li><strong>Traffic jams.</strong> Sustained low motion should mark crawling traffic, but the
neighboring cars keep moving, so the picture keeps changing and a jam looks
like driving.</li>
<li><strong>Garage boundaries.</strong> A gray band at the top of the frame marks a concrete
ceiling on a clear day, but an overcast sky is the same gray. The boundary
was set by timing instead.</li>
</ul>
<p>None of these is fixed by tuning a threshold. When a signal can&rsquo;t tell two
cases apart, a better number for the threshold still can&rsquo;t.</p>
<p>Close passes did work once the frame&rsquo;s overall brightness change was
subtracted from the side cameras: 37 of the top 60 were real passes. A person
still had to sort them.</p>
<h2 id="signals-the-camera-leaves-alone">Signals the camera leaves alone<a class="heading-anchor" href="https://looselydecoupled.com/find-landmark-dashcam-video-tunnel-light-color/#signals-the-camera-leaves-alone"
     aria-label="Link to section: Signals the camera leaves alone" data-pagefind-ignore></a>
</h2>
<p>A camera processes its own picture before anything reads it, and that
processing exists to make the picture look normal. Auto-exposure made a tunnel
look like daylight and left the lamp color intact. Before measuring anything in
recorded video, ask which properties the camera flattens and which it passes
through, and build on the second kind.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How VBScript&#39;s Friction Produced PowerShell&#39;s Object Pipeline</title>
      <link>https://looselydecoupled.com/vbscript-primitive-glue-retrospective/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/vbscript-primitive-glue-retrospective/</guid>
      <pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>VBScript</category>
      <category>PowerShell</category>
      <category>History</category>
      <category>Windows Server</category>
      <category>DevOps</category>
      
      <description>The friction that moved Windows automation from VBScript&#39;s text to PowerShell&#39;s objects also moved cloud tooling from scripts to declarative templates.</description>
      <content:encoded><![CDATA[<p>If you started in the cloud era, infrastructure as code is JSON, HCL and YAML:
declare a state, and the provider makes it so. In the Windows environments I
managed in the early 2000s, state wasn&rsquo;t declared. It was coerced, one
script at a time, and the language was VBScript.</p>
<p>My claim: VBScript&rsquo;s friction is what produced PowerShell&rsquo;s object pipeline,
and the same friction is what moved cloud tooling from imperative scripts to
declarative templates. The history is worth a few minutes because it predicts
what the next tool will fix.</p>
<h2 id="on-error-resume-next"><code>On Error Resume Next</code><a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#on-error-resume-next"
     aria-label="Link to section: On Error Resume Next" data-pagefind-ignore></a>
</h2>
<p>In 2003, mapping a printer by security group or reading the BIOS version of
500 servers had no command. It had Notepad, and a <code>.vbs</code> file run by Windows
Script Host.</p>
<p>VBScript had no safety rails. The line that opened most production scripts
was:</p>
<div class="code-block code-block--wrap" data-copyable data-copy-label="VBScript">
  <div class="code-block__head"><span class="code-block__lang">VBScript</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vb" data-lang="vb"><span class="line"><span class="cl"><span class="k">On</span> <span class="k">Error</span> <span class="k">Resume</span> <span class="k">Next</span></span></span></code></pre></div></div>
  </div>
</div><p>It tells the interpreter to ignore any failing line and carry on. Distributed
systems talk about failing fast; a VBScript failed silently and left the
system in whatever state the last successful line produced. Nobody wrote it
out of carelessness. Without it, one missing printer stopped that user&rsquo;s logon
script at that line, and every mapping after it was skipped. Structured error
handling in VBScript was awkward enough that swallowing errors was the
pragmatic choice.</p>
<h2 id="wmi-as-the-engine-vbscript-as-the-crank">WMI as the engine, VBScript as the crank<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#wmi-as-the-engine-vbscript-as-the-crank"
     aria-label="Link to section: WMI as the engine, VBScript as the crank" data-pagefind-ignore></a>
</h2>
<p>The lasting legacy of that era isn&rsquo;t VBScript&rsquo;s syntax. It&rsquo;s that VBScript
was how a generation of administrators met WMI, Windows Management
Instrumentation, Microsoft&rsquo;s implementation of the DMTF&rsquo;s WBEM standard. WMI
exposed the operating system as a queryable database: disks, services and
processes as rows, with a SQL-like language to select them.</p>
<p>The database was good. The interface to it was this:</p>
<div class="code-block" data-copyable data-copy-label="VBScript">
  <div class="code-block__head"><span class="code-block__lang">VBScript</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-vb" data-lang="vb"><span class="line"><span class="cl"><span class="c">&#39; Free space per logical disk
</span></span></span><span class="line"><span class="cl"><span class="n">strComputer</span> <span class="o">=</span> <span class="s">&#34;.&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">Set</span> <span class="n">objWMIService</span> <span class="o">=</span> <span class="n">GetObject</span><span class="p">(</span><span class="s">&#34;winmgmts:\\&#34;</span> <span class="o">&amp;</span> <span class="n">strComputer</span> <span class="o">&amp;</span> <span class="s">&#34;\root\cimv2&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">Set</span> <span class="n">colItems</span> <span class="o">=</span> <span class="n">objWMIService</span><span class="p">.</span><span class="n">ExecQuery</span><span class="p">(</span><span class="s">&#34;Select * from Win32_LogicalDisk&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">For</span> <span class="k">Each</span> <span class="n">objItem</span> <span class="ow">in</span> <span class="n">colItems</span>
</span></span><span class="line"><span class="cl">    <span class="n">Wscript</span><span class="p">.</span><span class="n">Echo</span> <span class="s">&#34;Disk: &#34;</span> <span class="o">&amp;</span> <span class="n">objItem</span><span class="p">.</span><span class="n">Name</span> <span class="o">&amp;</span> <span class="s">&#34; Free Space: &#34;</span> <span class="o">&amp;</span> <span class="n">objItem</span><span class="p">.</span><span class="n">FreeSpace</span>
</span></span><span class="line"><span class="cl"><span class="k">Next</span></span></span></code></pre></div></div>
  </div>
</div><p>Seven lines to read one property, and every line is ceremony: a COM moniker,
a query string, a loop and string concatenation to print. The verbosity forced
administrators to learn loops, variable types and COM objects, which is the
one thing VBScript did for the profession. It was a high barrier, and it
separated the people who clicked from the people who scripted.</p>
<h2 id="from-parsing-text-to-passing-objects">From parsing text to passing objects<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#from-parsing-text-to-passing-objects"
     aria-label="Link to section: From parsing text to passing objects" data-pagefind-ignore></a>
</h2>
<p>Every VBScript that read another program&rsquo;s output parsed text. Jeffrey
Snover&rsquo;s Monad Manifesto (2002), the design document behind PowerShell 1.0
(2006), names that practice as the problem. Text output is a contract nobody
declared, so a script is coupled to the exact formatting of whatever it
reads. When &ldquo;Free Space&rdquo; became &ldquo;FreeSpace&rdquo;, the script broke.</p>
<p>PowerShell separated the data from its presentation. The same query, in the
modern cmdlet:</p>
<div class="code-block code-block--wrap" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="c"># Objects, not text</span>
</span></span><span class="line"><span class="cl"><span class="nb">Get-CimInstance</span> <span class="n">Win32_LogicalDisk</span> <span class="p">|</span> <span class="nb">Select-Object</span> <span class="n">DeviceID</span><span class="p">,</span> <span class="n">FreeSpace</span></span></span></code></pre></div></div>
  </div>
</div><p>Side by side, the two snippets show a change of model, not of syntax.
VBScript was imperative: you told the computer how to connect, how to iterate
and how to print. PowerShell is declarative at the call site: you ask for the
object and the pipeline handles the rest. Formatting happens once, at the end,
by a formatter that knows the object&rsquo;s type.</p>
<h2 id="why-this-matters-for-cloud-work">Why this matters for cloud work<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#why-this-matters-for-cloud-work"
     aria-label="Link to section: Why this matters for cloud work" data-pagefind-ignore></a>
</h2>
<p>Cloud tooling made the same move, a decade later. The first generation of AWS
automation was imperative scripts around the CLI: create an instance, wait,
attach a volume, tag it. CloudFormation (2011) and Terraform (2014) replaced
that with a declared end state and an engine that computes the steps.</p>
<p>VBScript was the imperative-scripts phase of Windows administration. It was
hard to debug and didn&rsquo;t scale past the person who wrote it. It was also
necessary: it&rsquo;s how administration got from the GUI to anything automated at
all, and the pain of it is what made the next model&rsquo;s advantages legible.</p>
<h2 id="what-would-change-my-mind">What would change my mind<a class="heading-anchor" href="https://looselydecoupled.com/vbscript-primitive-glue-retrospective/#what-would-change-my-mind"
     aria-label="Link to section: What would change my mind" data-pagefind-ignore></a>
</h2>
<p>The argument rests on one causal claim: that text-parsing friction, not some
other pressure, is what produced the object pipeline. The Monad Manifesto
supports it in Snover&rsquo;s own words, which is the best evidence available short
of the design meetings. The account could be a retrospective tidying of
decisions made for other reasons, such as .NET adoption inside Microsoft. If
it is, the lineage still holds but the lesson doesn&rsquo;t.</p>
<p>I still keep a folder of old <code>.vbs</code> scripts. I do not run them. They are a
record of what the tooling cost before someone decided the cost was the
problem. The next time a CloudFormation template takes an afternoon to debug,
the alternative was <code>On Error Resume Next</code>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>PowerShell Splatting: Replace Long One-Liners with a Hashtable</title>
      <link>https://looselydecoupled.com/splatting-readability-scaling/</link>
      <guid isPermaLink="true">https://looselydecoupled.com/splatting-readability-scaling/</guid>
      <pubDate>Sun, 12 Nov 2023 00:00:00 +0000</pubDate>
      <dc:creator>Emmanuel Tsouris</dc:creator>
      <category>PowerShell</category>
      <category>Code quality</category>
      
      <description>Splatting forces every parameter of a long command to be named and laid out, which keeps it readable and safe to change years after it was written.</description>
      <content:encoded><![CDATA[<p>Did you know a PowerShell command can take its parameters from a hashtable?
The feature is called splatting. It has been in the language since
PowerShell 2.0, nothing ever requires it, and so plenty of scripts are written
without it.</p>
<p>Here is a line it exists to fix. It opens an incident on a status page through
a REST API, with the token and the JSON body already in variables:</p>
<!-- style-check: off -->
<div class="code-block code-block--wrap" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="n">-Uri</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span> <span class="n">-Method</span> <span class="n">Post</span> <span class="n">-Headers</span> <span class="vm">@</span><span class="p">{</span><span class="n">Authorization</span><span class="p">=</span><span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span><span class="p">;</span><span class="n">Accept</span><span class="p">=</span><span class="s1">&#39;application/json&#39;</span><span class="p">}</span> <span class="n">-ContentType</span> <span class="s1">&#39;application/json&#39;</span> <span class="n">-Body</span> <span class="nv">$body</span> <span class="n">-TimeoutSec</span> <span class="mf">30</span></span></span></code></pre></div></div>
  </div>
</div><!-- style-check: on -->
<p>It works. It&rsquo;s also a line you have to read three times, and not because it&rsquo;s
wrong. It doesn&rsquo;t scale — not in execution time, in comprehension time. The
computer parses it in microseconds. A person parses it in minutes, even the
person who wrote it.</p>
<p>Splatting turns that line into a block you can read at a glance, and it&rsquo;s
worth using less as a convenience than as a discipline.</p>
<h2 id="six-months-later">Six months later<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#six-months-later"
     aria-label="Link to section: Six months later" data-pagefind-ignore></a>
</h2>
<p>A script like this is usually written in one sitting, for a job that needs
doing that evening, by someone who knows exactly what every argument is for.
Commands run across several lines on backtick continuations, parameters stack
one after another, a hashtable sits inline.</p>
<p>Six months later the person reading it doesn&rsquo;t know, even when it&rsquo;s the same
person.</p>
<p>The failure isn&rsquo;t correctness. The script still runs. The failure is that a
one-line change, a different endpoint, means re-deriving what the whole line
does before anyone dares touch it.</p>
<p><strong>A script its own author can&rsquo;t debug after six months has a problem, and so
does every script written the same way.</strong></p>
<h2 id="what-makes-a-line-hard-to-read">What makes a line hard to read<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#what-makes-a-line-hard-to-read"
     aria-label="Link to section: What makes a line hard to read" data-pagefind-ignore></a>
</h2>
<p>Cognitive load is the effort it takes to hold something in your head long
enough to understand it. In a command line it comes from a few places at once.</p>
<p><strong>Syntax you have to parse while reading.</strong> Where does the list of extensions
end? Is that parenthesis inside the script block or after it?</p>
<!-- style-check: off -->
<div class="code-block code-block--wrap" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Get-ChildItem</span> <span class="n">-Path</span> <span class="n">D:</span><span class="p">\</span><span class="n">Exports</span> <span class="n">-Include</span> <span class="p">*.</span><span class="n">csv</span><span class="p">,*.</span><span class="n">json</span> <span class="n">-Exclude</span> <span class="p">*</span><span class="n">-keep</span><span class="p">.*</span> <span class="n">-Recurse</span> <span class="o">-File</span> <span class="p">|</span> <span class="nb">Where-Object</span> <span class="p">{</span> <span class="nv">$_</span><span class="p">.</span><span class="py">LastWriteTime</span> <span class="o">-lt</span> <span class="p">(</span><span class="nb">Get-Date</span><span class="p">).</span><span class="py">AddDays</span><span class="p">(</span><span class="mf">-14</span><span class="p">)</span> <span class="p">}</span> <span class="p">|</span> <span class="nb">Remove-Item</span> <span class="n">-Force</span></span></span></code></pre></div></div>
  </div>
</div><!-- style-check: on -->
<p><strong>Density.</strong> One line means one chunk. The brain can&rsquo;t break it into pieces
and check them separately.</p>
<p><strong>Missing context.</strong> Why <code>D:\Exports</code>? Why fourteen days? What makes a file
worth keeping? None of that is in the code; it&rsquo;s in the head of whoever wrote
it, or in a document somewhere else.</p>
<p>Any one of these is manageable. The one-liner stacks all three.</p>
<h2 id="what-splatting-forces">What splatting forces<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#what-splatting-forces"
     aria-label="Link to section: What splatting forces" data-pagefind-ignore></a>
</h2>
<p>Splatting takes two steps: put the command&rsquo;s parameters in a hashtable, then
hand the whole table to the command with <code>@</code> in place of <code>$</code>:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$incident</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Uri</span>         <span class="p">=</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Method</span>      <span class="p">=</span> <span class="s1">&#39;Post&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Headers</span>     <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Authorization</span> <span class="p">=</span> <span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Accept</span>        <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ContentType</span> <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Body</span>        <span class="p">=</span> <span class="nv">$body</span>
</span></span><span class="line"><span class="cl">    <span class="n">TimeoutSec</span>  <span class="p">=</span> <span class="mf">30</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="nv">@incident</span></span></span></code></pre></div></div>
  </div>
</div><p>Same call. More lines. Far less to hold in your head, for four reasons.</p>
<p><strong>Every parameter is named and on its own line.</strong> Nothing is positional; nothing
depends on order.</p>
<p><strong>The structure is visible.</strong> The headers are a nested block, not a run of
braces in the middle of a line. You can see that the call posts JSON, with a
bearer token, to one endpoint.</p>
<p><strong>Changing it is safe.</strong> Adding a header is adding a line. Deleting a
parameter is deleting a line. There&rsquo;s no adjacent argument to break.</p>
<p><strong>Comments fit.</strong></p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$incident</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Uri</span>         <span class="p">=</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Method</span>      <span class="p">=</span> <span class="s1">&#39;Post&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Headers</span>     <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c"># Read the token from the vault, never from the script.</span>
</span></span><span class="line"><span class="cl">        <span class="n">Authorization</span> <span class="p">=</span> <span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Accept</span>        <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ContentType</span> <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Body</span>        <span class="p">=</span> <span class="nv">$body</span>
</span></span><span class="line"><span class="cl">    <span class="c"># Fail fast; the caller retries.</span>
</span></span><span class="line"><span class="cl">    <span class="n">TimeoutSec</span>  <span class="p">=</span> <span class="mf">30</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
  </div>
</div><p>None of this is for the computer. It&rsquo;s for the person reading the script
later — probably you, and if not you, someone who can&rsquo;t ask you.</p>
<h2 id="people-first-computers-second">People first, computers second<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#people-first-computers-second"
     aria-label="Link to section: People first, computers second" data-pagefind-ignore></a>
</h2>
<p>There&rsquo;s an old Unix principle: write programs for people first, computers
second. Not because the computer doesn&rsquo;t matter, but because the human is the
slow part. The machine executes the dense one-liner in the same time as the
splatted block. The human pays for the density every single time they read it.</p>
<p>Splatting is a forcing function for that principle. To splat, you have to name
every parameter, arrange them in a structure, and decide what you&rsquo;re passing.
You can&rsquo;t throw arguments at a cmdlet and hope.</p>
<h2 id="rules-that-are-not-obvious">Rules that are not obvious<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#rules-that-are-not-obvious"
     aria-label="Link to section: Rules that are not obvious" data-pagefind-ignore></a>
</h2>
<p>Splatting is simple until it isn&rsquo;t. These are the parts that trip people up.</p>
<p><strong>It&rsquo;s <code>@params</code> at the call and <code>$params</code> everywhere else.</strong> The hashtable is
a normal variable; only the call site uses <code>@</code>. Writing <code>Invoke-RestMethod $incident</code> passes the whole table as the first positional argument, and the
error message will be about a type conversion, not about splatting.</p>
<p><strong>Switch parameters take a boolean.</strong> <code>Recurse = $true</code>, not <code>Recurse = 'yes'</code>
and not a bare <code>Recurse</code>. A hashtable value has to be a value.</p>
<p><strong>You can mix splatted and explicit parameters.</strong> This is the pattern for a
common base with a per-call difference:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$common</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Headers</span>    <span class="p">=</span> <span class="vm">@</span><span class="p">{</span> <span class="n">Authorization</span> <span class="p">=</span> <span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">TimeoutSec</span> <span class="p">=</span> <span class="mf">30</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="nv">@common</span> <span class="n">-Uri</span> <span class="s2">&#34;</span><span class="nv">$api</span><span class="s2">/components&#34;</span>
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="nv">@common</span> <span class="n">-Uri</span> <span class="s2">&#34;</span><span class="nv">$api</span><span class="s2">/incidents&#34;</span></span></span></code></pre></div></div>
  </div>
</div><p>PowerShell merges them. Naming the same parameter in both places is an error,
which is what you want — a silent override would be worse.</p>
<p><strong>You can build the table conditionally.</strong> A one-liner can&rsquo;t express &ldquo;add
this parameter only if the variable is set&rdquo;; a hashtable can:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$copy</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Path</span>        <span class="p">=</span> <span class="nv">$source</span>
</span></span><span class="line"><span class="cl">    <span class="n">Destination</span> <span class="p">=</span> <span class="nv">$target</span>
</span></span><span class="line"><span class="cl">    <span class="n">Recurse</span>     <span class="p">=</span> <span class="vm">$true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="nv">$DryRun</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nv">$copy</span><span class="p">[</span><span class="s1">&#39;WhatIf&#39;</span><span class="p">]</span> <span class="p">=</span> <span class="vm">$true</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">Copy-Item</span> <span class="nv">@copy</span></span></span></code></pre></div></div>
  </div>
</div><p><strong>A function can pass its own parameters straight through.</strong> Inside a
function, <code>$PSBoundParameters</code> is a hashtable of the parameters the caller
supplied, and it splats:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="kd">function</span><span class="w"> </span><span class="nb">Get-RecentLog</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">param</span> <span class="p">([</span><span class="no">string</span><span class="p">]</span><span class="nv">$Path</span><span class="p">,</span> <span class="p">[</span><span class="no">switch</span><span class="p">]</span><span class="nv">$Recurse</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="nb">Get-ChildItem</span> <span class="nv">@PSBoundParameters</span> <span class="n">-Filter</span> <span class="p">*.</span><span class="n">log</span> <span class="o">-File</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">        <span class="nb">Sort-Object</span> <span class="n">LastWriteTime</span> <span class="n">-Descending</span> <span class="p">|</span>
</span></span><span class="line"><span class="cl">        <span class="nb">Select-Object</span> <span class="n">-First</span> <span class="mf">10</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
  </div>
</div><p>Whatever the caller passed goes through unchanged, and whatever they didn&rsquo;t
pass isn&rsquo;t mentioned at all. It&rsquo;s the cleanest way to write a wrapper, and
it only exists because splatting does.</p>
<p><strong>Arrays splat positionally.</strong> <code>@('a', 'b')</code> splatted fills parameters by
position, not by name. It&rsquo;s legal and almost never what you want in a script
you intend to read later.</p>
<h2 id="bigger-commands-bigger-differences">Bigger commands, bigger differences<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#bigger-commands-bigger-differences"
     aria-label="Link to section: Bigger commands, bigger differences" data-pagefind-ignore></a>
</h2>
<p>The gap between the two forms grows with the command. Here is the incident call
once it runs from a server. It goes through a proxy, sends a user agent the
status page can identify, and retries when the status page answers with an
error.</p>
<!-- style-check: off -->
<div class="code-block code-block--wrap" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="n">-Uri</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span> <span class="n">-Method</span> <span class="n">Post</span> <span class="n">-Headers</span> <span class="vm">@</span><span class="p">{</span><span class="n">Authorization</span><span class="p">=</span><span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span><span class="p">;</span><span class="n">Accept</span><span class="p">=</span><span class="s1">&#39;application/json&#39;</span><span class="p">;</span><span class="s1">&#39;X-Request-Id&#39;</span><span class="p">=</span><span class="nv">$requestId</span><span class="p">}</span> <span class="n">-ContentType</span> <span class="s1">&#39;application/json&#39;</span> <span class="n">-Body</span> <span class="nv">$body</span> <span class="n">-TimeoutSec</span> <span class="mf">30</span> <span class="n">-UserAgent</span> <span class="s1">&#39;deploy-bot/2.1&#39;</span> <span class="n">-Proxy</span> <span class="s1">&#39;http://proxy.corp.example:8080&#39;</span> <span class="n">-ProxyUseDefaultCredentials</span> <span class="n">-MaximumRetryCount</span> <span class="mf">3</span> <span class="n">-RetryIntervalSec</span> <span class="mf">5</span></span></span></code></pre></div></div>
  </div>
</div><!-- style-check: on -->
<p>One command, eleven parameters, one of them a table. Now the same call,
splatted:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$incident</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Uri</span>                        <span class="p">=</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Method</span>                     <span class="p">=</span> <span class="s1">&#39;Post&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Headers</span>                    <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Authorization</span>  <span class="p">=</span> <span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Accept</span>         <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">        <span class="s1">&#39;X-Request-Id&#39;</span> <span class="p">=</span> <span class="nv">$requestId</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ContentType</span>                <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Body</span>                       <span class="p">=</span> <span class="nv">$body</span>
</span></span><span class="line"><span class="cl">    <span class="n">TimeoutSec</span>                 <span class="p">=</span> <span class="mf">30</span>
</span></span><span class="line"><span class="cl">    <span class="n">UserAgent</span>                  <span class="p">=</span> <span class="s1">&#39;deploy-bot/2.1&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Proxy</span>                      <span class="p">=</span> <span class="s1">&#39;http://proxy.corp.example:8080&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">ProxyUseDefaultCredentials</span> <span class="p">=</span> <span class="vm">$true</span>
</span></span><span class="line"><span class="cl">    <span class="n">MaximumRetryCount</span>          <span class="p">=</span> <span class="mf">3</span>
</span></span><span class="line"><span class="cl">    <span class="n">RetryIntervalSec</span>           <span class="p">=</span> <span class="mf">5</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="nv">@incident</span></span></span></code></pre></div></div>
  </div>
</div><p>Longer, and easier to understand, modify and debug. When the proxy changes, the
proxy is one line you can find. <code>MaximumRetryCount</code> and <code>RetryIntervalSec</code> need
PowerShell 7. To run the same call on Windows PowerShell 5.1, you delete those
two lines; nothing around them changes.</p>
<h2 id="when-not-to-splat">When not to splat<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#when-not-to-splat"
     aria-label="Link to section: When not to splat" data-pagefind-ignore></a>
</h2>
<p>Splatting is a tool for commands that are hard to read. Applying it to
commands that aren&rsquo;t adds lines and nothing else.</p>
<p><strong>One or two parameters.</strong> <code>Get-Content 'settings.json'</code> doesn&rsquo;t improve with
a hashtable.</p>
<p><strong>Interactive use.</strong> At the prompt you&rsquo;re the only reader, and you&rsquo;re reading
it right now.</p>
<p><strong>Idioms everyone knows.</strong> <code>Get-ChildItem | Where-Object Name -like '*.log'</code> is
a pattern PowerShell users recognize on sight; splatting it hides the idiom.</p>
<p>The rule I use: <strong>splat when future-you would have to re-read the line to
understand it</strong>. For most scripts that turns out to be most of the
non-trivial calls, and almost none of the trivial ones.</p>
<h2 id="documentation-you-no-longer-need">Documentation you no longer need<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#documentation-you-no-longer-need"
     aria-label="Link to section: Documentation you no longer need" data-pagefind-ignore></a>
</h2>
<p>A splatted call documents itself in two places. The variable name says what
the call is for; the table says how:</p>
<div class="code-block" data-copyable data-copy-label="PowerShell">
  <div class="code-block__head"><span class="code-block__lang">PowerShell</span>
  </div>
  <div class="code-block__scroll">
    <div data-copy-source><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-powershell" data-lang="powershell"><span class="line"><span class="cl"><span class="nv">$openIncident</span> <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="n">Uri</span>         <span class="p">=</span> <span class="s1">&#39;https://status.example.com/api/incidents&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Method</span>      <span class="p">=</span> <span class="s1">&#39;Post&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Headers</span>     <span class="p">=</span> <span class="vm">@</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="n">Authorization</span> <span class="p">=</span> <span class="s2">&#34;Bearer </span><span class="nv">$token</span><span class="s2">&#34;</span>
</span></span><span class="line"><span class="cl">        <span class="n">Accept</span>        <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">ContentType</span> <span class="p">=</span> <span class="s1">&#39;application/json&#39;</span>
</span></span><span class="line"><span class="cl">    <span class="n">Body</span>        <span class="p">=</span> <span class="nv">$body</span>
</span></span><span class="line"><span class="cl">    <span class="n">TimeoutSec</span>  <span class="p">=</span> <span class="mf">30</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nb">Invoke-RestMethod</span> <span class="nv">@openIncident</span></span></span></code></pre></div></div>
  </div>
</div><p>The one-liner needed a comment above it to say what it did. This doesn&rsquo;t.
Comments still earn their place for <em>why</em> — why this endpoint, why this
timeout —
but <em>what</em> is now in the code, where it can&rsquo;t drift from the code.</p>
<h2 id="readability-compounds">Readability compounds<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#readability-compounds"
     aria-label="Link to section: Readability compounds" data-pagefind-ignore></a>
</h2>
<p>Every future change to a script starts with reading it. A bug fix, a new
parameter, an upgrade to a newer module: read first, then edit. The cost of an
unreadable line is paid on every one of those visits, for as long as the script
is in use. The scripts that matter stay in use for years.</p>
<p>Splatting is a small feature. What it represents is larger: optimize for the
person who will maintain the code, not the machine that will run it. The
machine is fast and doesn&rsquo;t care whether the call is one line or twenty. The
person is slow and cares whether they can understand it without an
afternoon of archaeology.</p>
<p>Splatting is how a script written in one evening stays usable for the next
several years.</p>
<h2 id="references">References<a class="heading-anchor" href="https://looselydecoupled.com/splatting-readability-scaling/#references"
     aria-label="Link to section: References" data-pagefind-ignore></a>
</h2>
<ul>
<li><a href="https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_splatting" rel="external noopener" target="_blank">about_Splatting<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/splatting-readability-scaling/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> — the PowerShell documentation, including <code>$PSBoundParameters</code> and positional splatting</li>
<li><a href="http://www.catb.org/~esr/writings/taoup/html/ch01s06.html" rel="external noopener" target="_blank">Basics of the Unix Philosophy<svg class="icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><use href="https://looselydecoupled.com/splatting-readability-scaling/#i-external-link"/></svg><span class="visually-hidden"> (opens in a new tab)</span></a> — Eric Raymond on writing for people first</li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
