<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Brett Rowberry’s Blog]]></title><description><![CDATA[I love to write!]]></description><link>https://brettrowberry.com</link><generator>RSS for Node</generator><lastBuildDate>Mon, 20 Apr 2026 12:34:25 GMT</lastBuildDate><atom:link href="https://brettrowberry.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Vertical Tabs in Chrome]]></title><description><![CDATA[I've been using Arc Browser for a long time. Why? Because I love vertical tabs in my browser. They allow me to read more of each tab's title when I have a lot of tabs.
What Are Vertical Tabs?
Tabs are]]></description><link>https://brettrowberry.com/vertical-tabs-in-chrome</link><guid isPermaLink="true">https://brettrowberry.com/vertical-tabs-in-chrome</guid><category><![CDATA[mouse]]></category><category><![CDATA[trackpad]]></category><category><![CDATA[mac]]></category><category><![CDATA[vertical-tabs]]></category><category><![CDATA[horizontal-tabs]]></category><category><![CDATA[tabs]]></category><category><![CDATA[browser]]></category><category><![CDATA[arc_browser]]></category><category><![CDATA[Google Chrome]]></category><category><![CDATA[Google Chrome]]></category><category><![CDATA[claude]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Fri, 17 Apr 2026 03:41:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/613450e6489de51a6a7aed22/cf12b583-bb04-46e9-9bf4-666b54035ad6.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've been using <a href="https://arc.net/">Arc Browser</a> for a long time. Why? Because I love vertical tabs in my browser. They allow me to read more of each tab's title when I have a lot of tabs.</p>
<h2>What Are Vertical Tabs?</h2>
<p>Tabs are tabs. When we say vertical or horizontal tabs, we're talking about how they're displayed. Vertical tabs are arranged in a vertical stack, instead of a horizontal list.</p>
<h2>What's Wrong With Horizontal Tabs?</h2>
<p>As horizontal tabs accumulate, they either have to get narrower (less legible) or be scrolled sideways, which is difficult to do with most mice (but not trackpads!).</p>
<h2>Are Vertical Tabs New?</h2>
<p>Vertical tabs in software applications aren't a new concept. After all, look this macOS Finder window with "tabs" <code>Recents</code>, <code>Shared</code>, etc.:</p>
<img src="https://cdn.hashnode.com/uploads/covers/613450e6489de51a6a7aed22/02a9a97f-39f5-4af6-a3e2-de74502b32b0.png" alt="macOS Finder window showing horizontal tabs" style="display:block;margin:0 auto" />

<h2>Tabs in Browsers</h2>
<p>Many browsers have had horizontal tabs for a long time. Apparently, <a href="https://www.buzzfeednews.com/article/josephbernstein/meet-the-man-who-invented-tabs">horizontal tabs in browsers first appeared in 1997 with SimulBrowse (later NetCaptor) by Adam Stiles. Mozilla picked up and popularized it in 2002</a>. I love this snippet from the interview:</p>
<blockquote>
<p><strong>Did you have an "aha!" moment, when you realized that tabbed browsing would be a good thing to put in NetCaptor?</strong></p>
<p><strong>Adam Stiles:</strong> NetCaptor (originally SimulBrowse) was built from the beginning to be a tabbed browser. The HTML editor I was using at the time (HomeSite) had tabs, so I was used to flipping between a bunch of HTML documents. I wanted the same thing in my browser, so I built it. At first it was just an experiment to see if I could do it.</p>
</blockquote>
<p>From developer tools to the mainstream!</p>
<h2>Vertical Tabs in Browsers</h2>
<p>From what I can gather, Opera got vertical tabs in 2005. Chrome had an early version that was scrapped, but it officially got them back on <a href="https://blog.google/products-and-platforms/products/chrome/new-chrome-productivity-features/">April 7, 2026</a>.</p>
<h2>Why Leave Arc?</h2>
<p>If Arc was working for me, why switch back to Chrome? Well, I use Claude at work and I can't get the currently experimental <a href="https://claude.com/claude-for-chrome">Claude for Chrome</a> extension to work with Arc.</p>
<h2>Enabling Vertical Tabs in Google Chrome</h2>
<p>To enable this feature, I had to go to <code>chrome://flags/#vertical-tabs</code> and switch from <code>Default</code> to <code>Enabled</code>. <em>For reference, my installed version of Chrome is Version 147.0.7727.102 (Official Build) (arm64).</em></p>
<img src="https://cdn.hashnode.com/uploads/covers/613450e6489de51a6a7aed22/65d335a0-caf0-40b2-8c50-ee8ccc5c7313.png" alt="" style="display:block;margin:0 auto" />

<p>Wait, nothing happened. I called my brother. Turns out this flag enables the feature, but the arrangement of tabs still has to be toggled from horizontal to vertical. To do so, secondary click (right click) on a tab and select <code>Show Tabs Vertically</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/613450e6489de51a6a7aed22/62fbc765-ac7f-494c-8a59-b0ff527d27f7.png" alt="" style="display:block;margin:0 auto" />

<p>Voilà! Vertical tabs!</p>
<img src="https://cdn.hashnode.com/uploads/covers/613450e6489de51a6a7aed22/8cd1fa76-3d46-4c61-8b3d-3bcf10c455fb.png" alt="" style="display:block;margin:0 auto" />

<p>As you can imagine, to switch back to horizontal tabs, do a secondary click (right click) on a tab and select <code>Show Tabs Horizontally</code>.</p>
<h2>Aside</h2>
<p>Why do I say secondary click instead of right click? Because I'm an insufferable left-handed Mac user that uses mice, vertical and otherwise, and trackpads ambidextrously. So, to me, they're primary and secondary click, not left and right click.</p>
]]></content:encoded></item><item><title><![CDATA[Transpose a Matrix in Clojure]]></title><description><![CDATA[Imagine rows of letters, numbers, and Greek letters:
[[:a :b]
 [:1 :2]
 [:alpha :beta]]

Now, you want to transpose them. You started with a vector of vectors. Clojure's map function is pretty amazing]]></description><link>https://brettrowberry.com/transpose-a-matrix-in-clojure</link><guid isPermaLink="true">https://brettrowberry.com/transpose-a-matrix-in-clojure</guid><category><![CDATA[transpose]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[Matrix]]></category><category><![CDATA[map]]></category><category><![CDATA[Functional Programming]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 06 Nov 2025 05:27:58 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/iar-afB0QQw/upload/4282357e0350e755e23ae2cdd586e1ff.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine rows of letters, numbers, and Greek letters:</p>
<pre><code class="language-plaintext">[[:a :b]
 [:1 :2]
 [:alpha :beta]]
</code></pre>
<p>Now, you want to transpose them. You started with a vector of vectors. Clojure's <a href="https://clojuredocs.org/clojure.core/map"><code>map</code></a> function is pretty amazing::</p>
<blockquote>
<ul>
<li><p>(map f)</p>
</li>
<li><p>(map f coll)</p>
</li>
<li><p>(map f c1 c2)</p>
</li>
<li><p>(map f c1 c2 c3)</p>
</li>
<li><p>(map f c1 c2 c3 &amp; colls)</p>
</li>
</ul>
<p>Returns a lazy sequence consisting of the result of applying f to the set of first items of each coll, followed by applying f to the set of second items in each coll, until any one of the colls is exhausted. Any remaining items in other colls are ignored. Function f should accept number-of-colls arguments. Returns a transducer when no collection is provided.</p>
</blockquote>
<p>Normally, I think of this arity of <code>map</code>: <code>(map f coll)</code>. In your case, you have three collections, so you'll make use of <code>(map f c1 c2 c3)</code>. You want to stick with vectors, so you'll use <code>mapv</code>, which has the same semantics.</p>
<p>If you ignore the outer vector, you can use <code>mapv</code> with <code>vector</code>:</p>
<pre><code class="language-clojure">(mapv vector [:a :b] [:1 :2] [:alpha :beta])
;;=&gt; [[:a :1 :alpha]
;;    [:b :2 :beta]]
</code></pre>
<p>You don’t have to omit the outer vector thanks to the magic of <a href="https://clojuredocs.org/clojure.core/apply"><code>apply</code></a>, which allows you to pass a collection of arguments to a function that wants individual arguments:</p>
<pre><code class="language-clojure">(apply mapv vector [[:a :b] [:1 :2] [:alpha :beta]])
;;=&gt; [[:a :1 :alpha]
;;    [:b :2 :beta]]
</code></pre>
<p>This is amazing.</p>
]]></content:encoded></item><item><title><![CDATA[Binary Search in Clojure]]></title><description><![CDATA[I decided to implement linear search before going after binary search. Now, I’m ready!
With linear search, we required the elements be sorted. We didn’t require the elements be bounded. In binary search, the inputs must be bounded because we maintain...]]></description><link>https://brettrowberry.com/binary-search-in-clojure</link><guid isPermaLink="true">https://brettrowberry.com/binary-search-in-clojure</guid><category><![CDATA[Clojure]]></category><category><![CDATA[Binary Search Algorithm]]></category><category><![CDATA[search]]></category><category><![CDATA[division operator]]></category><category><![CDATA[integer]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Tue, 04 Nov 2025 03:04:31 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/jPVcZsxRGJo/upload/42b67a1ae45df4e5ecd754ffcf527a10.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I decided to implement <a target="_blank" href="https://brettrowberry.com/linear-search-in-clojure">linear search</a> before going after binary search. Now, I’m ready!</p>
<p>With linear search, we required the elements be sorted. We didn’t require the elements be bounded. In binary search, the inputs must be bounded because we maintain two indexes or pointers that start at the beginning and end of the search space. Because we move one index half-way closer to the other each iteration, the time complexity is O(log n).</p>
<h1 id="heading-solution">Solution</h1>
<p>Loop recur was a nice combination for linear search. So, we’ll use it again here.</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">binary-search</span>
  [x xs]
  (<span class="hljs-name"><span class="hljs-builtin-name">loop</span></span> [low <span class="hljs-number">0</span>
         high (<span class="hljs-name"><span class="hljs-builtin-name">dec</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">count</span></span> xs))]
    (<span class="hljs-name"><span class="hljs-builtin-name">when</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">&lt;=</span></span> low high)
      (<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [mid (<span class="hljs-name"><span class="hljs-builtin-name">quot</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> low high) <span class="hljs-number">2</span>)
            mid-val (<span class="hljs-name">xs</span> mid)]
        (<span class="hljs-name"><span class="hljs-builtin-name">cond</span></span>
          (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> x mid-val) mid
          (<span class="hljs-name"><span class="hljs-builtin-name">&lt;</span></span> x mid-val) (<span class="hljs-name"><span class="hljs-builtin-name">recur</span></span> low (<span class="hljs-name"><span class="hljs-builtin-name">dec</span></span> mid))  <span class="hljs-comment">;; x in left half, scoot high index</span>
          <span class="hljs-symbol">:else</span>         (<span class="hljs-name"><span class="hljs-builtin-name">recur</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> mid) high) <span class="hljs-comment">;; x in right half, scoot low index</span>
          )))))
</code></pre>
<h1 id="heading-challenges">Challenges</h1>
<h2 id="heading-two-are-better-than-one">Two Are Better Than One</h2>
<p>One of the hardest parts for me in figuring this out was that I forgot two indexes are necessary. I tried using a single index and it never converged.</p>
<h2 id="heading-integer-division">Integer Division</h2>
<p>In addition, my <code>mid</code> formula was wrong!</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [low <span class="hljs-number">1</span>
      high <span class="hljs-number">10</span>]
  [(/ (<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> low high) <span class="hljs-number">2</span>)      <span class="hljs-comment">;; 11/2</span>
   (<span class="hljs-name"><span class="hljs-builtin-name">quot</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> low high) <span class="hljs-number">2</span>)]) <span class="hljs-comment">;; 5</span>
</code></pre>
<p>Sometimes, <code>/</code> returns a <code>clojure.lang.Ratio</code> while <code>quot</code> always returns a <code>java.lang.Long</code>. I’ve never used <code>quot</code> before. In C, <code>/</code> truncates toward 0 when both inputs are <code>int</code>s, which is what we need.</p>
<h1 id="heading-tests">Tests</h1>
<pre><code class="lang-clojure">(<span class="hljs-name">are</span> [x xs i] (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> (<span class="hljs-name">binary-search</span> x xs) i)
  <span class="hljs-number">1</span> [] <span class="hljs-literal">nil</span>
  <span class="hljs-number">1</span> [<span class="hljs-number">1</span>] <span class="hljs-number">0</span>
  <span class="hljs-number">1</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span>] <span class="hljs-number">0</span>
  <span class="hljs-number">-1</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-literal">nil</span>
  <span class="hljs-number">1</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">0</span>
  <span class="hljs-number">3</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">1</span>
  <span class="hljs-number">5</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">2</span>
  <span class="hljs-number">7</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">3</span>
  <span class="hljs-number">9</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">4</span>
  <span class="hljs-number">10</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-literal">nil</span>
  <span class="hljs-number">25</span> (<span class="hljs-name"><span class="hljs-builtin-name">vec</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">101</span>)) <span class="hljs-number">25</span>
  <span class="hljs-number">75</span> (<span class="hljs-name"><span class="hljs-builtin-name">vec</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">101</span>)) <span class="hljs-number">75</span>)
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Linear Search in Clojure]]></title><description><![CDATA[I consider myself a classically trained computer scientist. However, I don’t regularly practice implementing algorithms at home. I hope to do more of that! At work, I do much higher level things, like partnering with a product manager, establishing a...]]></description><link>https://brettrowberry.com/linear-search-in-clojure</link><guid isPermaLink="true">https://brettrowberry.com/linear-search-in-clojure</guid><category><![CDATA[algorithms]]></category><category><![CDATA[linearsearch]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[search]]></category><category><![CDATA[sorted]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Sun, 02 Nov 2025 03:43:07 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/d9ILr-dbEdg/upload/992039f07e98b875e23135fffb035ae2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I consider myself a classically trained computer scientist. However, I don’t regularly practice implementing algorithms at home. I hope to do more of that! At work, I do much higher level things, like partnering with a product manager, establishing and maintaining monitoring and alerting, setting up on-call rotations, capacity planning, etc. However, I wanted to see if I could implement a binary search algorithm. Then I thought, can I do a linear search?</p>
<p>First, what do I mean by search? Find the location, or index, of a desired item in a sorted collection.</p>
<p>We’ll assume the collection is sorted in ascending order (1, 2, 3, … and not 10, 9, 8, …). This doesn’t actually matter for linear search. It does matter for binary search! The best case for a linear search algorithm is O(1), when the desired item is the first in the collection. The worst case is O(n), when the desired item is the last in the collection or isn’t present at all. One thing I really want in my implementation is early return.</p>
<p>Since most of my algorithmic training was done in C (probably C89 since variable declarations for iterators had to happen outside of for loops), I thought I’d start with <code>while</code>. This turned out to be very painful! I included a little test suite.</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">ns</span></span> sorting
  (<span class="hljs-symbol">:require</span> [clojure.test <span class="hljs-symbol">:refer</span> [are]]))

(<span class="hljs-keyword">defn</span> <span class="hljs-title">linear-search-while</span>
  [x xs]
  (<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [i (<span class="hljs-name"><span class="hljs-builtin-name">atom</span></span> <span class="hljs-number">0</span>)]
    (<span class="hljs-name"><span class="hljs-builtin-name">while</span></span>
     (<span class="hljs-name"><span class="hljs-builtin-name">and</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">not=</span></span> x (<span class="hljs-name"><span class="hljs-builtin-name">get</span></span> xs @i))     <span class="hljs-comment">;; haven't found it yet</span>
          (<span class="hljs-name"><span class="hljs-builtin-name">&lt;</span></span> @i (<span class="hljs-name"><span class="hljs-builtin-name">dec</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">count</span></span> xs)))) <span class="hljs-comment">;; haven't gotten to the end yet</span>
      (<span class="hljs-name"><span class="hljs-builtin-name">swap!</span></span> i inc))
    (<span class="hljs-name"><span class="hljs-builtin-name">if</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> x (<span class="hljs-name"><span class="hljs-builtin-name">get</span></span> xs @i))          <span class="hljs-comment">;; find it?</span>
      @i
      <span class="hljs-literal">nil</span>)))

(<span class="hljs-name">are</span> [x xs i] (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> (<span class="hljs-name">linear-search-while</span> x xs) i)
  <span class="hljs-number">1</span> [] <span class="hljs-literal">nil</span>
  <span class="hljs-number">1</span> [<span class="hljs-number">1</span>] <span class="hljs-number">0</span>
  <span class="hljs-number">1</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">0</span>
  <span class="hljs-number">5</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">2</span>
  <span class="hljs-number">9</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-number">4</span>
  <span class="hljs-number">-1</span> [<span class="hljs-number">1</span> <span class="hljs-number">3</span> <span class="hljs-number">5</span> <span class="hljs-number">7</span> <span class="hljs-number">9</span>] <span class="hljs-literal">nil</span>)
</code></pre>
<p>I got the stopping condition in the while loop wrong several times. First, we want to stop when we’ve found <code>x</code>. Second, we want to stop once we’ve exhausted the collection. I can’t think of a time I had less fun writing Clojure. Mutation, manually keeping track of an index, fiddling with a while loop. This is the worst. It does work, though! One thing that jumped out to me implementing this the use of <code>nil</code> instead of a sentinel value, like -1, that is common when coding in something like C.</p>
<p>What’s like a while loop but way better? Loop and recur! With a while loop, you say what must be true to continue. With loop recur, you say when to try another iteration and with what input. So, here’s a version that uses that.</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">linear-search-loop-recur-cond</span>
  [x xs]
  (<span class="hljs-name"><span class="hljs-builtin-name">loop</span></span> [i <span class="hljs-number">0</span>]
    (<span class="hljs-name"><span class="hljs-builtin-name">cond</span></span>
      (<span class="hljs-name"><span class="hljs-builtin-name">&gt;=</span></span> i (<span class="hljs-name"><span class="hljs-builtin-name">count</span></span> xs)) <span class="hljs-literal">nil</span>    <span class="hljs-comment">;; we're at the end, not found</span>
      (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> x (<span class="hljs-name"><span class="hljs-builtin-name">nth</span></span> xs i)) i       <span class="hljs-comment">;; found it</span>
      <span class="hljs-symbol">:else</span> (<span class="hljs-name"><span class="hljs-builtin-name">recur</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> i))))) <span class="hljs-comment">;; try again at the next spot</span>
</code></pre>
<p>This is so much better!</p>
<p>Here’s another way to do it.</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">linear-search-loop-recur-when</span>
  [x xs]
  (<span class="hljs-name"><span class="hljs-builtin-name">loop</span></span> [i <span class="hljs-number">0</span>]
    (<span class="hljs-name"><span class="hljs-builtin-name">when</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">&lt;</span></span> i (<span class="hljs-name"><span class="hljs-builtin-name">count</span></span> xs))  <span class="hljs-comment">;; don't run off the end</span>
      (<span class="hljs-name"><span class="hljs-builtin-name">if</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> x (<span class="hljs-name"><span class="hljs-builtin-name">nth</span></span> xs i))  <span class="hljs-comment">;; find it?</span>
        i                   <span class="hljs-comment">;; found it</span>
        (<span class="hljs-name"><span class="hljs-builtin-name">recur</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> i)))))) <span class="hljs-comment">;; try again at the next spot</span>
</code></pre>
<p>It’s the same number of lines. I like both.</p>
]]></content:encoded></item><item><title><![CDATA[Clojure +, +', and unchecked-add]]></title><description><![CDATA[+
In Clojure core there’s a handy function, +. I’ll bet you know what it does:
(+ 1 2 3)
;;=> 6

Let’s introduce a handy function as we explore:
(defn value-and-type
  [& args]
  (map (juxt identity type) args))

Let’s look at the largest supported L...]]></description><link>https://brettrowberry.com/clojure-plus-plus-prime-and-unchecked-add</link><guid isPermaLink="true">https://brettrowberry.com/clojure-plus-plus-prime-and-unchecked-add</guid><category><![CDATA[unchecked]]></category><category><![CDATA[overflow]]></category><category><![CDATA[underflow]]></category><category><![CDATA[Math]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[Java]]></category><category><![CDATA[exception]]></category><category><![CDATA[throw]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 09 Oct 2025 03:33:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759980823884/d9daf835-1082-4c94-af1b-bd7f56cd950d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-kw">+</h1>
<p>In Clojure core there’s a handy function, <code>+</code>. I’ll bet you know what it does:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> <span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; 6</span>
</code></pre>
<p>Let’s introduce a handy function as we explore:</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">value-and-type</span>
  [&amp; args]
  (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">juxt</span></span> identity type) args))
</code></pre>
<p>Let’s look at the largest supported <code>Long</code>:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span> Long/MAX_VALUE)
<span class="hljs-comment">;;=&gt; ([9223372036854775807 java.lang.Long])</span>
</code></pre>
<p>What happens if we exceed it?</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span> (<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> <span class="hljs-number">1</span> Long/MAX_VALUE))
<span class="hljs-comment">;;=&gt; Execution error (ArithmeticException) at java.lang.Math/addExact (Math.java:931).</span>
<span class="hljs-comment">;;   long overflow</span>
</code></pre>
<p>It throws!</p>
<h1 id="heading-kyc">+'</h1>
<p>The fact that <code>+</code> throws on overflow is why <code>+'</code> exists:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span> (<span class="hljs-name">+'</span> <span class="hljs-number">1</span> Long/MAX_VALUE))
<span class="hljs-comment">;;=&gt; ([9223372036854775808N</span>
<span class="hljs-comment">;;     clojure.lang.BigInt])</span>
</code></pre>
<p>Instead of overflowing, it promoted from a <code>Long</code> to a <code>BigInt</code>. In addition to <code>+'</code>, there are <code>-'</code>, <code>*'</code>, <code>inc'</code>, and <code>dec'</code>.</p>
<h1 id="heading-unchecked-add">unchecked-add</h1>
<p>Now for <code>unchecked-add</code>. Notice how it wraps around:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span>
 Long/MIN_VALUE
 (<span class="hljs-name"><span class="hljs-builtin-name">unchecked-add</span></span> Long/MAX_VALUE <span class="hljs-number">1</span>))
<span class="hljs-comment">;;=&gt; ([-9223372036854775808 java.lang.Long]</span>
<span class="hljs-comment">;;    [-9223372036854775808 java.lang.Long])</span>
</code></pre>
<p>In addition to <code>unchecked-add</code>, there are <code>unchecked-subtract</code>, <code>unchecked-multiply</code>, <code>unchecked-inc</code>, <code>unchecked-dec</code>, and <code>unchecked-negate</code>.</p>
<h1 id="heading-summary">Summary</h1>
<p>If you’re dealing with a <code>Long</code>, on overflow:</p>
<ul>
<li><p><code>+</code> throws</p>
</li>
<li><p><code>+'</code> promotes to <code>BigInt</code></p>
</li>
<li><p><code>unchecked-add</code> wraps around</p>
</li>
</ul>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">+</span></span> Long/MAX_VALUE <span class="hljs-number">1</span>)
<span class="hljs-comment">;;=&gt; Execution error (ArithmeticException) at java.lang.Math/addExact (Math.java:931).</span>
<span class="hljs-comment">;;   long overflow</span>
(<span class="hljs-name">+'</span> Long/MAX_VALUE <span class="hljs-number">1</span>)
<span class="hljs-comment">;;=&gt; 9223372036854775808N</span>
(<span class="hljs-name"><span class="hljs-builtin-name">unchecked-add</span></span> Long/MAX_VALUE <span class="hljs-number">1</span>)
<span class="hljs-comment">;;=&gt; -9223372036854775808</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Clojure Ratios]]></title><description><![CDATA[Computers do math really quickly. Yet, computers struggle with all kinds of things with math. Take, for example, the simple fraction: 2/3. This should be easy to store right? In most languages, it isn’t.
We’re going to use 2/3 throughout all our exam...]]></description><link>https://brettrowberry.com/clojure-ratios</link><guid isPermaLink="true">https://brettrowberry.com/clojure-ratios</guid><category><![CDATA[Clojure]]></category><category><![CDATA[ratio]]></category><category><![CDATA[binary]]></category><category><![CDATA[Math]]></category><category><![CDATA[computing]]></category><category><![CDATA[Precision]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Tue, 07 Oct 2025 04:17:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759807867158/0c9ae250-94f1-49c8-9be0-92a93790bc03.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Computers do math really quickly. Yet, computers struggle with all kinds of things with math. Take, for example, the simple fraction: 2/3. This should be easy to store right? In most languages, it isn’t.</p>
<p>We’re going to use 2/3 throughout all our examples. We’ll use the literal <code>2/3</code> instead of <code>(/ 2 3)</code> because it’s more compact, and is equivalent:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span> (/ <span class="hljs-number">2</span> <span class="hljs-number">3</span>))
<span class="hljs-comment">;;=&gt; true</span>

<span class="hljs-number">2</span>/<span class="hljs-number">3</span>
<span class="hljs-comment">;;=&gt; 2/3</span>
</code></pre>
<p>If you’ve programmed in most languages, you probably expected output like <code>0.6666667</code>. In Clojure, we need to force that with a cast:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; 0.6666667</span>
</code></pre>
<p>After casting, we can see they’re not equal:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">=</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>))
<span class="hljs-comment">;;=&gt; false</span>
</code></pre>
<p>So, what was that <code>2/3</code> we saw? Let’s inspect its type, and its value. I don’t want to write that more than once, so here’s a cool little helper function:</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">value-and-type</span>
  [&amp; args]
  (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">juxt</span></span> identity type) args))
</code></pre>
<p>Here it is in action:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>))
<span class="hljs-comment">;;=&gt; ([2/3 clojure.lang.Ratio]</span>
<span class="hljs-comment">;;    [0.6666667 java.lang.Float])</span>
</code></pre>
<p>Aha! The <code>2/3</code> is a special Clojure <code>Ratio</code> type. What can we do with a <code>Ratio</code>?</p>
<p>Determine whether a value is one:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">ratio?</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; true</span>

(<span class="hljs-name"><span class="hljs-builtin-name">ratio?</span></span> <span class="hljs-number">3</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; false</span>
</code></pre>
<p>Note that <code>rational?</code> is a little more inclusive:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">rational?</span></span> <span class="hljs-number">3</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; true</span>
</code></pre>
<p>Inspect its numerator and denominator:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">numerator</span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; 2</span>

(<span class="hljs-name">denominator</span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)
<span class="hljs-comment">;;=&gt; 3</span>
</code></pre>
<p>Do math operations without losing precision:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span>
 (<span class="hljs-name"><span class="hljs-builtin-name">*</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span> <span class="hljs-number">3</span>)
 (<span class="hljs-name"><span class="hljs-builtin-name">*</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>) <span class="hljs-number">3</span>))
<span class="hljs-comment">;;=&gt; ([2N clojure.lang.BigInt]</span>
<span class="hljs-comment">;;    [2.0000000596046448 java.lang.Double])</span>
</code></pre>
<p>That is pretty great!</p>
<p>You can attempt to turn a floating point number into a ratio. Beware!</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span>
 (<span class="hljs-name"><span class="hljs-builtin-name">rationalize</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)
 (<span class="hljs-name"><span class="hljs-builtin-name">rationalize</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">2</span>/<span class="hljs-number">3</span>)))
<span class="hljs-comment">;;=&gt; ([2/3 clojure.lang.Ratio]</span>
<span class="hljs-comment">;;    [416666679084301/625000000000000</span>
<span class="hljs-comment">;;     clojure.lang.Ratio])</span>
</code></pre>
<p>You may have guessed the following would fare better because our computers are binary, and you’d be right:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span>
 (<span class="hljs-name"><span class="hljs-builtin-name">rationalize</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">1</span>/<span class="hljs-number">2</span>))
 (<span class="hljs-name"><span class="hljs-builtin-name">rationalize</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">float</span></span> <span class="hljs-number">3</span>/<span class="hljs-number">2</span>)))
<span class="hljs-comment">;;=&gt; ([1/2 clojure.lang.Ratio]</span>
<span class="hljs-comment">;;    [3/2 clojure.lang.Ratio])</span>
</code></pre>
<p>However, for even <code>2/3</code>, <code>BigInt</code> can save the day!</p>
<pre><code class="lang-clojure">(<span class="hljs-name">value-and-type</span>
 (<span class="hljs-name"><span class="hljs-builtin-name">rationalize</span></span> (/ <span class="hljs-number">2</span>N <span class="hljs-number">3</span>N)))
<span class="hljs-comment">;;=&gt; ([2/3 clojure.lang.Ratio])</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Repeating Digits in Clojure]]></title><description><![CDATA[My son asked me if I could make the computer double numbers, like

2 4 6 → 22 44 66

I started with a really ugly solution:
(map
 (fn [x]
   (parse-long (clojure.string/join
                ""
                [(str x) (str x)])))
 [2 4 6])
;;=> (22 4...]]></description><link>https://brettrowberry.com/repeating-digits-in-clojure</link><guid isPermaLink="true">https://brettrowberry.com/repeating-digits-in-clojure</guid><category><![CDATA[concatentation]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[code golf]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Sat, 20 Sep 2025 03:48:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/gGFw3jVE31w/upload/abad06ff0b6e8748f9ff7b95a2fc45f2.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>My son asked me if I could make the computer double numbers, like</p>
<blockquote>
<p>2 4 6 → 22 44 66</p>
</blockquote>
<p>I started with a really ugly solution:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span>
 (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [x]
   (<span class="hljs-name">parse-long</span> (<span class="hljs-name">clojure.string/join</span>
                <span class="hljs-string">""</span>
                [(<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> x) (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> x)])))
 [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>])
<span class="hljs-comment">;;=&gt; (22 44 66)</span>
</code></pre>
<p>I wanted to see how ChatGPT could improve my answer. Here’s what it came up with:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span>
 #(<span class="hljs-name">parse-long</span> (<span class="hljs-name"><span class="hljs-builtin-name">format</span></span> <span class="hljs-string">"%d%d"</span> % %))
 [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>])
</code></pre>
<p>This is really elegant. I’ve used format strings extensively in C, C#, F#, Clojure, and a handful of other languages I don’t know very well, but I never thought about putting the same value in a format string more than once.</p>
<pre><code class="lang-clojure"><span class="hljs-comment">;; Of course</span>
(<span class="hljs-name">#</span>(<span class="hljs-name"><span class="hljs-builtin-name">format</span></span> <span class="hljs-string">"%d%d"</span> %<span class="hljs-number">1</span> %<span class="hljs-number">2</span>) <span class="hljs-number">2</span> <span class="hljs-number">2</span>)
<span class="hljs-comment">;;=&gt; "22"</span>

<span class="hljs-comment">;; Wow, never thought of it like this</span>
(<span class="hljs-name">#</span>(<span class="hljs-name"><span class="hljs-builtin-name">format</span></span> <span class="hljs-string">"%d%d"</span> %<span class="hljs-number">1</span> %<span class="hljs-number">1</span>) <span class="hljs-number">2</span>)
<span class="hljs-comment">;;=&gt; "22"</span>
</code></pre>
<p>Here’s a second solution ChatGPT came up with:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span>
 #(<span class="hljs-name">parse-long</span> (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> % %))
 [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>])
</code></pre>
<p>Bravo! I felt extra silly with my <code>clojure.string.join</code> after seeing this. I forgot <code>str</code> is multi-arity with a variadic arity. I also didn’t know it had a 0-arity. Here’s the actual implementation:</p>
<pre><code class="lang-clojure">(<span class="hljs-keyword">defn</span> <span class="hljs-title">str</span>
  <span class="hljs-string">"With no args, returns the empty string. With one arg x, returns
  x.toString().  (str nil) returns the empty string. With more than
  one arg, returns the concatenation of the str values of the args."</span>
  {<span class="hljs-symbol">:tag</span> String
   <span class="hljs-symbol">:added</span> <span class="hljs-string">"1.0"</span>
   <span class="hljs-symbol">:static</span> <span class="hljs-literal">true</span>}
  (<span class="hljs-comment">^String</span> [] <span class="hljs-string">""</span>)
  (<span class="hljs-comment">^String</span> [<span class="hljs-comment">^Object</span> x]
   (<span class="hljs-name"><span class="hljs-builtin-name">if</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">nil?</span></span> x) <span class="hljs-string">""</span> (<span class="hljs-name"><span class="hljs-builtin-name">.</span></span> x (<span class="hljs-name">toString</span>))))
  (<span class="hljs-comment">^String</span> [x &amp; ys]
     ((<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [<span class="hljs-comment">^StringBuilder</span> sb more]
          (<span class="hljs-name"><span class="hljs-builtin-name">if</span></span> more
            (<span class="hljs-name"><span class="hljs-builtin-name">recur</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">.</span></span> sb  (<span class="hljs-name">append</span> (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">first</span></span> more)))) (<span class="hljs-name"><span class="hljs-builtin-name">next</span></span> more))
            (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> sb)))
      (<span class="hljs-name"><span class="hljs-builtin-name">new</span></span> StringBuilder (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> x)) ys)))
</code></pre>
<p>Here’s a third solution from ChatGPT. I quite like it:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">for</span></span> [n [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>]]
  (<span class="hljs-name">parse-long</span> (<span class="hljs-name"><span class="hljs-builtin-name">str</span></span> n n)))
</code></pre>
<p>I almost never use <code>for</code>, but I should. It’s pretty amazing!</p>
<p>I didn’t want to be bested by the LLM, so i came up with another solution using <code>repeat</code>:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> #(<span class="hljs-name"><span class="hljs-builtin-name">apply</span></span> str (<span class="hljs-name"><span class="hljs-builtin-name">repeat</span></span> <span class="hljs-number">2</span> %)) [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>])
</code></pre>
<p>One benefit of this solution is the ability to parameterize the repetitions. For example, if I wanted to repeat the digit five times instead of twice:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [repetitions <span class="hljs-number">5</span>]
  (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span>
   #(<span class="hljs-name"><span class="hljs-builtin-name">apply</span></span> str (<span class="hljs-name"><span class="hljs-builtin-name">repeat</span></span> repetitions %))
   [<span class="hljs-number">2</span> <span class="hljs-number">4</span> <span class="hljs-number">6</span>]))
<span class="hljs-comment">;;=&gt; ("22222" "44444" "66666")</span>
</code></pre>
]]></content:encoded></item><item><title><![CDATA[Sliding Windows in Clojure]]></title><description><![CDATA[I was doing a little Clojure programming and my son asked me if the computer could

make it go 1 2 3, 2 3 4, 3 4 5, …

I thought, hmm, that’s a sliding window.
Here was my first thought:
(->> (range 1 4)
     (map (fn [x] [x (inc x) (inc (inc x))])))...]]></description><link>https://brettrowberry.com/sliding-windows-in-clojure</link><guid isPermaLink="true">https://brettrowberry.com/sliding-windows-in-clojure</guid><category><![CDATA[Sliding Windows]]></category><category><![CDATA[Clojure]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 18 Sep 2025 04:08:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/UQ470wQWx4o/upload/f223d446055ff91b735374ac8dca9d9c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was doing a little Clojure programming and my son asked me if the computer could</p>
<blockquote>
<p>make it go 1 2 3, 2 3 4, 3 4 5, …</p>
</blockquote>
<p>I thought, hmm, that’s a sliding window.</p>
<p>Here was my first thought:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">1</span> <span class="hljs-number">4</span>)
     (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [x] [x (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> x) (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> x))])))
<span class="hljs-comment">;;=&gt; ([1 2 3] [2 3 4] [3 4 5])</span>
</code></pre>
<p>I asked if he wanted it as just one list. He asked me to show him, so I thought… I’m mapping over this and need to concatenate the lists. Wait, that’s literally <code>mapcat</code>:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">1</span> <span class="hljs-number">4</span>)
     (<span class="hljs-name"><span class="hljs-builtin-name">mapcat</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">fn</span></span> [x] [x (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> x) (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> x))])))
<span class="hljs-comment">;;=&gt; (1 2 3 2 3 4 3 4 5)</span>
</code></pre>
<p>He said he preferred the little lists to the one big one. Good thinking.</p>
<p>Then I thought, can we make it shorter? I could use <code>iterate</code> instead of the vector literal:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">1</span> <span class="hljs-number">4</span>)
     (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> #(<span class="hljs-name"><span class="hljs-builtin-name">take</span></span> <span class="hljs-number">3</span> (<span class="hljs-name"><span class="hljs-builtin-name">iterate</span></span> inc %))))
<span class="hljs-comment">;;=&gt; ((1 2 3) (2 3 4) (3 4 5))</span>
</code></pre>
<p>Looking at this, it’s really easy to parameterize:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [window-size <span class="hljs-number">3</span>
      number-of-windows <span class="hljs-number">3</span>]
  (<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">1</span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> number-of-windows))
       (<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> #(<span class="hljs-name"><span class="hljs-builtin-name">take</span></span> window-size (<span class="hljs-name"><span class="hljs-builtin-name">iterate</span></span> inc %)))))
<span class="hljs-comment">;;=&gt; ((1 2 3) (2 3 4) (3 4 5))</span>
</code></pre>
<p>Then I thought, the first number in a little collection forms the basis for the next little collection. I also like this way best because it starts with a literal vector <code>[1 2 3]</code>:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> [<span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span>]
     (<span class="hljs-name"><span class="hljs-builtin-name">iterate</span></span> #(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> inc %))
     (<span class="hljs-name"><span class="hljs-builtin-name">take</span></span> <span class="hljs-number">3</span>))
<span class="hljs-comment">;;=&gt; ([1 2 3] (2 3 4) (3 4 5))</span>
</code></pre>
<p>It can be parameterized as well:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [number-of-windows <span class="hljs-number">3</span>]
  (<span class="hljs-name"><span class="hljs-builtin-name">-&gt;&gt;</span></span> [<span class="hljs-number">1</span> <span class="hljs-number">2</span> <span class="hljs-number">3</span>]
       (<span class="hljs-name"><span class="hljs-builtin-name">iterate</span></span> #(<span class="hljs-name"><span class="hljs-builtin-name">map</span></span> inc %))
       (<span class="hljs-name"><span class="hljs-builtin-name">take</span></span> number-of-windows)))
<span class="hljs-comment">;;=&gt; ([1 2 3] (2 3 4) (3 4 5))</span>
</code></pre>
<p>After doing all that, I realize that this isn’t a sliding window at all! In a true sliding window problem, you don’t know what the elements will be. THIS is a sliding window:</p>
<pre><code class="lang-clojure">(<span class="hljs-name"><span class="hljs-builtin-name">let</span></span> [window-size <span class="hljs-number">3</span>
      step-size <span class="hljs-number">1</span>]
  (<span class="hljs-name"><span class="hljs-builtin-name">partition</span></span> window-size step-size (<span class="hljs-name"><span class="hljs-builtin-name">range</span></span> <span class="hljs-number">1</span> (<span class="hljs-name"><span class="hljs-builtin-name">inc</span></span> <span class="hljs-number">5</span>))))
<span class="hljs-comment">;;=&gt; ((1 2 3) (2 3 4) (3 4 5))</span>
</code></pre>
<p>In any case, Clojure is awesome for this type of work.</p>
]]></content:encoded></item><item><title><![CDATA[VS Code: Simple Browser]]></title><description><![CDATA[Updated September 16, 2025 to indicate that Flares have a singleton mode as explained by PEZ.
I just learned about Calva Flares. Then I wondered, is there something built into VS Code for viewing webpages? There is! It’s called Simple Browser and was...]]></description><link>https://brettrowberry.com/vs-code-simple-browser</link><guid isPermaLink="true">https://brettrowberry.com/vs-code-simple-browser</guid><category><![CDATA[simple-browser]]></category><category><![CDATA[calva-flares]]></category><category><![CDATA[VS Code]]></category><category><![CDATA[webview]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[calva]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Fri, 12 Sep 2025 04:01:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/8pc4Z3XEBO8/upload/7f7d4ba92719784dabf3f2c08f1b2ede.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Updated September 16, 2025 to indicate that Flares have a singleton mode as explained by PEZ.</em></p>
<p>I just learned about <a target="_blank" href="https://brettrowberry.com/calva-flares?showSharer=true">Calva Flares</a>. Then I wondered, is there something built into VS Code for viewing webpages? There is! It’s called <code>Simple Browser</code> and <a target="_blank" href="https://github.com/microsoft/vscode/pull/109276">was added way back in 2021</a>!</p>
<p>For my use case of viewing the <a target="_blank" href="https://clojure.org/api/cheatsheet">Clojure Cheatsheet</a> without leaving VS Code, all I have to do is:</p>
<p>Open the <a target="_blank" href="https://code.visualstudio.com/api/ux-guidelines/command-palette">Command Palette</a> and select <code>Simple Browser: Show</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757649050892/d6cd0ee0-26f1-438e-9436-6abc7af6f8f3.png" alt class="image--center mx-auto" /></p>
<p>Type or paste in the URL</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757649066899/a8ec1345-e89d-4d37-93b4-31f204510cde.png" alt class="image--center mx-auto" /></p>
<p>And the page appears!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757649081774/ba2f4d2c-dc5b-4440-acc9-dcc9a3142847.png" alt class="image--center mx-auto" /></p>
<p>Simple Browser beats out Flares by having an address bar, back, forward, reload, and open in browser controls. But, Simple Browser is a singleton.</p>
<p>With Flares, I can open as many as I want, and I can have a singleton in the sidebar if I add the key-value pair <code>:sidebar-panel? true</code>.</p>
<p>It’s nice to have options!</p>
]]></content:encoded></item><item><title><![CDATA[Calva Flares]]></title><description><![CDATA[Updated September 16, 2025 after PEZ showed me how to use the sidebar panel.
I was playing with some Clojure code in Calva for Visual Studio Code and stumbled onto a feature. In my Primary Side Bar, I have the Calva view. I noticed the Inspector prev...]]></description><link>https://brettrowberry.com/calva-flares</link><guid isPermaLink="true">https://brettrowberry.com/calva-flares</guid><category><![CDATA[flares]]></category><category><![CDATA[VS Code]]></category><category><![CDATA[Clojure]]></category><category><![CDATA[calva]]></category><category><![CDATA[webview]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Fri, 12 Sep 2025 03:19:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/6KSbdozttrQ/upload/f5bb0e05ee927f1304926129f7f2707b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Updated September 16, 2025 after PEZ showed me how to use the sidebar panel.</em></p>
<p>I was playing with some <a target="_blank" href="https://clojure.org/">Clojure</a> code in <a target="_blank" href="https://calva.io/">Calva</a> for <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a> and stumbled onto a feature. In my <a target="_blank" href="https://code.visualstudio.com/docs/configure/custom-layout#_primary-side-bar">Primary Side Bar</a>, I have the Calva view. I noticed the <code>Inspector</code> previously, but it wasn’t until now that I noticed <code>Flare</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757645884729/cfbc3589-469b-408b-a598-8c96ed4c5a9f.png" alt class="image--center mx-auto" /></p>
<p>I decided to see what it is. Here are the official docs on <a target="_blank" href="https://calva.io/flares/">Flares</a>. One of the examples shows how to display a webpage. Here’s the code snippet shown:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">tagged-literal</span> 'flare/html {<span class="hljs-symbol">:url</span> <span class="hljs-string">"https://calva.io/"</span>,
                             <span class="hljs-symbol">:title</span> <span class="hljs-string">"Calva homepage"</span>
                             <span class="hljs-symbol">:key</span> <span class="hljs-string">"example"</span>})
</code></pre>
<p>Here’s my editor before evaluating that form:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757646534431/219e3e50-81bd-41a7-a8cb-2c163fc80cb9.png" alt class="image--center mx-auto" /></p>
<p>After evaluating, a new tab is opened with the Flare:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757646519648/36733a0b-c280-45a1-9718-f6df7732ceac.png" alt class="image--center mx-auto" /></p>
<p>Now how about that <code>Flare</code> under <code>Inspector</code> in the <a target="_blank" href="https://code.visualstudio.com/docs/configure/custom-layout#_primary-side-bar">Primary Sidebar</a>? Add the key-value pair</p>
<pre><code class="lang-clojure"><span class="hljs-symbol">:sidebar-panel?</span> <span class="hljs-literal">true</span>
</code></pre>
<pre><code class="lang-clojure">(<span class="hljs-name">tagged-literal</span> 'flare/html {<span class="hljs-symbol">:url</span> <span class="hljs-string">"https://calva.io/"</span>,
                             <span class="hljs-symbol">:title</span> <span class="hljs-string">"Calva homepage"</span>
                             <span class="hljs-symbol">:key</span> <span class="hljs-string">"example"</span>
                             <span class="hljs-symbol">:sidebar-panel?</span> <span class="hljs-literal">true</span>})
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1758077681801/27a29d3e-e9f6-4c23-bdf8-12ff993de913.png" alt class="image--center mx-auto" /></p>
<p>There it is, on the left!</p>
<p>This is awesome! I can open a webview in both the sidebar and in tabs so I don’t have to leave my editor.</p>
<p>Here’s an example that I find helpful:</p>
<pre><code class="lang-clojure">(<span class="hljs-name">tagged-literal</span> 'flare/html {<span class="hljs-symbol">:url</span> <span class="hljs-string">"https://clojure.org/api/cheatsheet"</span>,
                             <span class="hljs-symbol">:title</span> <span class="hljs-string">"Cheatsheet"</span>
                             <span class="hljs-symbol">:key</span> <span class="hljs-string">"cheatsheet"</span>})
</code></pre>
<p>Happy coding!</p>
]]></content:encoded></item><item><title><![CDATA[Sales Promises]]></title><description><![CDATA[Imagine you’re a salesperson for a boutique car manufacturer and you’re having a sales conversation with a new, important customer:
Customer: Thanks for seeing me. I’m really looking forward to one of your custom cars!Salesperson: Absolutely. You can...]]></description><link>https://brettrowberry.com/sales-promises</link><guid isPermaLink="true">https://brettrowberry.com/sales-promises</guid><category><![CDATA[sales]]></category><category><![CDATA[engineering]]></category><category><![CDATA[Estimation]]></category><category><![CDATA[negotiations]]></category><category><![CDATA[product]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Fri, 01 Aug 2025 21:37:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/vWchRczcQwM/upload/fb9b80f41ff5451803d22735df257429.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you’re a salesperson for a boutique car manufacturer and you’re having a sales conversation with a new, important customer:</p>
<p><strong>Customer</strong>: Thanks for seeing me. I’m really looking forward to one of your custom cars!<br /><strong>Salesperson</strong>: Absolutely. You can choose a 2-door or 4-door. It will be ready from the factory in 2 months.<br /><strong>Customer</strong>: Actually, I'd like a 6-door.</p>
<p>Let’s sketch out two possible responses:</p>
<ul>
<li><p>promise anything</p>
</li>
<li><p>promise what you can</p>
</li>
</ul>
<h2 id="heading-promise-everything">Promise Everything</h2>
<p><strong>Salesperson</strong>: Absolutely!<br /><strong>Customer</strong>: Great, when can I have it?<br /><strong>Salesperson</strong>: Our 2-door and 4-door cars have a lead time of 2 months. I’m sure we can do the same for a 6-door!</p>
<h2 id="heading-promise-what-you-can">Promise What You Can</h2>
<p><strong>Salesperson</strong>: This relationship is important to us, and your business is important to us. While we do offer a high level of customization, we’ve never offered a 6-door, so I can't say with certainty how long that would take. I can check with my team. I expect it would double the timeline. Alternatively, we could deliver you a 2-door and/or a 4-door in 2 months to meet your immediate needs. If the 6-door remains important, we could revisit that after delivery.<br /><strong>Customer</strong>: I chose you because I heard you offer more customization than your competitors.<br /><strong>Salesperson</strong>: We do. I also want you to understand the tradeoffs involved with this request: a 2-door and/or 4-door in 2 months, or a 6-door with a longer, less certain timeline.<br /><strong>Customer</strong>: I need a car in 3 months. I’ll take a 4-door and keep in touch.</p>
<h2 id="heading-closing">Closing</h2>
<p>I don’t work in sales, so can you really trust my advice? I can’t say. What I can say is that the promise everything route may lead to disappointed customers and will lead to unhappy people at your company.</p>
<p>Is this scenario too simplistic to be useful? Even if it is, there are some lessons you can apply:</p>
<ol>
<li><p>Know what you can reliably deliver in a given timeframe.</p>
</li>
<li><p>Know which changes to your offerings will make a timeframe uncertain.</p>
</li>
<li><p>Don’t estimate on behalf of others doing the work.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Understanding Stacks]]></title><description><![CDATA[In computer science, a stack is what you think it is. A pile. There are two important restrictions:

Only add to the top

Only remove from the top


To illustrate, let’s track how we get dressed using a stack. At first, the stack is empty. Let’s add ...]]></description><link>https://brettrowberry.com/understanding-stacks</link><guid isPermaLink="true">https://brettrowberry.com/understanding-stacks</guid><category><![CDATA[stack]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[data structure]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Sat, 26 Jul 2025 05:43:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/FO7bKvgETgQ/upload/6c2901576117d3b97a0df55a78ed1a30.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In computer science, a stack is what you think it is. A pile. There are two important restrictions:</p>
<ol>
<li><p>Only add to the top</p>
</li>
<li><p>Only remove from the top</p>
</li>
</ol>
<p>To illustrate, let’s track how we get dressed using a stack. At first, the stack is empty. Let’s add a <code>shirt</code>, a <code>sweater</code>, and a <code>coat</code>. Our stack now looks like this:</p>
<pre><code class="lang-mermaid">flowchart TD
coat ~~~ sweater ~~~ shirt
</code></pre>
<p>Now, we want to go swimming. In real life, and in a stack, we can’t remove the <code>shirt</code> first since the <code>sweater</code> and <code>coat</code> are in the way. The stack we built tells us the order in which to remove the clothes: <code>coat</code>, <code>sweater</code>, <code>shirt</code>.</p>
<p>The only thing we can access is the last thing we put in. Therefore, we say that a stack is last-in, first-out (LIFO). We could also call it first-in, last-out (FILO), but nobody says that.</p>
<p>You can read more at <a target="_blank" href="https://www.geeksforgeeks.org/dsa/applications-advantages-and-disadvantages-of-stack/">https://www.geeksforgeeks.org/dsa/applications-advantages-and-disadvantages-of-stack/</a>.</p>
]]></content:encoded></item><item><title><![CDATA[The Assignment Meeting Squeeze]]></title><description><![CDATA[Say you want someone to do a task for you: fill out a questionnaire, sign off on a document, etc. You can ask them, and they may get around to it.
Try this next time. Tell them what you need and set up a fallback meeting far enough in the future that...]]></description><link>https://brettrowberry.com/the-assignment-meeting-squeeze</link><guid isPermaLink="true">https://brettrowberry.com/the-assignment-meeting-squeeze</guid><category><![CDATA[power-move]]></category><category><![CDATA[task]]></category><category><![CDATA[assignment]]></category><category><![CDATA[meetings]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Fri, 25 Apr 2025 01:46:52 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/smgTvepind4/upload/bda969feee950d6ea6490cbf3d8166fa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Say you want someone to do a task for you: fill out a questionnaire, sign off on a document, etc. You can ask them, and they may get around to it.</p>
<p>Try this next time. Tell them what you need and set up a fallback meeting far enough in the future that they can reasonably complete the task. They may start working on it right away! If not, you’ll solve it in the meeting. Win-win.</p>
<p>It’s a real “power move”, like the one below. I am not the Brett from the video.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=ilSkmPPax6c">https://www.youtube.com/watch?v=ilSkmPPax6c</a></div>
]]></content:encoded></item><item><title><![CDATA[Date for a Date]]></title><description><![CDATA[Imagine you’re at work and someone asks you when a new project will be done. Perhaps it’s a request for an on the spot estimate. Further, imagine you have little to no idea.
I’d like to introduce you to a concept that I thought was entirely ridiculou...]]></description><link>https://brettrowberry.com/date-for-a-date</link><guid isPermaLink="true">https://brettrowberry.com/date-for-a-date</guid><category><![CDATA[due-date]]></category><category><![CDATA[date-for-date]]></category><category><![CDATA[d4d]]></category><category><![CDATA[manage-expectations]]></category><category><![CDATA[date]]></category><category><![CDATA[deadlines]]></category><category><![CDATA[deadline]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 24 Apr 2025 01:35:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/GjAKNK4ocRc/upload/d56685e1e024aa2d97e73bd35de70131.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you’re at work and someone asks you when a new project will be done. Perhaps it’s a request for an on the spot estimate. Further, imagine you have little to no idea.</p>
<p>I’d like to introduce you to a concept that I thought was entirely ridiculous but have since embraced: a date for a date.</p>
<p>A date for a date (D4D for short) is a date on which you’ll have an answer. You many not know how long a project will take, but you can probably say how long it will take you to come up with an estimate, and, consequently, a date. So, if today is April 23rd and you’re asked when a project will complete, instead of saying “I don’t know”, say, “I’ll get you a date on April 30th”.</p>
<p>Did I invent this term? No. I picked it up at work, and it’s <a target="_blank" href="https://ell.stackexchange.com/a/46136">fairly common in industry</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Hacking the Project Management Triangle]]></title><description><![CDATA[Audience
If you’re reading this, I assume you’re a software engineering manager. However, the overall content applies to all projects, and the hack applies to all projects in the context of a company.
What’s the Project Management Triangle?
For the p...]]></description><link>https://brettrowberry.com/hacking-the-project-management-triangle</link><guid isPermaLink="true">https://brettrowberry.com/hacking-the-project-management-triangle</guid><category><![CDATA[project management]]></category><category><![CDATA[Software Engineering Manager ]]></category><category><![CDATA[engineering-management]]></category><category><![CDATA[Scope]]></category><category><![CDATA[schedule]]></category><category><![CDATA[resources]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Wed, 23 Apr 2025 03:37:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/9Ili8POa3b0/upload/8f95ff639507e0dfbb65202b237f53d3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-audience">Audience</h1>
<p>If you’re reading this, I assume you’re a software engineering manager. However, the overall content applies to all projects, and the hack applies to all projects in the context of a company.</p>
<h1 id="heading-whats-the-project-management-triangle">What’s the Project Management Triangle?</h1>
<p>For the purposes of this post, the project management triangle is an illustration of the tension between <strong>scope</strong>, <strong>schedule</strong>, and <strong>resources</strong> when executing a project. Let’s isolate each of the aspects and explain what happens as they increase:</p>
<ul>
<li><p><strong>Scope</strong>: requires a longer <strong>schedule</strong> and/or <strong>resources</strong>.</p>
</li>
<li><p><strong>Schedule</strong>: can accommodate more <strong>scope</strong> and/or require fewer <strong>resources</strong>.</p>
</li>
<li><p><strong>Resources</strong>: the <strong>scope</strong> can be expanded and/or the <strong>schedule</strong> can be shortened.</p>
</li>
</ul>
<p>You can read more <a target="_blank" href="https://en.wikipedia.org/wiki/Project_management_triangle">here</a>. Said another way:</p>
<blockquote>
<p>Fast, good, and cheap. Choose two.</p>
</blockquote>
<h1 id="heading-what-do-software-engineering-managers-do">What Do Software Engineering Managers Do?</h1>
<p>As a software engineering manager, you’re likely tasked with delivering projects on time (<strong>schedule</strong>), at an acceptable cost (<strong>resources</strong>), with sufficient quality (<strong>scope</strong>). Your most significant cost is probably personnel.</p>
<h2 id="heading-too-many-projects">Too Many Projects</h2>
<p>Unfortunately, you may find your team having too many projects (or parts of a project) to work on at once. Imagine you want to work on fewer projects at a time. You should explain that the additional projects put other projects at risk due to the split attention. Sometimes, your boss or stakeholders won’t accept that. This is where the hack comes in.</p>
<h2 id="heading-the-hack">The Hack</h2>
<p>Ask for more people by explaining what the people you have are doing and what additional people will do. Now, you may not actually want more people, but you may have better success with this request. Why might this request land better? Instead of saying some work will have to wait, which they don’t want, you’re proposing a solution that is their responsibility to fulfill. Remember, this isn’t your plan A. Plan A was asking to defer some project(s). This is Plan B. This will likely have one of two outcomes.</p>
<h3 id="heading-outcome-1">Outcome 1</h3>
<p>This is the best case scenario: you don’t get more people. Now, your stakeholders clearly understand the capacity limitation. So, you come to an agreement to reduce the scope. Success!</p>
<h3 id="heading-outcome-2">Outcome 2</h3>
<p>This is the worst case scenario: you get more people. You should be accepting of this since managers are often meant to grow their area, not just run it.</p>
<h1 id="heading-summary">Summary</h1>
<p>Hopefully, your team is working on just the right amount of projects. If you have too many, ask to defer (overall <strong>scope</strong> reduction or <strong>schedule</strong> growth) to ensure better delivery of the most important ones. If that doesn’t work, try the hack: ask for more people! Good luck, engineering manager!</p>
]]></content:encoded></item><item><title><![CDATA[Prepending to Living Agenda Documents]]></title><description><![CDATA[Imagine you have a recurring meeting with an agenda open to contributions by all participants. Many meetings in the workplace are like this:

1:1s

Sprint demos

Team operational reviews


Perhaps the agenda is written in a Google Doc or a Confluence...]]></description><link>https://brettrowberry.com/prepending-to-living-agenda-documents</link><guid isPermaLink="true">https://brettrowberry.com/prepending-to-living-agenda-documents</guid><category><![CDATA[Documents]]></category><category><![CDATA[agenda]]></category><category><![CDATA[prepend]]></category><category><![CDATA[linked list]]></category><category><![CDATA[stack]]></category><category><![CDATA[array]]></category><category><![CDATA[resize array]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 02 Jan 2025 21:54:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4H9IuFBIpYM/upload/0b4c22721b9ef19b300fb2b1236d6e65.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine you have a recurring meeting with an agenda open to contributions by all participants. Many meetings in the workplace are like this:</p>
<ul>
<li><p>1:1s</p>
</li>
<li><p>Sprint demos</p>
</li>
<li><p>Team operational reviews</p>
</li>
</ul>
<p>Perhaps the agenda is written in a Google Doc or a Confluence page. Say have your first meeting on January 3rd. The document will look something like this:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># January 3rd</span>
<span class="hljs-bullet">-</span> Party cleanup (10 minutes)
<span class="hljs-bullet">-</span> Goal review (20 minutes)
</code></pre>
<p>Here’s the living agenda document for January 10th:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># January 3rd</span>
<span class="hljs-bullet">-</span> Party cleanup (10 minutes)
<span class="hljs-bullet">-</span> Goal review (20 minutes)

<span class="hljs-section"># January 10th</span>
<span class="hljs-bullet">-</span> Printer paper (5 minutes)
<span class="hljs-bullet">-</span> Lunch menu (15 minutes)
</code></pre>
<p>You may notice a pattern. Each meeting, the latest agenda gets lower on the page. By the fifth meeting or so, the scrolling becomes tedious.</p>
<p>Enter prepending! It looks like this:</p>
<pre><code class="lang-markdown"><span class="hljs-section"># January 10th</span>
<span class="hljs-bullet">-</span> Printer paper (5 minutes)
<span class="hljs-bullet">-</span> Lunch menu (15 minutes)

<span class="hljs-section"># January 3rd</span>
<span class="hljs-bullet">-</span> Party cleanup (10 minutes)
<span class="hljs-bullet">-</span> Goal review (20 minutes)
</code></pre>
<p>Enjoy!</p>
<p>For the computer scientists out there, note that the most common access patterns are:</p>
<ul>
<li><p>peeking the top item, and</p>
</li>
<li><p>pushing a new top item.</p>
</li>
</ul>
<p>That sounds like a stack! That’s why it’s better to prepend to the agenda. It’s more efficient to access the head of a linked list, O(1), than the tail, O(n). That’s not entirely true since you can hit <code>Command</code> + <code>Down</code> on Mac or <code>End</code> on Windows to jump to the bottom of a document in a browser, but not everyone knows that!</p>
]]></content:encoded></item><item><title><![CDATA[Hello, Production!]]></title><description><![CDATA[When coding locally, a "Hello, World!" program demonstrates a functioning development environment. Deploying a minimal app to production ("Hello, Production!") demonstrates that the program runs in its intended environment. To eliminate uncertainty w...]]></description><link>https://brettrowberry.com/hello-production</link><guid isPermaLink="true">https://brettrowberry.com/hello-production</guid><category><![CDATA[hello production]]></category><category><![CDATA[Hello World]]></category><category><![CDATA[production]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Devops]]></category><category><![CDATA[twelve factor app]]></category><category><![CDATA[silverbullet]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 02 Jan 2025 03:46:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/PyegXTiAWEY/upload/c05a7eed1a53061ef1c739b1834728d9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When coding locally, a "Hello, World!" program demonstrates a functioning development environment. Deploying a minimal app to production ("Hello, Production!") demonstrates that the program runs in its intended environment. To eliminate uncertainty when deploying a new application, embrace the idea of "Hello, Production!". The earlier you get to "Hello, Production!", in the development process, the higher the return on investment (ROI). That’s because you’ll complete some one-time tasks you need to do anyway, minimizing errors and toil. For a website, this might mean a white page with the words "Hello, World!".</p>
<p>Here are the elements I propose every "Hello, Production!" include:</p>
<ol>
<li><p>Remote Code Repository</p>
</li>
<li><p>Automated Builds</p>
</li>
<li><p>Automated Tests</p>
</li>
<li><p>Automated Deployment</p>
</li>
<li><p>Logging and Alerting</p>
</li>
</ol>
<h1 id="heading-remote-code-repository">Remote Code Repository</h1>
<p>Having a remote code repository, or version control, definitely helps in a team setting. However, it also helps individuals. Something could go wrong on your machine. People with cats or small children probably know best what I’m talking about. When that happens, you’ll be glad the code exists somewhere else! A remote code repository is also a prerequisite for the next two elements. As a bonus, it enables code review by others.</p>
<h1 id="heading-automated-builds">Automated Builds</h1>
<p>When your code builds on a remote machine, it increases your confidence that the build is repeatable. You might have a library installed on your local machine that isn’t specified by files in your repository. When that’s the case, remote automated builds show you the problem very quickly!</p>
<h1 id="heading-automated-tests">Automated Tests</h1>
<p>Automated tests run on a remote server protect your codebase from yourself (and others) when you forget to or simply don’t feel like manually running tests. On top of that, you know that your tests are reproducible. Imagine you have a local testing database with records you forgot to seed in your remote testing database. Automated tests that run on another machine surface that kind of issue right away. I find that automated testing rewards small batch sizes, because you want the tests to pass. The smaller the changes, the easier that is. In one of my favorite articles, <a target="_blank" href="https://blog.ploeh.dk/2019/07/01/yes-silver-bullet/#a3b19483cd6a4c509d8c3a77fe324872">Yes silver bullet</a>, Mark Seemann confirms the importance of automated testing.</p>
<h1 id="heading-automated-deployment">Automated Deployment</h1>
<p>The importance of automatically deploying software you run yourself (as opposed to creating binaries run by others) can’t be overstated because it plays such a significant role in increasing your deployment frequency by making every deployment less effort. See this article on <a target="_blank" href="https://cloud.google.com/blog/products/devops-sre/using-the-four-keys-to-measure-your-devops-performance">DORA Metrics</a>, or even better, read <a target="_blank" href="https://amzn.to/3VYdr6v">Accelerate</a>. It’s extremely valuable to find out early that the web server can’t connect to the database, or that CORS is misconfigured.</p>
<h1 id="heading-logging-and-alerting">Logging and Alerting</h1>
<p>Logging won’t come as a surprise to those familiar with the <a target="_blank" href="https://12factor.net">Twelve-Factor App</a>. The point of "Hello, Production!" is to get all of the setup done so there are no impediments to rapid iteration. Seeing evidence in logs that your changes worked builds your confidence. Perhaps you are adding authorization. It’s helpful to see approval and denial logs come through.</p>
<p>Having alerting set up early pays dividends. It’s best to resolve the cause of all known alerts before users arrive. It’s also good to know that alerts are making it to their intended destination before users cause alerts. One thing I’ve added to web servers is a "blow up" route to make it easy to prove that alerting is properly configured. Secure it properly so it doesn’t become a vector for a denial of service attack! You can even disable or remove it after it has served its purpose.</p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>Get these five elements in place before doing the rest of the development on an app:</p>
<ol>
<li><p>Remote Code Repository</p>
</li>
<li><p>Automated Builds</p>
</li>
<li><p>Automated Tests</p>
</li>
<li><p>Automated Deployment</p>
</li>
<li><p>Logging and Alerting</p>
</li>
</ol>
<p>By doing so, development becomes more efficient and pleasant. On your next app, don’t delay, get to "Hello, Production!" right away!</p>
]]></content:encoded></item><item><title><![CDATA[Peanut Butter Cookies]]></title><description><![CDATA[Verna Stott’s recipe, taken from an old Betty Crocker cookbook.
Cream together in a stand mixer:

1 cup soft shortening

1 cup peanut butter

1 cup sugar

1 cup light brown sugar

2 eggs


Stir in to the creamed mixture:

2 ½ cups sifted flour

1 tea...]]></description><link>https://brettrowberry.com/peanut-butter-cookies</link><guid isPermaLink="true">https://brettrowberry.com/peanut-butter-cookies</guid><category><![CDATA[cookies]]></category><category><![CDATA[peanut butter]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Sun, 29 Dec 2024 17:11:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1735492174619/e40010e4-5151-455b-bb11-8b22b84db73b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Verna Stott’s recipe, taken from an old Betty Crocker cookbook.</p>
<p>Cream together in a stand mixer:</p>
<ul>
<li><p>1 cup soft shortening</p>
</li>
<li><p>1 cup peanut butter</p>
</li>
<li><p>1 cup sugar</p>
</li>
<li><p>1 cup light brown sugar</p>
</li>
<li><p>2 eggs</p>
</li>
</ul>
<p>Stir in to the creamed mixture:</p>
<ul>
<li><p>2 ½ cups sifted flour</p>
</li>
<li><p>1 teaspoon baking powder</p>
</li>
<li><p>1 ½ teaspoons soda</p>
</li>
<li><p>½ teaspoons salt</p>
</li>
</ul>
<p>Bake 10-12 minutes at 350ºF</p>
]]></content:encoded></item><item><title><![CDATA[Adult Hot Chocolate]]></title><description><![CDATA[I wouldn’t say I’ve perfected this recipe, but I’m pretty happy with it. It beats hot chocolate mixes on nutrition. I think it wins on taste as well!
Ingredients

2 cups whole milk

1 tablespoon chia seeds (optional for thicker chocolate, soak overni...]]></description><link>https://brettrowberry.com/adult-hot-chocolate</link><guid isPermaLink="true">https://brettrowberry.com/adult-hot-chocolate</guid><category><![CDATA[cacao]]></category><category><![CDATA[hotcocoa]]></category><category><![CDATA[raw]]></category><category><![CDATA[cocoa]]></category><category><![CDATA[#chocolate]]></category><category><![CDATA[Beverage]]></category><category><![CDATA[hotchocolate]]></category><category><![CDATA[CARDAMOM]]></category><category><![CDATA[cinnamon]]></category><category><![CDATA[milk]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Thu, 05 Dec 2024 02:00:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/zKjidZxijiM/upload/e1b47b926f4ef06d3e7cab8c8bbbbd54.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I wouldn’t say I’ve perfected this recipe, but I’m pretty happy with it. It beats hot chocolate mixes on nutrition. I think it wins on taste as well!</p>
<h1 id="heading-ingredients">Ingredients</h1>
<ul>
<li><p>2 cups whole milk</p>
</li>
<li><p>1 tablespoon chia seeds (optional for thicker chocolate, soak overnight in the milk if possible)</p>
</li>
<li><p>½ cup <a target="_blank" href="https://amzn.to/3D38cf9">cocoa nibs</a></p>
</li>
<li><p>¼ cup <a target="_blank" href="https://amzn.to/4iloShV">brown sugar</a> (or ¼ cup jaggery, 4 dates, 3 tablespoons maple syrup)</p>
</li>
<li><p>1 teaspoon <a target="_blank" href="https://amzn.to/3BhppRy">vanilla extract</a></p>
</li>
<li><p>¼ teaspoon <a target="_blank" href="https://amzn.to/3OCbV5Y">cardamom powder</a></p>
</li>
<li><p>¼ teaspoon <a target="_blank" href="https://amzn.to/49lopZ0">Ceylon cinnamon powder</a></p>
</li>
<li><p>1/8 teaspoon <a target="_blank" href="https://amzn.to/3Bn5OPP">salt</a></p>
</li>
</ul>
<h2 id="heading-equipment">Equipment</h2>
<ul>
<li><p><a target="_blank" href="https://amzn.to/3ZdO3dK">Measuring cups &amp; spoons</a></p>
</li>
<li><p>A high-power blender with a soup setting, like this <a target="_blank" href="https://amzn.to/3Vklt9y">Blendtec</a> or <a target="_blank" href="https://amzn.to/3B3nRdV">Vitamix</a></p>
</li>
<li><p><a target="_blank" href="https://amzn.to/3VqGLSN">Mugs</a></p>
</li>
</ul>
<h2 id="heading-directions">Directions</h2>
<ol>
<li><p>Pour milk to the 2 cup line.</p>
</li>
<li><p>Add remaining ingredients.</p>
</li>
<li><p>Blend until smooth and hot. On my Blendtec, I run the soup setting twice.</p>
</li>
</ol>
<p>Serves 2.</p>
]]></content:encoded></item><item><title><![CDATA[Overcoming SQL Server Index Fragmentation]]></title><description><![CDATA[Licensing Woes
In one of my previous jobs, we managed many Oracle and SQL Server 🪟 databases. Licensing restrictions often forced us into making suboptimal choices. Here are two examples.
First, we had Oracle DB instances across the world that neede...]]></description><link>https://brettrowberry.com/sql-server-reorganize-index</link><guid isPermaLink="true">https://brettrowberry.com/sql-server-reorganize-index</guid><category><![CDATA[reorganize-index]]></category><category><![CDATA[rebuild-index]]></category><category><![CDATA[Databases]]></category><category><![CDATA[Relational Database]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[SQL]]></category><category><![CDATA[SQL Server]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[index]]></category><category><![CDATA[licensing]]></category><dc:creator><![CDATA[Brett Rowberry]]></dc:creator><pubDate>Sat, 03 Aug 2024 02:38:45 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/HQGYz4Eazwc/upload/a78a6409d9ffc8c2aac550bfb58a4418.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-licensing-woes">Licensing Woes</h1>
<p>In one of my previous jobs, we managed many Oracle and SQL Server 🪟 databases. Licensing restrictions often forced us into making suboptimal choices. Here are two examples.</p>
<p>First, we had Oracle DB instances across the world that needed to stay in sync in a variety of arrangements. We should have purchased Oracle's <a target="_blank" href="https://docs.oracle.com/goldengate/c1230/gg-winux/GGCON/introduction-oracle-goldengate.htm#GGCON-GUID-EF513E68-4237-4CB3-98B3-2E203A68CBD4">Golden Gate</a>, which is designed to "replicate, filter, and transform data from one database to another database". While it would have been exceedingly expensive, given our needs, it would have paid for itself by saving years of effort by DBAs and software engineers and would have yielded a better outcome.</p>
<p>Second, we had a library that ran inside of Oracle DB. We couldn't run an ephemeral Oracle DB instance locally or in CI to test against. So, the CI runner 🏃‍♂️ connected to a live staging DB server to build the library, load it into the DB, run tests, and copy back the built artifact.</p>
<h1 id="heading-dbs-at-my-current-company">DB's at My Current Company</h1>
<p>At my current company, every relational database is PostgreSQL, except for a single MySQL database, and a pair of SQL Server databases used by a licensed product we're phasing out. The SQL Server instances are arranged so that the secondary can replace the primary without any downtime, which is excellent for keeping availability high.</p>
<h1 id="heading-the-incident">The Incident</h1>
<p>One day, the primary database slowed to a crawl! 😱 This impacted business operations and had to be resolved quickly.</p>
<h1 id="heading-mitigation-and-diagnosis">Mitigation and Diagnosis</h1>
<p>We switched to the secondary, which seemed to have better performance. After some investigation, we determined that the table indexes were highly fragmented on both, and more pronounced on the primary. We don't have expert DBAs—just some application engineers and general-purpose IT/DevOps engineers.</p>
<p>We decided the causes were:</p>
<ol>
<li><p>The automated maintenance plans stopped working reliably.</p>
</li>
<li><p>This is a guess. After some work was done to encrypt the disks, during which the primary and secondary couldn't communicate for an extended period of time, the index fragmentation increased.</p>
</li>
</ol>
<p>We couldn't do maintenance on the original primary as it's only possible to connect to the active DB—you knew this was coming—due to of licensing restrictions. Sigh. I thought we would have to take a maintenance window one night to rebuild the indexes.</p>
<h1 id="heading-insight-and-resolution">Insight and Resolution</h1>
<p>A colleague asked ChatGPT what our options were. They then suggested <a target="_blank" href="https://learn.microsoft.com/en-us/sql/relational-databases/indexes/reorganize-and-rebuild-indexes?view=sql-server-ver16#reorganize-an-index">reorganizing</a> the indexes instead of rebuilding them. Reorganizing defragments the leaf level of clustered and non-clustered indexes, improving performance without requiring extensive resources. I tried it on the secondary and it noticeably improved the performance. Success! I switched back to the primary after hours, reorganized its indexes, and saw good improvements as well. 💪 Wahoo!</p>
<h1 id="heading-more-on-reorganizing">More on Reorganizing</h1>
<p>Reorganizing is lighter 🪶 on resources than rebuilding and can be done online. Rebuilding is heavier 🚜 on resources and can resolve fragmentation that reorganizing can't. In addition, rebuilding must be done offline ⛔️, unless, you guessed it - you have the right license 🪪!</p>
<h1 id="heading-just-use-postgresql">Just Use PostgreSQL</h1>
<p>This experience, coupled with ones prior, reinforced my belief that PostgreSQL 🐘 is the correct default choice when a relational database is required. PostgreSQL doesn't have to be the best at everything. The advantage it has over licensed DBs is that all the tools one would want are freely available. What about MySQL? I'll leave that as an exercise to the reader (hint: don't use it!).</p>
]]></content:encoded></item></channel></rss>