<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Hrakaroo</title>
    <link>https://hrakaroo.com/</link>
      <atom:link href="https://hrakaroo.com/index.xml" rel="self" type="application/rss+xml" />
    <description>Hrakaroo</description>
    <generator>Hugo Blox Builder (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Mon, 24 Oct 2022 00:00:00 +0000</lastBuildDate>
    <image>
      <url>https://hrakaroo.com/media/icon_hu_a40146739a0db508.png</url>
      <title>Hrakaroo</title>
      <link>https://hrakaroo.com/</link>
    </image>
    
    <item>
      <title>Example Talk</title>
      <link>https://hrakaroo.com/talk/example-talk/</link>
      <pubDate>Sat, 01 Jun 2030 13:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/talk/example-talk/</guid>
      <description>&lt;div class=&#34;alert alert-note&#34;&gt;
  &lt;div&gt;
    Click on the &lt;strong&gt;Slides&lt;/strong&gt; button above to view the built-in slides feature.
  &lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Slides can be added in a few ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create&lt;/strong&gt; slides using Hugo Blox Builder&amp;rsquo;s &lt;a href=&#34;https://docs.hugoblox.com/reference/content-types/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;&lt;em&gt;Slides&lt;/em&gt;&lt;/a&gt; feature and link using &lt;code&gt;slides&lt;/code&gt; parameter in the front matter of the talk file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Upload&lt;/strong&gt; an existing slide deck to &lt;code&gt;static/&lt;/code&gt; and link using &lt;code&gt;url_slides&lt;/code&gt; parameter in the front matter of the talk file&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embed&lt;/strong&gt; your slides (e.g. Google Slides) or presentation video on this page using &lt;a href=&#34;https://docs.hugoblox.com/reference/markdown/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;shortcodes&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Further event details, including &lt;a href=&#34;https://docs.hugoblox.com/reference/markdown/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;page elements&lt;/a&gt; such as image galleries, can be added to the body of this page.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Ray the Caster</title>
      <link>https://hrakaroo.com/project/ray-the-caster/</link>
      <pubDate>Mon, 24 Jun 2024 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/ray-the-caster/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Start with On-Call</title>
      <link>https://hrakaroo.com/post/start-with-oncall/</link>
      <pubDate>Mon, 22 Apr 2024 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/start-with-oncall/</guid>
      <description>&lt;p&gt;This is my second post on running a standup, you can read my first
post about walking the board &lt;a href=&#34;../walking-the-board/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&#34;start-with-on-call-a-healthier-way-to-begin-the-day&#34;&gt;Start with On-Call: A Healthier Way to Begin the Day&lt;/h1&gt;
&lt;p&gt;On-call work can be isolating, exhausting, and easy to overlook if
you&amp;rsquo;re not the one holding the pager. I’ve had nights where pages hit
back-to-back, leaving me exhausted and irritable the next
morning. When that happens, the idea of opening a standup with project
updates feels disconnected from reality. What I really want to talk
about is &lt;em&gt;why&lt;/em&gt; I was paged—and how the team can help make sure it
doesn’t happen again.&lt;/p&gt;
&lt;h2 id=&#34;rethinking-standup-begin-with-on-call&#34;&gt;Rethinking Standup: Begin with On-Call&lt;/h2&gt;
&lt;p&gt;Standup is usually the first meeting of the day and exists to align
the team. But before diving into project updates and task boards,
start with one question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Did the on-call engineer get paged overnight, and if so, why?&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;This isn’t a courtesy check-in—it’s a signal that the well-being of
the team matters, and that operational issues are real work. If the
overnight experience was rough, it might be appropriate to pause
project tasks and focus on the underlying problem.&lt;/p&gt;
&lt;h2 id=&#34;classify-every-page&#34;&gt;Classify Every Page&lt;/h2&gt;
&lt;p&gt;To make these conversations productive, each page should land in one
of three categories:&lt;/p&gt;
&lt;h3 id=&#34;1-known-issue-already-being-worked-on&#34;&gt;1. &lt;strong&gt;Known Issue Already Being Worked On&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;If the root cause is already under investigation—underprovisioned
services, a bug in progress—these pages can be deprioritized. Ideally,
you’d mute the alerts, but that risks hiding new issues. Just be
honest and precise: don’t group unrelated pages together.&lt;/p&gt;
&lt;h3 id=&#34;2-rare-one-off-events&#34;&gt;2. &lt;strong&gt;Rare, One-Off Events&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;A “rare” page should truly be rare. Over time, the definition should
stretch from once a month to once a quarter to once a year. If you&amp;rsquo;re
getting paged weekly, it’s not a one-off.&lt;/p&gt;
&lt;h3 id=&#34;3-new-uninvestigated-issues&#34;&gt;3. &lt;strong&gt;New, Uninvestigated Issues&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;Anything that isn’t known or rare belongs here. These deserve
investigation tickets. Failing to follow up leads to the same
interruptions night after night—and often much bigger problems down
the road.&lt;/p&gt;
&lt;h2 id=&#34;prevent-escalation-keep-small-problems-small&#34;&gt;Prevent Escalation: Keep Small Problems Small&lt;/h2&gt;
&lt;p&gt;Investigation work should be treated as a top priority for the on-call
engineer and ideally the secondary as well. Small problems are easy to
ignore when you’re busy, but they’re even easier to fix before they
escalate. As the backpacking adage goes: &lt;strong&gt;keep small problems
small.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&#34;protect-sleep-rotate-when-needed&#34;&gt;Protect Sleep: Rotate When Needed&lt;/h2&gt;
&lt;p&gt;Being paged at 2 AM isn’t just inconvenient—it’s damaging if it
happens repeatedly. If someone was paged between 9 PM and 9 AM,
consider swapping them with the secondary for the next
night. Guaranteeing a solid night’s rest can make all the
difference—and reinforces shared responsibility. In some cases,
you may want to also let the on-call person log off early to
recover.&lt;/p&gt;
&lt;h2 id=&#34;why-this-matters&#34;&gt;Why This Matters&lt;/h2&gt;
&lt;p&gt;In many teams, managers don’t participate in on-call rotations. When
page volume is high, that disconnect can breed resentment, especially
if nighttime interruptions feel invisible. Starting the day by
focusing on on-call sends a different message: the team’s experience
matters, and operational pain is a shared concern.&lt;/p&gt;
&lt;p&gt;A small shift—like opening standup with one intentional question—can
change how teams operate, collaborate, and care for each other. And
for the person who held the pager last night, that shift can mean
everything.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Glob Library for Go</title>
      <link>https://hrakaroo.com/project/glob-library-go/</link>
      <pubDate>Tue, 09 Jan 2024 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/glob-library-go/</guid>
      <description></description>
    </item>
    
    <item>
      <title>42 Inch Round Table in White Oak</title>
      <link>https://hrakaroo.com/post/white-oak-table/</link>
      <pubDate>Mon, 11 Dec 2023 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/white-oak-table/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;For at least the last 20 years we&amp;rsquo;ve had a 48 inch round dining room
table in our kitchen nook. Like most folks, we ended up using this as
our primary dining table and while it was great, it was always a bit
over sized for the space. Recently, while we were updating other parts
of our house, we decided to finally replace the table with a 42 inch
one that felt less bulky.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jokes aside, six inches may not seem like a lot but if you multiply it
out, a 48 inch table has a surface area of approximately 1809 squared
inches, while a 42 inch table has a surface area of approximately 1385
squared inches. That&amp;rsquo;s a difference of 414 squared inches, so almost
three full square feet smaller, which is considerable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We knew we wanted a solid hardwood table and we liked the look of white
oak. Most of the tables we found on-line were either super expensive
($3000+) and/or had straight or chunky legs which we didn&amp;rsquo;t love.
What we wanted was something closer to our outdoor table which has
legs that curve into a center ring and then out again.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./outsideTable.jpeg&#34; alt=&#34;Outside Table&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After a fair bit of research I decided to try tackling the table build
myself.&lt;/p&gt;
&lt;h2 id=&#34;design&#34;&gt;Design&lt;/h2&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./tableSketch.png&#34; alt=&#34;SketchUp&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Using SketchUp Maker (2017) I worked up some designs until I had
something I was happy with. I knew I wanted to curve the legs in, but
I wasn&amp;rsquo;t entirely sure how to do it. My initial idea (which you can
sort of see in the sketch up) was to build the legs by laminating
three thinner boards together. By overlapping them at the curve I
thought I could build a strong joint. I still think this is a good
design idea that would create a very strong curved leg, but in the end
I went with a more traditional joint and just used dowels.&lt;/p&gt;
&lt;h2 id=&#34;wood&#34;&gt;Wood&lt;/h2&gt;
&lt;p&gt;After making a cut list I went down to Crosscut Hardwoods and picked
up ~30 feet of 6/4 for the table top and ~15 feet of 8/4 for the legs.
The total cost for the wood was around $400.&lt;/p&gt;
&lt;p&gt;Once I had the wood home I let it acclimate in the house for about a
week before starting to mill it down.  While I do have a planer I
don&amp;rsquo;t have a jointer so I had to use a sled for the planer to get one
side flat and a simple jig for my table saw to get a straight edge.&lt;/p&gt;
&lt;h2 id=&#34;table-top&#34;&gt;Table Top&lt;/h2&gt;
&lt;p&gt;After finding an arrangement I liked for the top I glued the boards
together and let them dry overnight.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mistake #1&lt;/p&gt;
&lt;p&gt;I opted to not use any dowels or biscuits as they don&amp;rsquo;t really do much
for adding strength. Not using anything for alignment proved to be a
mistake as I didn&amp;rsquo;t get the boards perfectly flush and after it dried
I had to take a hand plane and resurface both the top and bottom.
Next time I&amp;rsquo;m probably going to put at least a couple of dowels in to
assist with alignment&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./glueUp.jpeg&#34; alt=&#34;Glue Up&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Once the top was dry I used a circle cutting jig and a spiral bit on
my plunge router to cut the circle. I only cut it 3/4 of the way down
before flipping the top over and using a flush trim bit on my trim
router to finish the circle.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mistake #2&lt;/p&gt;
&lt;p&gt;I wasn&amp;rsquo;t entirely sure how far to plunge cut before flipping the table
over. Several videos I watched on this seemed to suggest you only
needed to make one or two passes with the router before flipping the
table over. Thankfully I took it down further as my trim router was
barely able to cut through what I had left. In the future it&amp;rsquo;s
probably safer to take this down to 1/4 inch before flipping.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./routedTop.jpeg&#34; alt=&#34;Routed Top&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;legs&#34;&gt;Legs&lt;/h2&gt;
&lt;p&gt;For the legs I first used cardboard to try out several different
variations until I found something I liked. Although I had my
sketchup design it helps to actually see it in person. Plus, once I
had my cardboard template I was able to work backwards to figure out
how big my leg blanks needed to be.&lt;/p&gt;
&lt;p&gt;To do this I took my cardboard template and cut it right where I
was picturing my joint to be. Then I was able to flip over the top
piece and tape it back together which gave me an approximate template
for how big I needed to cut each leg blank. Since I kept top and
bottom leg angles the same, the resulting template was now perfectly
straight and the taped cut line gave me the exact angle I needed.&lt;/p&gt;
&lt;p&gt;Lining up the dowels for the joints was a process but everything came
together pretty well in the end.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./leg1.jpeg&#34; alt=&#34;Leg 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I did have some concerns that the dowel joints might not be strong
enough as the pressure from the table to would basically be trying
to pry them apart. To counter act this I made sure that when I
jointed in my cross pieces that I used four dowels which really
locked in this joint.&lt;/p&gt;
&lt;p&gt;(And yes, I used more glue than what is shown.  I had forgotten to
take a picture, so I stopped in the middle of my glue up to snap this
one.)&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mistake #3&lt;/p&gt;
&lt;p&gt;Clamping the legs proved more difficult than I anticipated.  I first
tried the trick of gluing cauls to some blue tape, but those popped
off pretty quickly when I started to tighten the clamps.  Thankfully I
had created some angle extensions that I was able to clamp to the
individual pieces and then add an additional clamp. If I had to do it
over again I would plan this part out better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./leg2.jpeg&#34; alt=&#34;Leg 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Once set I built a simple jig so that I could make parallel cuts in
the top and bottom of the leg, as well as create the flat inner cut.
Using this I was able to get all four legs nearly identical.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./leg3.jpeg&#34; alt=&#34;Leg 3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Next I made a router template from a piece of MDF I had lying around
and used it to cut the inner edge of each leg.  I did the same process
of only cutting part of the way through from the top, then removing
the template and flipping the piece over to cut the rest with the
flush trim bit.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./leg4.jpeg&#34; alt=&#34;Leg 4&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Finally I cut the center joining pieces and created a lap joint for
them to fit together.  Then I glued up each set of legs.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./leg5.jpeg&#34; alt=&#34;Leg 5&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;sanding&#34;&gt;Sanding&lt;/h2&gt;
&lt;p&gt;Then came the sanding.  All in all I probably spent 2 full days
sanding everything.  I started with 80 grit sandpaper and took it all
the way up to 220 and used a wand light to make sure there were no
imperfections.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mistake #4&lt;/p&gt;
&lt;p&gt;The finish I was planning on using was Rubio Monocoat which actually
does not recommend going past 150 grit, so I then made one more pass
to bring it back down to 150.&lt;/p&gt;
&lt;p&gt;Related, although I like Rubio Monocoat, it is wildly expensive.  On
my next project I&amp;rsquo;m likely going to try to use Natura Onecoat as it
reportedly works similar to Rubio and is about half the cost.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./sanding1.jpeg&#34; alt=&#34;Sanding&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mistake #5&lt;/p&gt;
&lt;p&gt;After testing out several different samples I ended up going with Pure
which, in retrospect, was probably a bit of a mistake. I still really
like how the table looks, but its a little too honey colored for my
tastes. I think something with a small percentage of white would have
offset the honey color, but its too late now and overall I&amp;rsquo;m pretty
happy with the way it turned out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./table2.jpeg&#34; alt=&#34;Table 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;This project took about two months to complete. I was mostly working
on it during the weekends and some evenings. I did allow myself to
buy a planer for this project and I needed to buy a couple of router
bits as well. So all included (wood, planer, router bits, Rubio
Monocoat, &amp;hellip;) I probably spent about $1200 in total on this project.
And while this is a lot, it&amp;rsquo;s still a far cry from the $3000 we were
seeing on-line and I&amp;rsquo;m pretty proud to have built this myself.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Summiting Mt. Hood 2022</title>
      <link>https://hrakaroo.com/post/summiting-hood-2022/</link>
      <pubDate>Sun, 08 May 2022 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/summiting-hood-2022/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;In March 2022 I was accepted into the &lt;a href=&#34;https://mazamas.org/BCEP/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Mazamas
BCEP&lt;/a&gt; program which taught basic
rock and snow climbing skills. This was a fantastic experience and I
highly recommend the program if you are in the Northwest and are at
all interested in hiking and climbing. Once the program completed I
signed up to join the Mt. Hood Climb scheduled for May 8, 2022
(mothers day), which was eventually pushed back to the following
Wednesday (May 11th) due to weather conditions. There were a total of
nine people (including the climb leaders) in the climbing group.&lt;/p&gt;
&lt;h2 id=&#34;the-day-before&#34;&gt;The day before&lt;/h2&gt;
&lt;p&gt;The day before the climb I drove up to Timberline Lodge and parked in
the lower parking lot that allows climbers to camp/park overnight. In
the weeks before the climb I had built a flat &amp;ldquo;bed&amp;rdquo; in the back of my
Honda Pilot so I could put a sleeping pad down and stretch out.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./HondaPilot.jpg&#34; alt=&#34;honda pilot&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&amp;rsquo;s a &lt;em&gt;really&lt;/em&gt; good idea to get to the parking lot the night before
and start to acclimate to the altitude. Timberline lodge is at around
6,000 ft of elevation and the top of Mt. Hood is 11,249 so the more
time you have to acclimate the better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Six of the nine people from our group opted to show up the night
before with three people driving up the morning of the climb.
Initially we were planning to start our climb at 1am the next morning,
but due to temperatures being lower than expected we were able to push
our start time back to 2pm.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As the temperature heats up it can cause the ice and snow to soften
creating dangerous climbing conditions as rocks and ice are more
likely to break away and fall on you.  Additionally, warmer
temperatures weaken the structure of snow bridges over crevasses which
increases risk.  Ideally you want to climb when everything is frozen
solid and stable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So after rechecking my pack several times I finally went to sleep
around 8pm.&lt;/p&gt;
&lt;h2 id=&#34;the-day-of&#34;&gt;The day of&lt;/h2&gt;
&lt;h3 id=&#34;getting-ready&#34;&gt;Getting Ready&lt;/h3&gt;
&lt;p&gt;For a start time of 2am I should ideally have woken up at 1am to begin
getting ready, but due to nerves I awoke up closer to 12am and was
unable to fall back asleep. So at 12:30am I gave up trying and started
getting ready for the climb. The day before I had prepared oatmeal in
a thermos with hot water which made for nice warm breakfast. After
breakfast I started to get my gear on and mentally prepare for the
climb.&lt;/p&gt;
&lt;p&gt;In addition to everything in my pack, I was also carrying one of the
climbing ropes we were bringing in case we needed it going over the
bergschrund. This added around 15 lbs to my pack and was a bit awkward
to carry as you have to sling it over your pack. (I was also carring
the fuel for the teams stove in my pack as we were required to bring
it, but this only added a nominal amount of weight.)&lt;/p&gt;
&lt;h3 id=&#34;starting-the-climb&#34;&gt;Starting the climb&lt;/h3&gt;
&lt;p&gt;Just before 2am we started off. The night was amazingly clear but
&lt;em&gt;exceedingly cold&lt;/em&gt;. As we were hiking up we could see the headlights
from other climbers further up the mountain who had left earlier.&lt;/p&gt;
&lt;p&gt;The first part of the climb takes you to the top of the Palmer Lift
and the route you take is along the &amp;ldquo;road&amp;rdquo; the snowcats create when
coming back after grooming the ski slope. So the snow is pretty
compacted and you can climb it easily in just your mountaineering
boots and without crampons. (Crampons are great, but they are heavy,
increase the likelihood of tripping, and generally slow you down.)&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./HoodNight.jpg&#34; alt=&#34;hood1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;silcox-hut&#34;&gt;Silcox Hut&lt;/h3&gt;
&lt;p&gt;At approximately 3am we reached Silcox Hut and two things had already
happened.  First, two people decided to drop out of the climb as they
were not feeling well.  (It should be noted that both of these
people were from the group that decided to drive up the morning of the
climb so they did not have the extra six plus hours to acclimate to
the altitude.)&lt;/p&gt;
&lt;p&gt;Secondly, my water was starting to freeze. I had made the mistake of
using a bladder for my water with an attached hose for drinking. This
was the same setup I had used on multiple hikes and ski trips so I
thought that as long as, after taking a drink, I blew air into my hose
I could keep it mostly thawed. The problem was that the nozzle had
collected water and was mostly frozen solid. After a lot of work I
managed to get it somewhat unclogged and had, what would turn out to
be, my last sip of water until after I was off the summit. In
retrospect this was a &lt;em&gt;huge&lt;/em&gt; and possibly dangerous mistake on my part
and I should have carried backup water in my pack.&lt;/p&gt;
&lt;p&gt;Somewhere between Silcox Hut and the top of the Palmer lift we started
to get whiffs of the fumaroles on Mt. Hood which smells strong of
sulfur.&lt;/p&gt;
&lt;h3 id=&#34;top-of-palmer&#34;&gt;Top of Palmer&lt;/h3&gt;
&lt;p&gt;At 4:40am we reached the top of the Palmer lift. The sun was starting
to come up a bit but it was still &lt;em&gt;really&lt;/em&gt; cold.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Palmer1.jpg&#34; alt=&#34;Palmer 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;For the climb up to the Hogsback (and summit beyond) we would be in
deep snow and no longer on a groomed trail so we needed our crampons,
helmets and ice axes.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Palmer2.jpg&#34; alt=&#34;Palmer 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I was also able to switch off carrying the climbing rope to another
climber so for the rest of the time my pack was 15 lbs lighter which
made a considerable difference.&lt;/p&gt;
&lt;p&gt;We had brought snowshoes for the climb, but after looking at the steps
already dug for us by the previous climbers, we decided to stash our
snowshoes near Palmer and go on without them.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./AbovePalmer1.jpg&#34; alt=&#34;Above Palmer 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./AbovePalmer2.jpg&#34; alt=&#34;Above Palmer 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;You can see the shadow of the mountain on the landscape below.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./AbovePalmer3.jpg&#34; alt=&#34;Above Palmer 3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Hogsback1.jpg&#34; alt=&#34;Climb to Hogsback1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Hogsback2.jpg&#34; alt=&#34;Climb to Hogsback2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Hogsback3.jpg&#34; alt=&#34;Hogsback1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;hogsback&#34;&gt;Hogsback&lt;/h3&gt;
&lt;p&gt;Once we reached the Hogsback we were able to see the different routes
to the summit.  The Pearly Gates looked safe enough and the
bergschrund was sufficiently covered that we felt comfortable climbing
to the summit without ropes. This allowed us to stash our ropes and
harnesses which removed a considerable amount of weight from our
packs.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Pearly1.jpg&#34; alt=&#34;Path to Pearly Gates1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;fumaroles&#34;&gt;Fumaroles&lt;/h3&gt;
&lt;p&gt;At the Hogsback you can also see the fumaroles to either side of the
path to the Pearly Gates. It&amp;rsquo;s important to watch your step near them
because while they are warm, it&amp;rsquo;s really the lack of oxygen that will
kill you before the heat will. That said, they are rather stunning in
the snow.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Fumaroles1.jpg&#34; alt=&#34;Fumaroles 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Fumaroles2.jpg&#34; alt=&#34;Fumaroles 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;pearly-gates&#34;&gt;Pearly Gates&lt;/h3&gt;
&lt;p&gt;We were forced to wait a bit at Hogsback as there were a lot of people
trying to get through the Pearly Gates and the section right before
the Gates themselves is the bergschrund, which you want to pass over
as quickly as possible.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Pearly2.jpg&#34; alt=&#34;Path to Pearly Gates1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Pearly3.jpg&#34; alt=&#34;Path to Pearly Gates1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Going through the Pearly Gates was one of the more surreal experiences
of the entire climb. First, the angle is rather steep so a lot of the
climb is on your hands and feet. The climbers before us had already
cut steps into the snow so it isn&amp;rsquo;t particularly hard, but it is slow
going and some areas are only wide enough for two people (one going up
and one going down) so you are forced to go as slow as the person
ahead of you is going. Secondly, the natural ice sculptures that have
been created from the snow and wind are stunningly beautiful.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Pearly4.jpg&#34; alt=&#34;Path to Pearly Gates1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Pearly5.jpg&#34; alt=&#34;Path to Pearly Gates1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Of course, the entire time I was climbing up through the Pearly Gates
I had it in the back of my mind that I would shortly be having to
climb down this as well.&lt;/p&gt;
&lt;h3 id=&#34;the-summit&#34;&gt;The Summit&lt;/h3&gt;
&lt;p&gt;Once you pass through the Pearly Gates the top of the mountain
actually levels off a bit and the summit is mostly flat with only
about a 10-15% incline.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit1.jpg&#34; alt=&#34;Summit 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit2.jpg&#34; alt=&#34;Summit 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit3.jpg&#34; alt=&#34;Summit 3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit4.jpg&#34; alt=&#34;Summit 4&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The view from the top is nothing short of breathtaking.  The sun was
out and we could see for miles.  It was, however, very cold and we
still had to down climb through the Pearly Gates.&lt;/p&gt;
&lt;h3 id=&#34;the-climb-down&#34;&gt;The climb down&lt;/h3&gt;
&lt;p&gt;While climbing up through the Pearly Gates you can plan out where you
are going to step, but the way back down requires a lot more trust.
This is where having spikes on the front of your crampons and
mountaineering boots really pays off. For most steps I would kick my
foot into the snow a couple of times until I had created a slight
indentation and then use the spikes to hold the majority of my weight.
My mountaineering boots had a full shank so as long as my toes were
secure I could stand up securely with the majority of my foot
suspended by just my toes.&lt;/p&gt;
&lt;p&gt;Initially this was hard for me to relax into, but after a while I
really started to trust my equipment and the climb down became a lot
easier.&lt;/p&gt;
&lt;h3 id=&#34;plunge-stepping-down-to-palmer&#34;&gt;Plunge stepping down to Palmer&lt;/h3&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Down.jpg&#34; alt=&#34;Down&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Once we were back at the Hogsback my water tube finally thawed
enough that I was able to get a drink. After that we collected the
ropes and harnesses we had stashed and headed down to Palmer.&lt;/p&gt;
&lt;p&gt;We started out on the trail we had taken getting to the Hogsback, but
quickly drifted off and were mostly plunge stepping down to Palmer.
Some of the folks in our group tried glissading down, but the snow was
pretty deep which made for slow going.&lt;/p&gt;
&lt;h3 id=&#34;glissading&#34;&gt;Glissading&lt;/h3&gt;
&lt;p&gt;At Palmer we collected the snow shoes we had stashed and got back onto
the groomed snowcat trail. We also quickly discovered that while
glissading in the deep snow was difficult, glissading on the groomed
snowcat trail was really easy. I had brought with me some medium gauge
plastic that worked okay as a sled as I was able to glissade for
probably 75% of the way from Palmer back to Timberline.&lt;/p&gt;
&lt;h3 id=&#34;back-at-the-car&#34;&gt;Back at the car&lt;/h3&gt;
&lt;p&gt;Finally back at the car we packed up our gear and drove into Welches to
have lunch.  After lunch I went into the parking lot, got into my sleeping
bag and took a four hour nap before finally driving home.&lt;/p&gt;
&lt;h2 id=&#34;gear&#34;&gt;Gear&lt;/h2&gt;
&lt;p&gt;What follows is a full listing of all the gear (to the best of my
memory) that I had with me on the hike. With the exception of my water
situation and some slight changes for my feet and hands, the rest of
my gear really held up well and I don&amp;rsquo;t think I would change much, if
anything, for the next time.&lt;/p&gt;
&lt;h3 id=&#34;feet&#34;&gt;Feet&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Hiking socks&lt;/li&gt;
&lt;li&gt;Scapra Charmoz HD mountaineering boots&lt;/li&gt;
&lt;li&gt;Hikenture leg gaiters&lt;/li&gt;
&lt;li&gt;Grivel crampons&lt;/li&gt;
&lt;li&gt;Snowshoes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I also have some Sealskinz socks which I&amp;rsquo;ll likely be wearing next
time and I&amp;rsquo;m experimenting around with a couple different brands of
heated socks. The Sealskinz, in addition to being waterproof which is
a nice just-in-case feature, really trap in the heat, so these
with the heated socks should be pretty nice.&lt;/p&gt;
&lt;p&gt;The snowshoes I borrowed from a friend, but since this trip I have
purchased my own pair from REI using their 20% off coupon. Not long
after I purchased mine I noticed that Costco was also selling
snowshoes as a seasonal item at the start of winter so that might have
been another option, but I don&amp;rsquo;t know how heavy they are.&lt;/p&gt;
&lt;h3 id=&#34;pants&#34;&gt;Pants&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Zero Degree base layer leggings&lt;/li&gt;
&lt;li&gt;Kuhl Snow/Hiking Pants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Kuhl are fantastic. They are rugged enough for hiking in,
insulated and priced reasonably.&lt;/p&gt;
&lt;h3 id=&#34;torso&#34;&gt;Torso&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Zero Degree long sleeve base layer&lt;/li&gt;
&lt;li&gt;Quick dry t-shirt&lt;/li&gt;
&lt;li&gt;Mountain Hardware Gore-Tex Infinium thin hooded coat&lt;/li&gt;
&lt;li&gt;32 Degree Heat down jacket&lt;/li&gt;
&lt;li&gt;Columbia hooded rain coat&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The down jacket was a cheap one I bought off of Amazon for like $40
for camping and the Mountain Hardware and Columbia jackets I purchased
at the Columbia employee store for a discount.&lt;/p&gt;
&lt;h3 id=&#34;head&#34;&gt;Head&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Balaclava&lt;/li&gt;
&lt;li&gt;Thin beanie hat&lt;/li&gt;
&lt;li&gt;Mountaineering Helmet&lt;/li&gt;
&lt;li&gt;Headlamp&lt;/li&gt;
&lt;li&gt;Glacier glasses&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before leaving I loosened my helmet so that I was sure I could wear it
easily over the balaclava and beanie. The balaclava was actually
something I received for free from a local go kart race track. It was
cold enough that the condensation that collected on my balaclava from
breathing through it froze as well.&lt;/p&gt;
&lt;p&gt;The beanie hat is an Omni-Heat one from Columbia so it has gold dots
on the inside which really do a great job of reflecting the heat back.&lt;/p&gt;
&lt;h3 id=&#34;hands&#34;&gt;Hands&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Glove liners&lt;/li&gt;
&lt;li&gt;Three finger gloves&lt;/li&gt;
&lt;li&gt;Garmin Fenix 7&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Mittens would have been ideal, but in order to try to keep some
dexterity I opted for three finger gloves which have a dedicated
finger for your thumb and index finger, and then a mitten like pouch
for your remaining three fingers.  This caused my index finger to get
really cold so next time I&amp;rsquo;m either going to invest in heated gloves,
or get some mittens. If I do get mittens I&amp;rsquo;ll probably use some of the
chemical heating packets which I can stuff down in the outer glove.&lt;/p&gt;
&lt;p&gt;The Garmin is useful as it has an altimeter so you can figure out how
close you are to different milestones.  It also has the ability to
record your entire climb and has a full map which you can use in a pinch
if you get lost.&lt;/p&gt;
&lt;h3 id=&#34;backpack&#34;&gt;Backpack&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ten essential items&lt;/li&gt;
&lt;li&gt;Rock climbing harness&lt;/li&gt;
&lt;li&gt;2 Nalgene 1L water (next time)&lt;/li&gt;
&lt;li&gt;Thick hat (for when I wasn&amp;rsquo;t wearing my helmet)&lt;/li&gt;
&lt;li&gt;Blue bag (just in case)&lt;/li&gt;
&lt;li&gt;Extra glove liners&lt;/li&gt;
&lt;li&gt;Extra glove shell (five fingers)&lt;/li&gt;
&lt;li&gt;Small waffle sitting pad&lt;/li&gt;
&lt;li&gt;Cell phone&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My backpack is a Mountain Hardware 25L Scrambler I purchased from the
Columbia employee store. In addition to being a perfect size it also
has loops on the outside for two ice axes and I was able to easily
attach my hiking poles and snow shoes.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve listed two 1L Nalgene bottles which, of course, I didn&amp;rsquo;t have on
my trip but really really wished I did. It&amp;rsquo;s important that you store
the bottle upside down in your pack so when it starts to freeze it
doesn&amp;rsquo;t seal the bottle shut. Instead I had my water bladder (which I
didn&amp;rsquo;t list) but which was such a bad idea I&amp;rsquo;m leaving it off my list.&lt;/p&gt;
&lt;p&gt;For the beginning part of the climb I had my crampons in my backpack
which worked out well, but wrapping them so they don&amp;rsquo;t rip your
backpack is a bit of a trick. I ended up using a mailing envelope but
I might look to see if I can get/make a leather pouch for next time or
see if I can somehow attach them to the outside of my backpack.&lt;/p&gt;
&lt;h3 id=&#34;misc&#34;&gt;Misc&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Ice axe&lt;/li&gt;
&lt;li&gt;Hiking poles&lt;/li&gt;
&lt;li&gt;Snacks and energy cubes&lt;/li&gt;
&lt;li&gt;Glissading sled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Although I was only carrying one ice axe, I really think two would
have been a better idea and wouldn&amp;rsquo;t have added much weight.&lt;/p&gt;
&lt;p&gt;For the glissading sled I had made mine out of some shelf liner
plastic from Home Depot but it really didn&amp;rsquo;t hold up well. While
riding down one corner folded in on itself and I kept having to stop
to fix things and by the time I was finally down it was mostly
ruined. Since this trip I&amp;rsquo;ve gone to Tap Plastics and purchased a
sheet of thicker, but still flexible, plastic and have constructed a
better sled.&lt;/p&gt;
&lt;h2 id=&#34;closing-thoughts&#34;&gt;Closing thoughts&lt;/h2&gt;
&lt;h3 id=&#34;1010-would-do-it-again&#34;&gt;10/10 would do it again&lt;/h3&gt;
&lt;p&gt;I had a fantastic time overall, the weather was near perfect and the
climbing was excellent. The ice sculptures near the summit are
unworldly and something that you really need to see in person to
believe.  It was also amazing standing at the highest point in all of
Oregon and being able to see for miles around you.&lt;/p&gt;
&lt;h3 id=&#34;the-cold-was-no-joke&#34;&gt;The cold was no joke&lt;/h3&gt;
&lt;p&gt;I think overall I underestimated how cold it was going to get. In
addition to my water tube freezing and leaving me without water for
the majority of the climb, my fingers and toes also suffered.  I
suspect that I have Raynaud&amp;rsquo;s disease which causes my body to abandon
my fingers and toes pretty quickly when I get cold.  Although I was
wearing gloves and glove liners, my fingers still became very cold and
numb.  Plus, putting on my crampons, opening my backpack and even just
getting my snack bag opened required me to take off at least my outer
glove layer which didn&amp;rsquo;t help. I also started to lose feeling in my
toes around Palmer and continued to lose more feeling in my feet for
the rest of the climb.&lt;/p&gt;
&lt;p&gt;Normally once I warm up the feeling comes back into my fingers and
toes pretty quickly, but not this time. For at least the first week
after the climb (and some of the next) I still had loss of feeling in
my toes, thumbs and forefingers.  This was unnerving and at the time I
wasn&amp;rsquo;t quite sure if I would ever regain feeling.  Eventually, slowly,
the feeling did come back and now I&amp;rsquo;m back to normal, but it&amp;rsquo;s not
something I really want to repeat.&lt;/p&gt;
&lt;p&gt;For next time I have purchased some battery powered socks and mittens
that I&amp;rsquo;m hoping will keep me warmer.  Plus, I&amp;rsquo;m going to be taking some
of those chemical hand warmers with me as well.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Ice.jpg&#34; alt=&#34;Ice&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>In Defense of Obscure Names</title>
      <link>https://hrakaroo.com/post/obscure-names/</link>
      <pubDate>Sun, 07 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/obscure-names/</guid>
      <description>&lt;h4 id=&#34;in-defense-of-obscure-names&#34;&gt;In defense of obscure names&lt;/h4&gt;
&lt;h2 id=&#34;naming-things-is-hard&#34;&gt;Naming things is hard&lt;/h2&gt;
&lt;p&gt;As the joke goes&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache
invalidation and naming things.  &amp;ndash; Phil Karlton&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;and while this is funny (perhaps only to those in Computer Science)
underlying the quote is a real issue, naming things is really hard.&lt;/p&gt;
&lt;p&gt;The challenge is not just in giving something an appropriate name for
right now, but in trying to predict how it may evolve and be used in
the future.&lt;/p&gt;
&lt;p&gt;Maybe you start with a database of bicycles, but as powered bicycles
are introduced you start adding those but from there it&amp;rsquo;s a slippery
slope to electric motorcycles and then gas powered motorcycles. Pretty
soon your database of bicycles includes all motorcycles and perhaps
those three wheeled things I&amp;rsquo;ve seen driving on the freeway. Unless
you had amazing forethought and were able to predict exactly how the
industry was going to evolve, chances are whichever name you select is
going to be incorrect at some point in the future.&lt;/p&gt;
&lt;h2 id=&#34;renaming-isnt-always-easy&#34;&gt;Renaming isn&amp;rsquo;t always easy&lt;/h2&gt;
&lt;p&gt;So, if the scope does evolve and the original name is no longer
correct we should just rename it, right? Sometimes this is the
answer. Tools for software development have come a long way and doing
a code refactoring isn&amp;rsquo;t nearly as hard as it once was. So if we have
a variable name, function name, or even a class/object name that is no
longer accurate we usually have the ability to safely rename it across
all dependencies to a new and more appropriate name.&lt;/p&gt;
&lt;p&gt;But the coding level is often where this capability ends. The industry
has not yet developed the capability to reliability refactor service
names across a distributed environment and trying to do it manually is
time consuming and often fraught with errors that can negatively
impact uptime.&lt;/p&gt;
&lt;h2 id=&#34;in-defense-of-the-obscure&#34;&gt;In defense of the obscure&lt;/h2&gt;
&lt;p&gt;So if changing the name of something is difficult and if naming things
correctly &lt;em&gt;now&lt;/em&gt; is hard, (and predicting how they may evolve is nearly
impossible,) then maybe the best thing we can do is to purposely give
them an obscure name.&lt;/p&gt;
&lt;p&gt;When I was in college we were able to select our own email
address. Like most people I knew, I opted for the straightforward and
used my last name for my email address: &lt;code&gt;gerth@lclark.edu&lt;/code&gt;. However,
one of my classmates opted for something entirely different, she
selected the email address of &lt;code&gt;squidlips@lclark.edu&lt;/code&gt;. At first I
thought she was just trying to rebel, but her reasoning was actually
pretty interesting. Her name was something rather common, (think Jane
Doe) and her argument was that while &lt;code&gt;doe@lclark.edu&lt;/code&gt; runs the risk of
being mistaken for someone else, &lt;code&gt;squidlips@lclark.edu&lt;/code&gt; is almost
guaranteed not to.&lt;/p&gt;
&lt;p&gt;This proved to be more than just an academic observation for as it
happened, I was not the only Gerth at Lewis &amp;amp; Clark College. There was
one other one, my father, a tenured professor, and I would
occasionally receive email intended for him. People had seen
&lt;code&gt;gerth@lclark.edu&lt;/code&gt; and had incorrectly assumed I was my father, and
were sending me information clearly not intended for the
students. (Nothing salacious I&amp;rsquo;m sad to say).&lt;/p&gt;
&lt;p&gt;In this case the name (my email address) was even 100% correct and it
was still getting used incorrectly. And while while no one may have
guessed who Squid Lips was, she never got any email not intended for
her.&lt;/p&gt;
&lt;h2 id=&#34;obscure-is-better-than-wrong&#34;&gt;Obscure is better than wrong&lt;/h2&gt;
&lt;p&gt;We make snap decisions all the time and if I was looking to get a list
of motorcycles and came across a service named &lt;code&gt;bicycles&lt;/code&gt; I would
immediately cross it off my list. But, if instead the service was
named &lt;code&gt;x34a5a&lt;/code&gt; or even &lt;code&gt;horsetail&lt;/code&gt; &lt;em&gt;I would at least have to consider
it&lt;/em&gt;. I may first go looking for something more specific, but at least I
wouldn&amp;rsquo;t cross it off my list.&lt;/p&gt;
&lt;p&gt;(I also prefer the name &lt;code&gt;horsetail&lt;/code&gt; over an obscure id designation as
it&amp;rsquo;s much easier to remember &lt;code&gt;horsetail&lt;/code&gt; and distinguish it from
&lt;code&gt;pigfoot&lt;/code&gt; than trying to remember &lt;code&gt;a20b23&lt;/code&gt; and distinguish it from
&lt;code&gt;c23s40&lt;/code&gt;.)&lt;/p&gt;
&lt;p&gt;So if a thing is going to exist for a while, is likely to evolve in
scope, and changing it&amp;rsquo;s name is non trivial, then giving it an
obscure name may actually be better and ultimately easier than trying
to come up with the perfect name. So in some cases, naming things may
not be all that hard after all.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Avoiding Read/Write Locks</title>
      <link>https://hrakaroo.com/post/rw-locks/</link>
      <pubDate>Sat, 06 Nov 2021 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/rw-locks/</guid>
      <description>&lt;p&gt;Intuitively, read/write locks seem to be a ideal solution. They
prevent the read/write and write/write race conditions while making
sure multiple read routines don&amp;rsquo;t block one another. So as long as
the write operations are optimized to be as fast as possible, the
overall throughput &lt;em&gt;should&lt;/em&gt; be high.&lt;/p&gt;
&lt;p&gt;In most cases this is true, but there is a subtlety to read/write
locks that can end up causing them to be considerably more expensive
than they initially appear.&lt;/p&gt;
&lt;p&gt;Consider the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thread 1 requests read access to a shared read/write lock.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;At this point in time no one else has requested the lock for either
read or write access so a read lock is granted to Thread 1.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thread 2 requests read access to the same read/write lock.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Since the lock is currently only read locked by Thread 1, Thread 2
is also given read access.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thread 3 requests write access to the same read/write lock.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Since the lock has been locked for read access by both Thread 1 and
Thread 2, Thread 3 is put on a wait list for write access until
Thread 1 and Thread 2 release their read locks.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Thread 4 requests read access to the same read/write lock.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;What happens here?&lt;/p&gt;
&lt;p&gt;A first guess might be to give Thread 4 the read lock, along with
Threads 1 and 2, since Thread 3 has not yet obtained the write lock,
but lets consider the fall out if we let this happen. Imagine a system
where every read operation took a minute to execute and requests are
comming in every 30 seconds. If every read request is granted a read
lock so long as no write thread had been given a write lock, new read
requests would always come in before previous ones finished ensuring
that the lock was never free (ie not read locked.) In this situation
the write operation would be blocked from every achieving write
access. aka, lock starvation.&lt;/p&gt;
&lt;p&gt;In order to prevent lock starvation it is necessary that as soon as a
write lock is requested every subsequent read lock request is blocked
from obtaining their lock until the thread requesting the write lock
has been given the write lock, executes, and then releases their lock.&lt;/p&gt;
&lt;p&gt;This means that as soon as a write lock is requested any read threads
currently running will block all new read requests. So a
read/write lock is, worse case, as &amp;ldquo;expensive&amp;rdquo; as the time it takes to
execute the write operation plus the worse case time it takes to
execute a read operation.&lt;/p&gt;
&lt;p&gt;Therefore, even if you optimize your system so the write operations
are as fast as possible, your overall throughput may still be
dependent on your slowest running read operation.&lt;/p&gt;
&lt;h2 id=&#34;what-readwrite-locks-are-good-for&#34;&gt;What read/write locks are good for.&lt;/h2&gt;
&lt;p&gt;Read/Write are useful when you need a memory barrier and &lt;em&gt;every&lt;/em&gt;
thread on your service should only see the state of some memory either
before or after a write operation, but never but never both. Meaning
that it is not allowable for some threads to see the value of the
memory before the write operations and other threads running at the
same time to see the memory value after the write operation.&lt;/p&gt;
&lt;p&gt;But, as we saw above, this comes at a cost and in &lt;em&gt;many&lt;/em&gt; cases it may
not be needed.&lt;/p&gt;
&lt;p&gt;For example, suppose we have a data structure that holds the account
balance for an individual. Suppose also that this account receives a
constant stream of read requests that, for what ever reason, take
several minutes to calculate. If we were to use a traditional
read/write lock here then every time there is a deposit or withdrawal
(a write operation) all read operations will be blocked until all
running read operations finish and the write executes.&lt;/p&gt;
&lt;p&gt;Although the write operation may be quick, the long execution time on
the read operation is going to cause the system to appear blocked
while it waits for the write lock. But if we can tolerate concurrent
threads seeing two different values then we can remove the block all
together and achieve higher overall throughput.&lt;/p&gt;
&lt;p&gt;To achieve this we first need to make the data structure immutable.
Updates would instead be handled by the write thread copying the
immutable data structure locally, modifying it (or modify on copy) and
then using an atomic operation to swap in the new data structure.&lt;/p&gt;
&lt;p&gt;Read operations running against the old data structure would continue
to run and would report the old value, while any new read operation
would run over the new data structure and report the new value.
Eventually all old read operations would finish and the only view
would be of the new data structure, but for a time it would be
possible for different threads to report different values.&lt;/p&gt;
&lt;p&gt;Destroying the old data structure can be handled either automatically by
letting the garbage collector pick it up, or we can add read counters to
objects which can tell us when an object is no longer being accessed and
we can delete it manually.&lt;/p&gt;
&lt;p&gt;We could also re-introduce a simple lock for just the writer
operations to ensure that multiple write operations always happen in
sequence so they can never overlap and they would never cause
inconsistent results.&lt;/p&gt;
&lt;p&gt;So while read/write locks often appear, at first blush, like a
panacea, they can actually carry a substantial hidden cost. And in
some cases the functionality provided by a read/write lock may not
even be what you really need.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Creating a workshop in my garage</title>
      <link>https://hrakaroo.com/post/garage-wall/</link>
      <pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/garage-wall/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve always wanted a small woodshop.&lt;/p&gt;
&lt;p&gt;With both of my parents in theatre I spent a lot of my childhood in
the shop they used for building sets.  When we bought our first house
I purchased a couple of woodworking tools, but my work area was in the
garage which was shared with our cars.  This made it hard to work on
bigger projects and sawdust was always getting all over everything.&lt;/p&gt;
&lt;p&gt;Our second house had a three car garage which meant I finally
had a dedicated space, but sawdust control was still an issue.  Plus,
it rains a lot where I live so dampness control was also an issue,
especially as we went in and out of the garage.&lt;/p&gt;
&lt;p&gt;When the pandemic hit I suddenly found myself in desperate need of a
project and decided to see if I could construct a wall between the
main garage and the third bay to create a small, dedicated woodshop.&lt;/p&gt;
&lt;p&gt;This is the before picture.  Since the third bay was not isolated it
often became just a dumping ground for things.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage1.jpeg&#34; alt=&#34;Garage 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;My first step was to take all the sheetrock off the post.  Once I did
that I was pretty much committed to the project.  I knew I wanted
to use the post to divide the wall in half, structurally at least, and
I figured it would be better to work on the smaller half first, even though
it was going to have a door in it which, at the time, I had no idea
how to actually do.&lt;/p&gt;
&lt;p&gt;But first, I had to put in the header and footer boards to attach the
vertical 2x4s to.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage2.jpeg&#34; alt=&#34;Garage 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;In order to nail into the concrete I bought a hammer drill to drill
anchor holes.  This worked, but for the other half of the wall I
borrowed a powder actuated nail gun from a friend of mine, which
basically uses a 22 cartridge to shoot a nail into the concrete.  Very
loud, but much faster.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage3.jpeg&#34; alt=&#34;Garage 3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage4.jpeg&#34; alt=&#34;Garage 4&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage5.jpeg&#34; alt=&#34;Garage 5&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Once the header and footer were attached I started putting in the vertical
2x4s. I read up a lot on how to build a door frame with a king stud, jack
stud and header plate.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage6.jpeg&#34; alt=&#34;Garage 6&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Satisfied with the framing I went on to do the other side, using a
laser level to make sure the wall was straight.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage7.jpeg&#34; alt=&#34;Garage 7&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;For this side I wasn&amp;rsquo;t as lucky with the ceiling beams and they were
parallel with the wall instead of perpendicular, as they had been for
the first section.  This meant that I had to climb up in the attic
space and attach cross beams first before I could then attach the
header boards.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage8.jpeg&#34; alt=&#34;Garage 8&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage9.jpeg&#34; alt=&#34;Garage 9&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage10.jpeg&#34; alt=&#34;Garage 10&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage11.jpeg&#34; alt=&#34;Garage 11&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;insulation&#34;&gt;Insulation&lt;/h2&gt;
&lt;p&gt;Initially I was not going to add insulation as I hate working with it.
If you don&amp;rsquo;t cover up enough you can get thousands of tiny glass
slivers that seem to take weeks to work their way out.  But in the end
I decided to add it, mostly because I was going to be running some
power tools and wanted the extra sound insulation.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage13.jpeg&#34; alt=&#34;Garage 13&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I also added in some 20amp outlets and ran all the wiring, but ended
up hiring an electrician to actually hook it into my electrical
panel.  I&amp;rsquo;ve added breakers to my panel before, but the whole thing
scares the hell out of me and is not something I wanted to do again.&lt;/p&gt;
&lt;p&gt;I also installed a 240v 50amp circuit on the garage side for a future
electric car plug in, and a 240v 30amp circuit on the shop side for my
table saw.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage14.jpeg&#34; alt=&#34;Garage 14&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;sheetrock&#34;&gt;Sheetrock&lt;/h2&gt;
&lt;p&gt;I debated about installing the sheetrock myself or hiring out.
Although I&amp;rsquo;ve done some small patch repairs, I haven&amp;rsquo;t actually done a
full install before so this could be an opportunity to learn.  That
said, sheetrock is heavy and although I probably could have rigged up
something to help me install it, it is much faster as a two person
job.  So in the end I just hired out for this part as well.  We had
temporary access to a truck so I was able to pick up the sheets myself
and then two pros came in and got the whole thing installed and mudded
in less than a week.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage15.jpeg&#34; alt=&#34;Garage 15&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage16.jpeg&#34; alt=&#34;Garage 16&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage18.jpeg&#34; alt=&#34;Garage 18&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage19.jpeg&#34; alt=&#34;Garage 19&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;painting&#34;&gt;Painting&lt;/h2&gt;
&lt;p&gt;I did decide to paint it myself so I started with primer and was
surprised at how much a new wall soaks in the primer.  I went through
&lt;em&gt;way&lt;/em&gt; more than I was expecting.&lt;/p&gt;
&lt;p&gt;I also opted to &lt;em&gt;not&lt;/em&gt; add any texture to the wall as most of the walls
in the garage were already pretty flat and I would have had to hire
out for the texture as well as I&amp;rsquo;ve heard it&amp;rsquo;s a trick to get right
the first time.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage20.jpeg&#34; alt=&#34;Garage 20&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;door&#34;&gt;Door&lt;/h2&gt;
&lt;p&gt;I had never hung a door before, but it turned out easier than I was
expecting.  In the three years since I installed this I still haven&amp;rsquo;t
had any issues with expansion or jamming.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage21.jpeg&#34; alt=&#34;Garage 21&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;moulding&#34;&gt;Moulding&lt;/h2&gt;
&lt;p&gt;Next I attached &lt;em&gt;most&lt;/em&gt; of the moulding.  After going around the
door I got busy with other projects and still, as of this writing,
have not entirely finished the moulding.  Seeing as how we are now
modernizing the moulding in our house I may just wait and replace
this with the new moulding as well.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage22.jpeg&#34; alt=&#34;Garage 22&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;lights&#34;&gt;Lights&lt;/h2&gt;
&lt;p&gt;Adding lights was the last step.  There was already a plug in the
ceiling where a garage door opener could plug in, so I bought some
led strip lights and plugged them in with a remote on/off switch.
(Initially I was thinking I might actually wire up a switch, but
in the end this was easier and cheaper.)&lt;/p&gt;
&lt;p&gt;The led lights are fantastic and really do a great job of lighting up
the woodshop.  I have since pinned the wires to the ceiling so they
are no longer dangling down like you see here.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./garage23.jpeg&#34; alt=&#34;Garage 23&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;final-thoughts&#34;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;This was a fantastic project which I both super enjoyed working on,
and also left me with a very usable space for a woodshop.  I still
have a lot of work to do on organizing and building out the actual
shop, but having a dedicated space has been a dream come true.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Summiting Mount St. Helens in 3&#43;1 Days</title>
      <link>https://hrakaroo.com/post/summiting-st-helens-2019/</link>
      <pubDate>Wed, 01 Apr 2020 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/summiting-st-helens-2019/</guid>
      <description>&lt;h2 id=&#34;background&#34;&gt;Background&lt;/h2&gt;
&lt;p&gt;In July 2019 my friend Tobin and I decided to hike the full Loowit
loop trail around Mount St. Helens. Both of us are experienced hikers
and in reasonable shape and figured we could probably complete about
10-14 miles a day. The total distance around the mountain is about
35 miles, so we figured it should take us approximately 3 days to
hike. We also managed to secure two passes to climb to the top of
St. Helens for Aug 2nd which extended our trip by one day.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In order to climb to the top of Mount St. Helens you need a permit,
which can be difficult to obtain depending on when you want to climb.
The summer permits often sell out very quickly so initially we assumed
we would not be able to climb to the top. Even so, we signed up on a
reseller web site and about a week before we were set to climb we were
contacted by someone who had two extra passes as some people in their
party backed out at the last moment. Further, the person lived in
Seattle and was only a short distance from Tobin, so two days before
we were set to leave Tobin drove to their house and bought the two
passes from the seller. The guy was really nice and didn&amp;rsquo;t mark up
the cost and just charged us the $20 or so per pass that he had paid.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;the-trip&#34;&gt;The trip&lt;/h2&gt;
&lt;h3 id=&#34;where-to-start&#34;&gt;Where to start&lt;/h3&gt;
&lt;p&gt;The first challenge was figuring out where to enter the Loowit trail
and which direction to hike (clockwise or counter clockwise).  Since
there is only one trail up to the summit and our passes were for Aug
2nd we had a hard requirement of being near Monitor Ridge on the night
of Aug 1st. Initially we thought we would enter at Sheep Canyon
trailhead and hike clockwise on the trail.  This would put us around
Windy Pass on the first night and close to June Lake on the second
night.  (In retrospect this was probably not a great plan as Windy
Pass isn&amp;rsquo;t named ironically.  It is windy as hell and camping there
would have been a trick.)  However, the &lt;em&gt;night before&lt;/em&gt; we were
planning on leaving we realized the trailhead we were going to use had
been washed out and we had to scramble to come up with a new plan.&lt;/p&gt;
&lt;p&gt;The new plan was to park at the Windy Ridge trailhead and hike counter
clockwise around the mountain.  Tobin is from Seattle and I&amp;rsquo;m from
Portland so we met at Spiffy&amp;rsquo;s at I-5 and Highway 12, stashed his car
in a gravel parking near the 76 gas station and drove my car to the
trailhead.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Spiffy&amp;rsquo;s is a bit of a dive restaurant off I-5 but there were not a
lot of other places around and we knew we wanted to get some take away
sandwiches.  I showed up first, walked in and started looking at the
menu.  While I was deciding, an older guy with a &amp;ldquo;get off my lawn&amp;rdquo;
look to him walked in with a small lap dog in his arms.  The hostess
informed him that the dog would need to wait outside which caused him
to get really upset and he demanded/yelled that it was a &amp;ldquo;therapy
dog.&amp;rdquo;  The hostess wasn&amp;rsquo;t having any of it and told him that he needed
a permit or some sort of license if it was a therapy dog. At that
point the guy yelled at the hostess &amp;ldquo;go fuck yourself you fucking
whore&amp;rdquo; and stormed out.  That was also the exact moment at which Tobin
walked into the restaurant and only heard the insult as the guy stored
out.  We looked at each other and both cracked up laughing as it was
such a bizarre way to start the trip.  To her credit, the hostess
handled it really well and the sandwiches were pretty good.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The trail head is at the end of NF-99, super easy to find and there is
plenty of parking and a flush toilet bathroom. There are also some
interesting informational signs that talk about the eruption and slow
recovery of the area. We were a bit eager to get started so we didn&amp;rsquo;t
linger too long. We ate the sandwiches we had picked up at Spiffy,
sunscreened up and headed off.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Trailhead.jpg&#34; alt=&#34;trailhead&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Our packs weighed around 24lbs and since Mount St. Helens is well
known for not having much water each of us was also carrying close
to three liters.&lt;/p&gt;
&lt;h2 id=&#34;day-1---122-miles&#34;&gt;Day 1 - 12.2 miles&lt;/h2&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./day1.png&#34; alt=&#34;day1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;starting&#34;&gt;Starting&lt;/h3&gt;
&lt;p&gt;For the first 1.7 miles the hike is along a winding gravel road. It&amp;rsquo;s
blocked off from cars and well maintained, albeit not terribly
interesting.  At the end of the gravel road it&amp;rsquo;s still another 1.2
miles until you actually connect to the Loowit trail.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./TobinJoshua.jpg&#34; alt=&#34;Tobin and Joshua&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;As we were hiking counter clockwise around the mountain we headed to
the right and started on the Loowit trail.  At this point the scenery
looks sort of like rolling hills with the mountain in the distance.
You can clearly see where the &amp;ldquo;blow out&amp;rdquo; was from the eruption but the
walking is pretty easy.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Starting.jpg&#34; alt=&#34;starting&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;loowit-falls&#34;&gt;Loowit Falls&lt;/h3&gt;
&lt;p&gt;Around a mile into the trail there is a turn out to go see Loowit
Falls.  It&amp;rsquo;s about 0.6 miles off the tail but well worth checking out.
Although you can &lt;em&gt;see&lt;/em&gt; the falls, you can&amp;rsquo;t actually get near it and
even getting down to the water is a challenge.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./LoowitFalls.jpg&#34; alt=&#34;loowit falls&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We did meet up with a forest ranger just before we got to the turn off
for the falls and chatted with him a bit.  We asked the ranger if the
waterfall was worth the extra hike (as we had a long ways that day).
His answer was something like &amp;ldquo;yeah, it&amp;rsquo;s pretty good&amp;rdquo; which we didn&amp;rsquo;t
find very convincing.  I think we even said, in a joking way, &amp;ldquo;you are
not exactly selling it here&amp;rdquo; to which he then responded very excitedly
&amp;ldquo;it&amp;rsquo;s a 200 ft waterfall made of glacier melt coming out an active
Volcano!&amp;rdquo;  He was awesome and convinced us to go and waterfall was
amazing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once we were back on the main trail it was another 0.8 miles until
Willow Springs.  Near Willow Springs there was actually water running
across the path and if we had needed water this would have been a
great place to fill up.  It was also the last time we would find water
until we stopped for camp that night.&lt;/p&gt;
&lt;h3 id=&#34;restricted-zone&#34;&gt;Restricted Zone&lt;/h3&gt;
&lt;p&gt;After Willow Springs the scenery gets significantly more barren and
rocky and the mountain looks blown out.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;Blast&#34; srcset=&#34;
               /post/summiting-st-helens-2019/BlastZone_hu_e3ee5246ddadb2c5.webp 400w,
               /post/summiting-st-helens-2019/BlastZone_hu_1e3b976756a579c5.webp 760w,
               /post/summiting-st-helens-2019/BlastZone_hu_f5520f27c4d45c61.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/BlastZone_hu_e3ee5246ddadb2c5.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;This is also the recovery area so no camping is allowed and really, it
would have been hard to find a good spot to camp if we had
wanted. This goes on for 5.4 miles, but the trail is pretty easy to
see and mostly flat.  In a couple of places we did drift slightly off
the trail but thankfully others have erected rock piles that make
getting back on the trail pretty easy.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Restricted.jpg&#34; alt=&#34;restricted&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;castle-ridge&#34;&gt;Castle Ridge&lt;/h3&gt;
&lt;p&gt;As you get close to Castle Ridge the scenery starts to change
significantly.  The trail gets close to the ridge itself in sections
and it&amp;rsquo;s a steep drop down.  Tobin also recognized Huckleberries so we
stopped at several big patches before continuing on.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./CastleRidge.jpg&#34; alt=&#34;castle ridge&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;From Castle Ridge it&amp;rsquo;s 1.4 miles down to the Toutle River.  The start
of this section is a switch back on a sandy incline that could be
challenging to walk at time.  At times it could take a fair bit of
concentration but it wasn&amp;rsquo;t impossible.  (I would not advise either
this section or the ridge for smaller children.)  After the sandy
incline you enter the forest and continue your switchbacks down.  It
was here that Tobin discovered wild Salmonberries within reach of the
trail and we were again stopping periodically.&lt;/p&gt;
&lt;h3 id=&#34;toutle-river&#34;&gt;Toutle River&lt;/h3&gt;
&lt;p&gt;The last section down to the river itself is basically a steep cliff
for which the forest rangers have attached ropes for hikers to use.
It requires going down backwards, with your feet walking down the cliff
as you go hand over hand on the rope.  Once you get over the initial
shock it is actually rather easy to do.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ToutleRope2.jpg&#34; alt=&#34;toutle rope2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ToutleRope3.jpg&#34; alt=&#34;toutle rope3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ToutleRiver.jpg&#34; alt=&#34;toutle river&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;When we were there the Toutle River was around 5 feet across and we
were able to cross easily by jumping across rocks.  We set up camp
next to the river and the sandy floor made for a really plush
ground. It was honestly one of the best night sleeps I&amp;rsquo;ve ever had
camping.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ToutleRiver1.jpg&#34; alt=&#34;toutle river&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;There were lots of places to set up our tents and when we arrived we
were the only people there, although another group did arrive just
before dusk.&lt;/p&gt;
&lt;h2 id=&#34;day-2---102-miles&#34;&gt;Day 2 - 10.2 miles&lt;/h2&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./day2.png&#34; alt=&#34;day2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;In the morning we packed up, filled our water bottles and used the
ropes on the other side to climb out.  The path to climb out was
easier than the one to climb down and we probably could have managed
it without the rope.&lt;/p&gt;
&lt;p&gt;Our goal was to hike down near the Summit trail, find a spot to camp
and hopefully water nearby.  The other campers at the river were
hiking the mountain in a clockwise direction and said they didn&amp;rsquo;t see
any water other than June Lake. So our fall back plan was to find a
camping spot around the Summit trail, dump our packs and hike down to
June Lake for water.&lt;/p&gt;
&lt;h3 id=&#34;crescent-ridge&#34;&gt;Crescent Ridge&lt;/h3&gt;
&lt;p&gt;Right out of the river the hike is a brutal uphill climb that takes you
from 3200 ft to 4700 ft in 1.7 miles.  It&amp;rsquo;s almost entirely in the
trees so you don&amp;rsquo;t get much of a view either.  We did stop to take a
couple of breaks on the hike up as we were still finding wild
Salmonberries and Huckleberries.  The Huckleberries especially were
perfectly ripe and really good.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./TobinRelaxing.jpg&#34; alt=&#34;Tobin Relaxing&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;About 1.5 miles after reaching the top of that climb the trail crosses
another ravine which again requires the use of attached ropes to climb in
and out of.  There was no water to cross in this one.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Rope1.jpg&#34; alt=&#34;rope1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After that the next 4 miles is a really nice and relaxing hike through
some beautiful scenery..&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Day2Trail.jpg&#34; alt=&#34;Trail&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;However, once we reached the trail for Summit Route we realized that
&amp;ldquo;finding a camping spot near by&amp;rdquo; was virtually impossible.  The trail
is really cut into a steep mountain side and the flora is pretty thick
on either side of the trail.  We either had to hike back a mile or so
to where there were some flat areas to camp, or hike forward and hope
it flattened out.  Hiking back would have taken us further away from
water so we decided to go forward.&lt;/p&gt;
&lt;h3 id=&#34;chocolate-falls&#34;&gt;Chocolate Falls&lt;/h3&gt;
&lt;p&gt;Almost exactly two miles from Summit Route is Chocolate Falls and we
decided to make camp there as there are plenty of flat areas nearby
just before you cross over the falls. However, if you venture a little
ways towards the cliff (and away from the falls) you will find a
really nice circular camping area, complete with a fire pit (which we
did not use).  However, being so far off the path was really nice as
we were not bothered at all by people passing on the trail and we felt
secure about leaving our stuff there while we hiked to the summit the
next day.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ChocolateCamp.jpg&#34; alt=&#34;chocolate camp&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Chocolate Falls is named as such because the water contains a &lt;em&gt;lot&lt;/em&gt; of
sediment and takes on a chocolate milk color/consistency.  But there
was water and by straining it through a bandana and our Sawyer squeeze
bag we were able to filter it enough to drink.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ChocolateFalls1.jpg&#34; alt=&#34;chocolate falls1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ChocolateFalls2.jpg&#34; alt=&#34;chocolate falls2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ChocolateFalls3.jpg&#34; alt=&#34;chocolate falls3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./ChocolateFalls4.jpg&#34; alt=&#34;chocolate falls4&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the bandana was not part of our inital plan but the water had a
lot of silt in it and Tobin had a brand new bandana with him.
Regretfully the bandana was red and had probably only been washed once
so when we were straining the water it took on a very pink color.
Even after using the Sawyer squeeze bag it still had a bit of a
pinkish hue to it, but it didn&amp;rsquo;t seem to contribute any taste.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&#34;day-3---82-miles---the-summit&#34;&gt;Day 3 - 8.2 miles - The Summit&lt;/h2&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./day3.png&#34; alt=&#34;day3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Up until now our weather had been perfect but this morning it was
raining.  We had initially thought we would start on our summit hike
at 8am, but the rain was coming down hard enough that we just stayed
in our tents for a couple of hours to see if it would clear.  By 11am the
rain was starting to let up enough so we decided to get going before
it was too late.  Both of us took our backpacks but only brought with us
some food, water, warm clothes and our permit.&lt;/p&gt;
&lt;p&gt;We now had to backtrack the two miles back to the Summit Route.  This
was mostly uphill and because of the rain we were getting soaked every time
we brushed up against the plants on the sides of the trail.&lt;/p&gt;
&lt;h3 id=&#34;summit-ridge&#34;&gt;Summit Ridge&lt;/h3&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./SummitRoute1.jpg&#34; alt=&#34;summit route1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;This was not an easy hike.  The very start of this path is like a
normal, albeit steep, trail but it quickly turns into climbing over
volcanic boulders.  There isn&amp;rsquo;t really a trail to follow anymore, but
the route is marked by posts which are placed just close enough
together that you can just see the next one.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./SummitRoute2.jpg&#34; alt=&#34;summit route2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;We passed a couple of people coming down who had gone up very early
that morning and mentioned that the entire top of the mountain was
pretty well fogged in and there was not much to see.  Still, we were
determined and continued up. The entire hike is about 2.1 miles, but
it is very slow going.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./SummitRoute3.jpg&#34; alt=&#34;summit route3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;About 2/3rds of the way up we met a forest ranger who told us that
&amp;ldquo;there is a crack at the rim and it appears that a section is about to
fall in,&amp;rdquo; so we should stay back several meters from the edge.  He
also mentioned that because of the weather he was guessing that of the
100 passes that were issued for that day, probably about 20 of them
were going to actually be used.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;pumice-path&#34;&gt;Pumice Path&lt;/h3&gt;
&lt;p&gt;As hard as the hike was so far, the pumice path that is at the end is
even harder.  The incline is very steep and with every step up you
take you slip back down almost a half a step.  It&amp;rsquo;s like this for
almost half a mile and the pumice easily doubles the distance. There
is also not a clear visual for how far you still have to go so it can
seem a bit unending at times.&lt;/p&gt;
&lt;h3 id=&#34;the-top&#34;&gt;The top&lt;/h3&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit1.jpg&#34; alt=&#34;summit 1&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit2.jpg&#34; alt=&#34;summit 2&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Summit3.jpg&#34; alt=&#34;summit 3&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./View.jpg&#34; alt=&#34;view&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The good thing about waiting those three hours before leaving camp is
that by the time we did reach the top the clouds were clearing and we
could see all around the mountain.  Unfortunately, the clouds &lt;em&gt;in&lt;/em&gt; the
crater did not clear so we never really got a good full look inside,
although from time to time we got a couple of good glimpses.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./Crater.jpg&#34; alt=&#34;cater&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Otherwise the view from the top is really amazing and I&amp;rsquo;m glad we made
the trip.  We did walk part way around the edge and we did see the
crack the ranger was talking about.  Because of the scale of
everything, looking over the edge didn&amp;rsquo;t seem like a huge drop, but
apparently it&amp;rsquo;s something like a 2600 ft drop.&lt;/p&gt;
&lt;p&gt;After spending about 30 mins at the top we headed down which gives you
a totally new perspective on just how far you have climbed and how
much on a ridge you really are.&lt;/p&gt;
&lt;p&gt;By the time we reached our camp at Chocolate Falls it was getting
dark.  We decided to grab some more water but then ended up just
leaving it in our bottles so we could filter it the next day.&lt;/p&gt;
&lt;p&gt;Day 4 - 12.3 miles&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img src=&#34;./day4.png&#34; alt=&#34;day4&#34; loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The next morning we were awakened by the sounds of runners going down
the trail.  It turns out that there were several races going on that
day, with one of them being a full run around the mountain.&lt;/p&gt;
&lt;p&gt;It also turns out that Chocolate Falls does not normally run in the
mornings as it needs the afternoon sun to melt the snow.  So it was
really fortunate we had collected water the night before as it was
totally dry that morning.&lt;/p&gt;
&lt;p&gt;It was a 1.2 mile hike to the cut off for June Lake and shortly past
that past a creek that was flowing and allowed us to get more water.
This water was clear and didn&amp;rsquo;t need to be double filtered.  After
that it was 4.8 miles to Pumice Butte.  The hike was really beautiful
and you can see several other mountains in the distance.  We stopped
often for Huckleberries and the very occasional Salmonberry and to move
off the path at times for runners.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Trail1_hu_a17ded8f68d22978.webp 400w,
               /post/summiting-st-helens-2019/Trail1_hu_c9ca5cb1fda47a8e.webp 760w,
               /post/summiting-st-helens-2019/Trail1_hu_7244e457a1c415dc.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Trail1_hu_a17ded8f68d22978.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Trail2_hu_64f3188e480bdd09.webp 400w,
               /post/summiting-st-helens-2019/Trail2_hu_8e36911938e67c9f.webp 760w,
               /post/summiting-st-helens-2019/Trail2_hu_ab4500bdac0d10c7.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Trail2_hu_64f3188e480bdd09.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Trail3_hu_c5ac975f6cbba38c.webp 400w,
               /post/summiting-st-helens-2019/Trail3_hu_be4b414e555d7fb8.webp 760w,
               /post/summiting-st-helens-2019/Trail3_hu_7e33873dc819ba85.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Trail3_hu_c5ac975f6cbba38c.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Trail4_hu_b42a9fa944fc6f72.webp 400w,
               /post/summiting-st-helens-2019/Trail4_hu_73ee58d19a2df885.webp 760w,
               /post/summiting-st-helens-2019/Trail4_hu_7480bd0e89cb8848.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Trail4_hu_b42a9fa944fc6f72.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 id=&#34;pumice-butte&#34;&gt;Pumice Butte&lt;/h3&gt;
&lt;p&gt;Several people claim you can get water near Pumice Butte but all the
water we saw was very stagnant and didn&amp;rsquo;t look good at all.  The 1.8
miles of The Plains of Abraham are flat, long and because of the time
of day we were there, very hot.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Flats1_hu_f36b676837fd26a6.webp 400w,
               /post/summiting-st-helens-2019/Flats1_hu_2c1cc59da06bee72.webp 760w,
               /post/summiting-st-helens-2019/Flats1_hu_23c9156d01dbf3bb.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Flats1_hu_f36b676837fd26a6.webp&#34;
               width=&#34;760&#34;
               height=&#34;570&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Flats2_hu_6f4eb416e1dea272.webp 400w,
               /post/summiting-st-helens-2019/Flats2_hu_8d82bb370f0cb173.webp 760w,
               /post/summiting-st-helens-2019/Flats2_hu_8cbbbb6573fd6a69.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Flats2_hu_6f4eb416e1dea272.webp&#34;
               width=&#34;760&#34;
               height=&#34;570&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;

















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/Flats3_hu_99d83307164c8e40.webp 400w,
               /post/summiting-st-helens-2019/Flats3_hu_6cdf22c6eda2812b.webp 760w,
               /post/summiting-st-helens-2019/Flats3_hu_f55d6c565f973a69.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/Flats3_hu_99d83307164c8e40.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;At the end of the Flats we could have opted to take a short cut back
to the car (and my feet were killing me by this point), or we could
continue on the Loowit trail and complete the full loop.  We opted to
complete the full loop and just before we headed up Windy pass we
crossed a running creek and were able to fill up on water one last
time.&lt;/p&gt;
&lt;h3 id=&#34;windy-pass&#34;&gt;Windy pass&lt;/h3&gt;
&lt;p&gt;The climb over Windy pass was harder than I was expecting as you have
a steep climb up, and short jog forward and then a steep switchback
back down.  Also, deceptively, they have put posts that run up to the
left, giving the impression you have to climb much higher than you
actually do.  I&amp;rsquo;m not sure if this was someones idea of a joke or if
there is another trail up that way.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;&#34; srcset=&#34;
               /post/summiting-st-helens-2019/WindyPass_hu_6e7c1d67f1446df.webp 400w,
               /post/summiting-st-helens-2019/WindyPass_hu_4c271db6f0462ec9.webp 760w,
               /post/summiting-st-helens-2019/WindyPass_hu_25c15f13cddfef5f.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/summiting-st-helens-2019/WindyPass_hu_6e7c1d67f1446df.webp&#34;
               width=&#34;760&#34;
               height=&#34;507&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;In any case we made it down and eventually made it back to the spot
where we first started on the Loowit trail.  From there it was 2.5
miles back to the car and by now we were feeling every step of it.
Also the winding gravel road has several false turns where you can
easily fall into thinking that the car is &amp;ldquo;just ahead&amp;rdquo;.&lt;/p&gt;
&lt;h3 id=&#34;the-car&#34;&gt;The car&lt;/h3&gt;
&lt;p&gt;Getting back to the car we changed into clean(er) clothes and headed
back to Tobins car, stopping off at Plaza Jalisco&amp;rsquo;s Mexican restaurant
for dinner.&lt;/p&gt;
&lt;h2 id=&#34;retrospective&#34;&gt;Retrospective&lt;/h2&gt;
&lt;h3 id=&#34;equipment&#34;&gt;Equipment&lt;/h3&gt;
&lt;h4 id=&#34;boots&#34;&gt;Boots&lt;/h4&gt;
&lt;p&gt;Tobin was wearing hiking shoes while I was wearing hiking boots.
Although my boots did better when it was wet, and when we were
climbing over the boulders, I think in the long run wearing hiking
shoes would have been a better call.  They are lighter and since we
were on a trail for a majority of the time I think the boots were,
perhaps, overkill.  And by the last day, especially that last 2.5
miles, my feet were killing me.  Tobin commented that his feet hurt as
well, but I think the boots were adding to my discomfort.&lt;/p&gt;
&lt;h4 id=&#34;socks&#34;&gt;Socks&lt;/h4&gt;
&lt;p&gt;I should have brought more socks.  In the past when I&amp;rsquo;ve hiked for a
couple of days (2-4) I usually bring just two pairs of socks, but for
those hikes I&amp;rsquo;m often spending the day at the destination or wearing
my camp shoes. For this trip we were hiking every day and it would
have been really nice to put on a clean pair of socks every morning.
Tobin washed his out in the river which seems to have helped, but
really I just think at least one pair of socks a day is the minimum
and perhaps two.&lt;/p&gt;
&lt;h4 id=&#34;camera&#34;&gt;Camera&lt;/h4&gt;
&lt;p&gt;Tobin and I both brought cameras with us, but while I just brought my
cell phone (because, hey, it has a camera in it) Tobin brought an
actual digital camera.  However, since I had my phone off for the
majority of the trip, taking a picture meant that I had to stop, take
out my phone, wait for it to turn on, take a picture and then shut it
down and put it away again.  Tobin, on the other hand, had a handy
pouch on his belt and was able to quickly take out his camera to take
pictures.  I had thought that non-SLR digital cameras were a thing
of the past, but after this trip I now included a digital camera in my
packing list.&lt;/p&gt;
&lt;h4 id=&#34;water&#34;&gt;Water&lt;/h4&gt;
&lt;p&gt;Similar to the camera, Tobin&amp;rsquo;s backpack also enabled him to carry at
least one liter of water where he could easily reach and drink from
it.  Mine was attached to the back of my backpack making it a pain and
often I had to ask Tobin to get my water for me.  As a consequence I
probably drank less than I should have.  I need to either figure out a
better way to hook up a bottle, or get one of those camel packs.&lt;/p&gt;
&lt;h4 id=&#34;more-space&#34;&gt;More space&lt;/h4&gt;
&lt;p&gt;I had brought with me some fleece pants and a fleece top and while
these don&amp;rsquo;t weigh much, they do take up a fair bit of space.  Weight
wasn&amp;rsquo;t really an issue on this trip, but space was.  As it was, my bag
was packed really tight which required extra time packing up in the
morning and trying to get to anything in my pack.  Since this trip I
have replaced some of the gear in my pack with things that just take
up less space so I can treat my bag as more of a simple stuff sack.&lt;/p&gt;
&lt;h4 id=&#34;bivy-or-tent&#34;&gt;Bivy or Tent&lt;/h4&gt;
&lt;p&gt;I really like sleeping in a bivy but at the last moment I decided to
swap my bivy for a tent which I feel was a good call.  I like using a
bivy because they are fast to set up and overall just more fun.
However, on this trip it would have been miserable sleeping in the
bivy when it was raining and the 3 hours we hung out in the tent
before climbing to the summit.  I&amp;rsquo;m also not sure I would have been as
keen on leaving my bivy out when we were hiking.&lt;/p&gt;
&lt;h4 id=&#34;sleeping-pad&#34;&gt;Sleeping pad&lt;/h4&gt;
&lt;p&gt;I had a blow up air pad while Tobin had the waffle mat.  I think the
air pad is more comfortable, but you do run the risk that it could
rupture and then you can be in some real trouble.  I did have a small
patch kit with me, but I&amp;rsquo;ve never used it and I&amp;rsquo;m not sure how well it
would work if I had to patch my air pad at night.  Additionally, the
air pad is noisy as hell when you move around on it.  It makes a sound
that is similar to rubbing a balloon. For next season I think I&amp;rsquo;m going
to try using a waffle pad and see how that works.  I also like that
the set up for the waffle pad is super quick.&lt;/p&gt;
&lt;h4 id=&#34;hiking-gloves&#34;&gt;Hiking gloves&lt;/h4&gt;
&lt;p&gt;I didn&amp;rsquo;t have a good pair of hiking gloves with me and I really wish I
did.  I did have some fingerless gloves (aka hobo gloves) which
convert to mittens, and while they were warm enough they were not the
best for climbing with.  Ultimately I need to get a good pair of
gloves that are warm enough for the evenings, but also rugged enough
to climb over boulders with.  Or, if no such glove exists, then
perhaps two pairs, one for warmth and one for climbing.&lt;/p&gt;
&lt;h4 id=&#34;folding-chair&#34;&gt;Folding chair&lt;/h4&gt;
&lt;p&gt;I have also added to my bag a folding chair.  Yeah, I could just sit
on stones and trees and the such, but after a long day, and in the
morning, it is nice to be able to sit on a real chair.  Sitting on the
stones and logs meant that I was constantly trying to find a
comfortable one or leaning to the side as I am sitting.  The new
folding chairs are down around a pound now so I&amp;rsquo;m hoping to try this
out on my next hike.&lt;/p&gt;
&lt;h4 id=&#34;sawyer-squeeze-bottles&#34;&gt;Sawyer squeeze bottles&lt;/h4&gt;
&lt;p&gt;These things are awesome.  When we got home and cleaned it out it was
amazing how brown the water was and how much they filtered things.
They are awesome, I just wanted to point that out.&lt;/p&gt;
&lt;h3 id=&#34;hike&#34;&gt;Hike&lt;/h3&gt;
&lt;h4 id=&#34;bugs&#34;&gt;Bugs&lt;/h4&gt;
&lt;p&gt;Normally I&amp;rsquo;m bothered by mosquitoes but I don&amp;rsquo;t think we encountered
any on this trip.  Perhaps this is due to the general lack of
water but even the flies were not that bad.&lt;/p&gt;
&lt;h4 id=&#34;overall&#34;&gt;Overall&lt;/h4&gt;
&lt;p&gt;All in all we had a fantastic time and hiked a total of 42.9 miles.
The changes in the scenery were really interesting and the rope climbs
and wild berries made for an interesting trek. Even though we had to
scramble to find an alternative, I really think entering at Windy Pass
and working counter clockwise worked out well for us.  The only
disappointment was that the weather wasn&amp;rsquo;t clearer when we were at the
top, but it just means we will have to return sometime.&lt;/p&gt;
&lt;h3 id=&#34;forest-rangers&#34;&gt;Forest Rangers&lt;/h3&gt;
&lt;p&gt;And finally, I want to give special thanks to the awesome forest
rangers we met along the way.  They are super fun to talk with and a
treasure trove of information. They are also the ones that go through
each year and set the ropes so you can climb down and up the cliffs.
So thank you!&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Samba4 Domain Controller on Ubuntu 18.04</title>
      <link>https://hrakaroo.com/post/samba4-ubuntu18.04/</link>
      <pubDate>Fri, 27 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/samba4-ubuntu18.04/</guid>
      <description>&lt;h2 id=&#34;quick-background&#34;&gt;Quick Background&lt;/h2&gt;
&lt;p&gt;I volunteer as a Systems Administrator for a small medical
clinic. They have a very slim IT budget so while their desktop systems
are primarily Windows 11, I&amp;rsquo;ve configured most of their server
infrastructure with Linux. A &lt;a href=&#34;https://www.proxmox.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Proxmox&lt;/a&gt;
server is used to manage their virtualized environment and I&amp;rsquo;m using
&lt;a href=&#34;https://www.samba.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Samba4&lt;/a&gt; for their Active Directory
Domain Controller.&lt;/p&gt;
&lt;p&gt;When I first set up Samba4 as their Active Directory I was able to use
&lt;a href=&#34;https://www.centos.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CentOS&lt;/a&gt; 6 with the Samba4 binary packages
from SerNet. Although this has been working well enough, it has always
been a little quirky and I had only ever set up a single Domain
Controller. So recently I decided to upgrade their Domain Controllers
to the newest version and add a second Domain Controller.&lt;/p&gt;
&lt;p&gt;Initially I tried to find the SerNet packages but it appears they have
rebranded themselves as &lt;a href=&#34;https://samba.plus/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Samba+&lt;/a&gt; and now charge a
subscription fee for the binary packages.  While I would love to
support them, paying a subscription fee is not an option for this
clinic.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d really like to stick with CentOS as the server distribution as I
have a lot of experience with it and have always found it to be a
solid, minimial distribution. However, for reasons RedHat has decided
that the Samba4 build that comes with Fedora should not include the
ability to act as an Active Directory Domain Controller.&lt;/p&gt;
&lt;p&gt;I could build the sources from scratch and still use CentOS, but that
would require installing all the build tools on each server and I&amp;rsquo;m
trying to keep them as light as possible.  Plus, building from scratch
often introduces its own host of challenges and I really just want to
set this up and move on to my next project.  So instead I&amp;rsquo;m going to
give a go at using &lt;a href=&#34;https://ubuntu.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Ubuntu&lt;/a&gt;, which I really like
as a desktop system but have never been terribly thrilled with as a
server system.&lt;/p&gt;
&lt;p&gt;For setting this up I am mostly following the instructions from
&lt;a href=&#34;https://www.tecmint.com/install-samba4-active-directory-ubuntu/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;tecmint&lt;/a&gt;
and specifically &lt;a href=&#34;https://www.tecmint.com/join-additional-ubuntu-dc-to-samba4-ad-dc-failover-replication/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;this
one&lt;/a&gt;. However,
these instructions were written for Ubuntu 16.04 so I&amp;rsquo;m making this
post to document the steps I took in setting up Samba4 on an Ubuntu
18.04 system.&lt;/p&gt;
&lt;h2 id=&#34;networking-overview&#34;&gt;Networking overview&lt;/h2&gt;
&lt;p&gt;For reference, the main gateway is a
&lt;a href=&#34;https://www.pfsense.org/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;pfSense&lt;/a&gt; system set up at 10.0.1.1.  This runs
as the primary DNS and DHCP server for the network.  The domain name
I&amp;rsquo;ll use in these instructions is &lt;code&gt;hrakaroo.lan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The Active Directory domain is &lt;code&gt;ad.hrakaroo.lan&lt;/code&gt;.  I&amp;rsquo;ve specifically
set this up so that Active Directory is on it&amp;rsquo;s own sub domain as I
don&amp;rsquo;t want the rest of the server infrastructure to be dependent on
Active Directory.  I&amp;rsquo;d rather isolate Active Directory to its own
area.&lt;/p&gt;
&lt;h2 id=&#34;creating-a-new-system-in-proxmox&#34;&gt;Creating a new system in Proxmox&lt;/h2&gt;
&lt;p&gt;In Proxmox I&amp;rsquo;ve created the following new host&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Hostname: adc1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Memory: 2 G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Disk: 32 G
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Processors: 1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Cores: 2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In retrospect (by looking at the usage charts in Proxmox) it looks
like I could have cut the memory, disk and cores in half and it would
have been fine, but I&amp;rsquo;m going to leave it as is for now.&lt;/p&gt;
&lt;p&gt;Once the vm is created, start the server.&lt;/p&gt;
&lt;p&gt;Most of the installation is pretty straightforward so I&amp;rsquo;m only
going to highlight when I didn&amp;rsquo;t select the default.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Installer update available&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;During the install it suggests that an update to the installer
is available and asks if you want to update.  I&amp;rsquo;m not sure if it
matters much, but I said yes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Networking&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Edit the IPv4 configuration and select Manual&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Subnet: 10.0.1.0/24
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Address: 10.0.1.25
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Gateway: 10.0.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Name servers: 10.0.1.26
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Search domains: ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;10.0.1.26 is the name servers for the existing Active Directory
Domain Controller. Once the system is fully configured I&amp;rsquo;ll change
this, but it makes the initial setup much easier if this points
at your existing Domain Controller.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Profile Setup&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;This is the first thing I don&amp;rsquo;t like about Ubuntu.  I&amp;rsquo;d rather just
have a password set for the root account.  I get the reason why they
are doing this, but for a super small setup this is more of an
annoyance than a help.  Sadly you also can&amp;rsquo;t use general accounts like
&amp;lsquo;admin&amp;rsquo; or &amp;lsquo;staff&amp;rsquo; either.  So I ended up creating a personal account
for Joshua Gerth.  (This turned out to be a bit of a mistake, more
on this can be found in the &lt;a href=&#34;#profile-mistake&#34;&gt;additional things&lt;/a&gt;
at the bottom.)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;SSH Setup&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[X] Enable install the OpenSSH server.&lt;/p&gt;
&lt;h2 id=&#34;basic-server-configuration&#34;&gt;Basic Server Configuration&lt;/h2&gt;
&lt;p&gt;For almost all of these commands I find it easier to work by ssh&amp;rsquo;ing
into the box rather than using the Proxmox console as copy/paste and
editing all work better.&lt;/p&gt;
&lt;h3 id=&#34;update-and-install-emacs&#34;&gt;Update and install emacs&lt;/h3&gt;
&lt;p&gt;Okay, once the server is up and running I always run the update commands
to make sure everything is up to date:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo apt update 
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo apt upgrade
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo apt dist-upgrade
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and also, because vi is terrible I always install emacs:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ sudo apt install -y emacs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;turn-off-ipv6&#34;&gt;Turn off IPv6&lt;/h3&gt;
&lt;p&gt;This may not be &lt;em&gt;necessary&lt;/em&gt;, but I find that debugging things when
only IPv4 is enabled to be a lot easier.  The environment I&amp;rsquo;m
installing this in is small enough that we are not at risk of running
out of IP numbers any time soon and since everything gets NAT&amp;rsquo;ed
anyhow I don&amp;rsquo;t really see a need to enable IPv6.&lt;/p&gt;
&lt;p&gt;That said, this is the second thing that I don&amp;rsquo;t care for about
Ubuntu.  In CentOS turning off IPv6 is sort of trivial, but on Ubuntu
I this proved to be rather difficult.  Most of the existing
suggestions only turned off part of IPv6 and it was only after a lot
of trial and error did I happen on a post with the best answer.&lt;/p&gt;
&lt;p&gt;Basically you need to edit &lt;code&gt;/etc/default/grub&lt;/code&gt; and set&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  GRUB_CMDLINE_LINUX=&amp;#34;xxxxx ipv6.disable=1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;(For me there was nothing in the xxxxx area, but if you have any
existing options then leave them there and add the ipv6.disable at the
end.)&lt;/p&gt;
&lt;p&gt;and then run&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ update-grub
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;turn-off-dnsmasq&#34;&gt;Turn off dnsmasq&lt;/h3&gt;
&lt;p&gt;This is the third thing that I don&amp;rsquo;t like about Ubuntu, it runs a stub
dnsmasq as a DNS caching server.  Again, I get &lt;em&gt;why&lt;/em&gt; they did this,
but I&amp;rsquo;ve never found this to be useful on a server and nine out of ten
times it just causes problems.&lt;/p&gt;
&lt;p&gt;To disable it I&amp;rsquo;m mostly following
&lt;a href=&#34;https://askubuntu.com/questions/1081832/how-do-i-disable-systemd-resolved-and-replace-with-something-sane-on-ubuntu-18&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;these instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Edit &lt;code&gt;/etc/systemd/resolved.conf&lt;/code&gt; and add&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;DNSStubListener=no
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then remove the existing symlink at /etc/resolv.conf and rebuild it&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ rm /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Since this has been such a thorn in my side I prefer to reboot here
to make damn sure the new settings have taken.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/resolv.conf&lt;/code&gt; should now show the values that you entered on
initial setup.&lt;/p&gt;
&lt;h3 id=&#34;ntp&#34;&gt;NTP&lt;/h3&gt;
&lt;p&gt;Domain Controllers are sensitive to clock drift and need to be
configured to use a network time server.  Normally I would configure
&lt;code&gt;ntpd&lt;/code&gt; on the host, but Ubuntu has decided to ship with &lt;code&gt;timedatectl&lt;/code&gt;
instead which claims to be a lightweight ntp client.  (Humorously, 90%
of the google searches for &lt;code&gt;timedatectl&lt;/code&gt; suggest turning it off and
replacing it with a real ntpd, however, I&amp;rsquo;m going to try sticking with
timedatectl for now.)&lt;/p&gt;
&lt;p&gt;First I like to set the timezone so I don&amp;rsquo;t need to convert
everything&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ timedatectl set-timezone America/Los_Angeles
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, edit the &lt;code&gt;/etc/systemd/timesyncd.conf&lt;/code&gt; file and set the NTP entry
to point to your NTP server. (I&amp;rsquo;m running an NTP server on another VM at
10.0.1.22).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;[Time]
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;NTP=10.0.1.22
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;restart the timesyncd&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ systemctl restart systemd-timesyncd
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and verify it with&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ cat /var/log/syslog &lt;span class=&#34;p&#34;&gt;|&lt;/span&gt; grep systemd-timesyncd
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   XXX adc1 systemd-timesyncd&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;1416&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt;: Synchronized to &lt;span class=&#34;nb&#34;&gt;time&lt;/span&gt; server 10.0.1.22:123 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;10.0.1.22&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;install-samba&#34;&gt;Install Samba&lt;/h2&gt;
&lt;p&gt;Since we are actually about to install Samba I like to do one more
reboot here.&lt;/p&gt;
&lt;h3 id=&#34;update-the-hosts-file&#34;&gt;Update the hosts file&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;/etc/hosts&lt;/code&gt; file should have an entry for every domain
controller, including itself.  (I also removed the old IPv6 stuff)&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;127.0.0.1 localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10.0.1.25 adc1.ad.hrakaroo.lan adc1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;10.0.1.26 oldadc.ad.hrakaroo.lan oldadc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, 10.0.1.25 is this box, the new Active Directory server and 10.0.1.26
is the old Active Directory server.&lt;/p&gt;
&lt;h3 id=&#34;install-samba4&#34;&gt;Install Samba4&lt;/h3&gt;
&lt;p&gt;Actually install samba&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ apt install -y samba krb5-user krb5-config winbind libpam-winbind libnss-winbind
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you already have things correctly setup then this should
automatically find the kerberos SRV records for your domain which are
being hosted on your existing Active Directory server, if not it may
prompt you for them.  If it does prompt you enter the domain in all
upper case for the Default realm and in regular case for the Kerberos
servers and Administrative server.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Default Kerberos realm:  AD.HRAKAROO.LAN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Kerberos servers realm:  ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Administrative server:   ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Verify kerberos works by&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ kinit jgerth@AD.HRAKAROO.LAN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Password &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; jgerth@AD.HRAKAROO.LAN:  &amp;lt;passwd&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ klist
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Ticket cache: FILE:/tmp/krb5cc_0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Default principal: jgerth@AD.HRAKAROO.LAN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Valid starting     Expires            Service principal
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   03/27/20 18:54:49  03/28/20 04:54:49  krbtgt/AD.HRAKAROO.LAN@AD.HRAKAROO.LAN
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;           renew &lt;span class=&#34;k&#34;&gt;until&lt;/span&gt; 03/28/20 18:54:46
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id=&#34;join-the-domain&#34;&gt;Join the Domain&lt;/h3&gt;
&lt;p&gt;Remove the existing smb.conf as it will be recreated by samba-tool.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ systemctl stop samba-ad-dc smbd nmbd winbind
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ mv /etc/samba/smb.conf /etc/samba/smb.conf.initial
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use samba-tool to join the domain&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ samba-tool domain join ad.hrakaroo.lan DC -U &lt;span class=&#34;s2&#34;&gt;&amp;#34;AD\jgerth&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now configure the server to start samba-ad-dc automatically&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ systemctl mask smbd nmbd winbind
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Created symlink /etc/systemd/system/smbd.service → /dev/null.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Created symlink /etc/systemd/system/nmbd.service → /dev/null.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Created symlink /etc/systemd/system/winbind.service → /dev/null.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ systemctl unmask samba-ad-dc
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Removed /etc/systemd/system/samba-ad-dc.service.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ systemctl &lt;span class=&#34;nb&#34;&gt;enable&lt;/span&gt; samba-ad-dc
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Use the host command to verify your configuration.  This apparently
hits every nameserver listed so you may get some errors when it tries
to hit the non-active directory DNS servers.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ host ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   ad.hrakaroo.lan has address 10.0.1.26
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   ad.hrakaroo.lan has address 10.0.1.25
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Host ad.hrakaroo.lan not found: 3&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;NXDOMAIN&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   Host ad.hrakaroo.lan not found: 3&lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;NXDOMAIN&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ host -t SRV _kerberos._udp.ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   _kerberos._udp.ad.hrakaroo.lan has SRV record &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;88&lt;/span&gt; oldadc.ad.hrakaroo.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   _kerberos._udp.ad.hrakaroo.lan has SRV record &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;88&lt;/span&gt; adc1.ad.hrakaroo.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;$ host -t SRV _ldap._tcp.ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   _ldap._tcp.ad.hrakaroo.lan has SRV record &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;389&lt;/span&gt; oldadc.ad.hrakaroo.lan.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;   _ldap._tcp.ad.hrakaroo.lan has SRV record &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;100&lt;/span&gt; &lt;span class=&#34;m&#34;&gt;389&lt;/span&gt; adc1.ad.hrakaroo.lan.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;change-dns&#34;&gt;Change DNS&lt;/h2&gt;
&lt;p&gt;At this point you could be done as the server is now up and running.
However, it is technically using the other domain controller for DNS
lookups.  Even if I wasn&amp;rsquo;t planning on decommissioning the older
server this would still be creating an out of band dependency between
them and as Active Directory is, itself, a DNS server I prefer to update
the server to use itself for DNS lookups.  (Similar to what
Ubuntu was initially trying to do with the dnsmasq stuff).&lt;/p&gt;
&lt;p&gt;To do this, modify &lt;code&gt;/etc/netplan&lt;/code&gt; and set &lt;code&gt;127.0.0.1&lt;/code&gt; as first in
your nameserver list and then the upstream DNS server second.&lt;/p&gt;
&lt;p&gt;Also verify that &lt;code&gt;search&lt;/code&gt; is set to use the ad domain first and then
your global domain.  Like this&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;            nameservers:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                addresses:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                - 127.0.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                - 10.0.1.1
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                search:
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                - ad.hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                - hrakaroo.lan
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As always, reboot and check the values in the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; and
do a couple of tests with &lt;code&gt;nslookup&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&#34;configure-proxmox-to-boot-the-server&#34;&gt;Configure Proxmox to boot the server&lt;/h2&gt;
&lt;p&gt;I also configure Proxmox to start the service at boot.  This way if there
is a power outage Proxmox will be sure to start your domain controllers.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Options -&amp;gt; Start at boot -&amp;gt; True&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;additional-things&#34;&gt;Additional things&lt;/h2&gt;
&lt;h3 id=&#34;decommissioning-the-old-active-directory-controller&#34;&gt;Decommissioning the old Active Directory Controller&lt;/h3&gt;
&lt;p&gt;With the new Domain Controller up you can now decommission the old
Domain Controller by moving the roles (7) from the old DC to your
new one.  However, the version of Samba which shipps with Ubuntu 18.04
is 4.7 which, apparently, has a bug with the python code that you
use to move the roles.  To fix this I had to edit
&lt;code&gt;/usr/lib/python2.7/dist-packages/samba/netcmd/fsmo.py&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;and insert line&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;samba.drs_utils&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;just after&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;samba&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;around line 20. After doing this the command to move the roles worked
just fine and I was able to fully decommission the older active domain
controller.&lt;/p&gt;
&lt;h3 id=&#34;profile-mistake&#34;&gt;Removing the profile&lt;/h3&gt;
&lt;p&gt;It turns out that creating the profile as I did during the initial set
up of Ubuntu was a bit of a mistake.  It didn&amp;rsquo;t cause an issue for
this server but it did on a file server I set up later which was also
on Ubuntu. The issue was that the Ubuntu account &lt;code&gt;jgerth&lt;/code&gt; conflicted
with the Active Directory accouunt &lt;code&gt;jgerth&lt;/code&gt; and made testing Windows File
Sharing under my account difficult.&lt;/p&gt;
&lt;p&gt;I could have addressed this by using a unique login on the Ubuntu
server (like &lt;code&gt;jogerth&lt;/code&gt;) but that feels like a pretty hacky solution.
Plus, it raises the bigger issue that ultimately someday someone else
is probably going to take over for me as the sysadmin and I don&amp;rsquo;t
really want them to have to use my account.  The &lt;em&gt;right&lt;/em&gt; way to solve
this is to create an LDAP server for the Linux boxes and host the
logins centrally, but this feels a bit like overkill to me as we
currently only have around 5 Linux systems and, at least for the time
being, I would be the only person in the LDAP server.&lt;/p&gt;
&lt;p&gt;I suppose I could bind the Linux boxes in to the Active Directory LDAP
server, but that creates a bit of a chicken and egg dependency on
Active Directory that I&amp;rsquo;m just not comfortable with.&lt;/p&gt;
&lt;p&gt;What I really want is just a generic sysadmin account, but if you are
going to do that then why not use the one that already comes with
every Unix/Linux system, &lt;code&gt;root&lt;/code&gt;.  So in the end I configured an
explicit password for &lt;code&gt;root&lt;/code&gt;, enabled SSH login for root and deleted
the account I created on setup.  I would never recommend this for a
larger environment, but for a small setup this worked out best.&lt;/p&gt;
&lt;h2 id=&#34;closing-thoughts&#34;&gt;Closing thoughts&lt;/h2&gt;
&lt;p&gt;In the end I&amp;rsquo;m glad I set this up from scratch rather than use the
build and instructions from SerNet. I have a better understanding
of what everything is doing and the weird quirkiness I was
experiencing with the old server has gone away (although, to be fair,
that could have been due to the older version I was running.)
I also feel more comfortable with the samba tool and adding and
removing domain controllers.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still not a huge fan of Ubuntu as a server. I don&amp;rsquo;t care for the
dnsmasq stub, the replacement for ntpd or how it forces you to create
a non-root account, but since it is a popular system I was able to
search for most of the errors I did encounter.&lt;/p&gt;
&lt;h2 id=&#34;supporting-windows-11-22h2&#34;&gt;Supporting Windows 11 22H2&lt;/h2&gt;
&lt;p&gt;In the two years since I set this system up it has been running nearly
flawlessly. The only real issue is that with Windows 11 update 22H2
Microsoft changed the encryption protocol (or something similar) and
they are no longer able to bind to the domain controller. (This does
not seem to impact existing systems that are already bound to the
domain.)&lt;/p&gt;
&lt;p&gt;The following Reddit link talks about the issue in depth
&lt;a href=&#34;https://www.reddit.com/r/sysadmin/comments/xoqend/samba_495_windows_11_22h2_kerberos/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://www.reddit.com/r/sysadmin/comments/xoqend/samba_495_windows_11_22h2_kerberos/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The fix is to update Samba to version 4.16.0, which isn&amp;rsquo;t directly
available for Ubuntu 18.04. The recommended path is probably to
upgrade to Ubuntu 22.x, but since that&amp;rsquo;s bound to cause it&amp;rsquo;s own host
of complications I instead opted to just change the source for Samba
by following these instructions&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://ubunlog.com/en/ya-fue-liberada-la-nueva-version-de-samba-4-16-0-y-estos-son-sus-cambios/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://ubunlog.com/en/ya-fue-liberada-la-nueva-version-de-samba-4-16-0-y-estos-son-sus-cambios/&lt;/a&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo add-apt-repository ppa:linux-schools/samba-latest
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt-get update
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;sudo apt install samba
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This was enough to upgrade samba on 18.04 to 4.16.0. Once I restarted
both domain controllers the Windows 11 with update 22H2 system was
able to bind to the Domain.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Fractal Rust</title>
      <link>https://hrakaroo.com/project/fractal-rust/</link>
      <pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/fractal-rust/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Glob Library for Java</title>
      <link>https://hrakaroo.com/project/glob-library-java/</link>
      <pubDate>Thu, 26 Dec 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/glob-library-java/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Ray the Tracer</title>
      <link>https://hrakaroo.com/project/ray-the-tracer/</link>
      <pubDate>Thu, 21 Nov 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/ray-the-tracer/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Go for Java Developers - Part 2</title>
      <link>https://hrakaroo.com/post/go-for-java-part2/</link>
      <pubDate>Tue, 01 Oct 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/go-for-java-part2/</guid>
      <description>&lt;p&gt;This is my second post on things I&amp;rsquo;m finding interesting about the Go
language.  For additional background on this series you might want to read
&lt;a href=&#34;../go-for-java-part1&#34;&gt;Part 1&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;dependency-management&#34;&gt;Dependency Management&lt;/h2&gt;
&lt;p&gt;This time I&amp;rsquo;m going to focus on dependency management and how
go approaches the &lt;a href=&#34;../sane-java-dependency-management&#34;&gt;diamond dependency issue&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First off, Go does not support pre-compiled libraries the way Java
does.  (Personally, I think compiled Jar files are from a by-gone time
and these days cause more harm than good, but that&amp;rsquo;s not why Go
doesn&amp;rsquo;t have them.)  Go doesn&amp;rsquo;t have them because Go code compiles
down into native assembly so it would be impossible to distribute
anything precompiled by Go and expect it to work on more than one
platform.&lt;/p&gt;
&lt;p&gt;So instead, Go publishes libraries as source code with Git tags and
avoids artifactory all together.  When you want to use a library you
create a dependency on a git repo with a specific tag version.&lt;/p&gt;
&lt;p&gt;This doesn&amp;rsquo;t exactly remove the diamond dependency issue as it is
entirely possible to have a transitive dependency on two different
versions of a library. In this case, as with Java, you still have to
select the version you want to use. However, there are two substantial
changes.&lt;/p&gt;
&lt;h3 id=&#34;cant-ignore-the-problem&#34;&gt;Can&amp;rsquo;t ignore the problem&lt;/h3&gt;
&lt;p&gt;First, you don&amp;rsquo;t have the option of ignoring or miss the issue.  This
means that if your service builds in Go, you can be confident your
code does not contain any &amp;ldquo;hidden&amp;rdquo; mismatch errors.  This effectively
removes an entire class of errors that you have with Java.  (TBH,
these don&amp;rsquo;t pop up too often, but the more common a library is the
greater the chance for this to happen.  I&amp;rsquo;ve seen this error triggered
with the Guava libraries.) Being able to completely remove
them as a class of problems would be nice.&lt;/p&gt;
&lt;h3 id=&#34;conflicts-identified-at-the-source-code-level&#34;&gt;Conflicts identified at the source code level&lt;/h3&gt;
&lt;p&gt;Second, conflicts are identified at the source code level and not at
the version level. This means that PATCH version mismatches won&amp;rsquo;t
break your build so long as they don&amp;rsquo;t change function signature (and
PATCHES really shouldn&amp;rsquo;t).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not saying that the way Go deals with this is perfect but it does
move the problem into a first class issue and forces a
resolution. Java could take this same approach if you copied all of
your dependencies into your code base as source code. But I did find
it as an interesting consequence on how Go handles dependency
management.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Go for Java Developers - Part 1</title>
      <link>https://hrakaroo.com/post/go-for-java-part1/</link>
      <pubDate>Thu, 01 Aug 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/go-for-java-part1/</guid>
      <description>&lt;p&gt;For the last 20+ years I&amp;rsquo;ve been mostly focused on Java development.
But recently I had an opportunity to join a team which was focused on
Go and having spent the early part of my career writing C++ I was
intrigued by some of the decisions Go made. So this is the first (of
possibly several) posts on things I found interesting about the
language and since my I have been doing mostly Java development I&amp;rsquo;m
going to be comparing and contrasting from that perspective.&lt;/p&gt;
&lt;h2 id=&#34;go-interfaces&#34;&gt;Go interfaces&lt;/h2&gt;
&lt;p&gt;For this first post I&amp;rsquo;m going to discuss one specific implication of
how Go handles interfaces.&lt;/p&gt;
&lt;p&gt;If you are unfamiliar with Go, it uses structural typing for
interfaces (which is more generally called &amp;ldquo;duck typing&amp;rdquo; meaning &amp;ldquo;if
it walks like a duck and talks like a duck … it&amp;rsquo;s a duck.&amp;rdquo;)  This
means that if a struct in Go has functions on it which match the
methods of an interface, instances of that struct are types of that
interface. This sounds more complicated than it actually is so lets
look at a quick example:&lt;/p&gt;
&lt;p&gt;Suppose you wanted to create a Person class and a Named interface in
Java, that might look like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Named&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Named&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Pretty straight forward.  Now, if we wanted to do a similar thing in
Go it would probably look something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Named&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Person&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;getName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ignoring the syntax differences, what is interesting is that in Go we
don&amp;rsquo;t declare Person as implementing Named.  This is implied by the
function on Person matching the method in the Named interface. The
compiler figures this out for us and instances of a Person are
automatically of type Named.&lt;/p&gt;
&lt;p&gt;So while this isn&amp;rsquo;t necessarily a new thing (Python mostly lets you
do the same thing), this was something new for a statically typed
language.&lt;/p&gt;
&lt;p&gt;When I first saw this I thought &amp;ldquo;meh, so you can leave off the
&amp;lsquo;implements&amp;rsquo; keyword, whatever.&amp;rdquo;  But I think this feature has a really
interesting implication.&lt;/p&gt;
&lt;h3 id=&#34;the-setup&#34;&gt;The setup&lt;/h3&gt;
&lt;p&gt;Back in Java land, let&amp;rsquo;s suppose you have a function that takes an SQL
PreparedStatement as one of its arguments.  Something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt; 	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;   &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might use this like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;PreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;prepareStatement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;SELECT …&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;someValue&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Writing a unit test for this would probably look something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DbTest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;nd&#34;&gt;@Test&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;public&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValueTest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;??&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;dog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// todo - verify the statement received the value of &amp;#34;dog&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The challenge is creating the test &lt;code&gt;PreparedStatement&lt;/code&gt;.  You could use
one of the mocking frameworks in Java (Mockito, PowerMock, …) to mock
the PreparedStatement and test that setValue is called with the value
&amp;ldquo;dog&amp;rdquo;.  While this does work, most/all of the mocking frameworks are
basically a nice user interface over a rats nest of reflection
calls. (Used incorrectly reflection is a tool to move compile time
errors back to the runtime.)  However, it is also possible to test
this without using a mocking framework by building our own class which
implements PreparedStatement:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;TestPreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;implements&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;PreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;parameterIndex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    	&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;this&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;	&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;An instance of our &lt;code&gt;TestPreparedStatement&lt;/code&gt; can now be passed into our
setValue method and we can later verify that the internal &amp;lsquo;value&amp;rsquo; is
set to &amp;ldquo;dog&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Ahhh … but there is a devil hidden in these details.
PreparedStatement is a massive interface with well over 50 methods.
In order to stub out the one method you want (setString) you are going
to need to also stub out all of the other ones as well.  We don&amp;rsquo;t ever
use them so they can all throw a RuntimeException (and thankfully most
modern IDEs can automatically generate this code for you) … but you
are still dealing with a lot of boilerplate code.&lt;/p&gt;
&lt;h3 id=&#34;the-switch&#34;&gt;The switch&lt;/h3&gt;
&lt;p&gt;Now let&amp;rsquo;s take a look at this same problem in Go land.  (For the sake
of argument let&amp;rsquo;s assume that PreparedStatement both exists in Go and
works much in the same way as its Java counterpart).&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;PreparedStatement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And again it is used like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;prepareStatement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;SELECT …&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;someValue&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s write the unit test:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;SetValueTest&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;testing&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;T&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;err&#34;&gt;??&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;dog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// todo - verify the statement received the value of &amp;#39;dog&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We&amp;rsquo;ve once again hit the same issue.  As with Java, we can use the
Go&amp;rsquo;s mock framework (which also uses reflection) or, as before, we can
try to roll our own.&lt;/p&gt;
&lt;p&gt;Approaching it directly we could build out a TestPreparedStatement as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;TestPreparedStatement&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tps&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;TestPreparedStatement&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parameterIndex&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;     &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;tps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But then we are going to have the same issue as we had with Java where
we also need to stub out all of the other 50+ methods in the
PreparedStatement interface which is just as wasteful as it was in
Java.  But we do have one other option.&lt;/p&gt;
&lt;h3 id=&#34;duck-typing&#34;&gt;Duck Typing&lt;/h3&gt;
&lt;p&gt;What if we didn&amp;rsquo;t stub out all of the other methods and instead
created a new interface which only contained the one method we are
using?  So something like:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StringSetter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;interface&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;parameterIndex&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, since our &lt;code&gt;TestPreparedStatement&lt;/code&gt; implements this one method it
is automatically a type of StringSetter.  But guess what, the object
returned by conn.prepareStatement is also now a type of StringSetter
since it too implements setString (by virtue of it being a
PreparedStatement which also requires the same method.)&lt;/p&gt;
&lt;p&gt;Now, we only need to change the signature of our setValue method to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;db&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Db&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setValue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;StringSetter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;stmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;setString&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is where the power of the duck typing comes into play as doing
this in Java would require modifying the definition of
PreparedStatement so it explicitly extend StringSetter, which is
virtually impossible.  Yet in Go this is trivial as we can assign
interfaces to objects by simply copying over the methods we want into
the interface.&lt;/p&gt;
&lt;p&gt;I found this to be a surprising feature about Go and is not something
I had considered before.  It feels really powerful and, to be honest,
I&amp;rsquo;m not yet sure if this is a good feature about the language or not,
but it is an interesting one.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Three Questions to Ask When Writing a New API</title>
      <link>https://hrakaroo.com/post/three-questions/</link>
      <pubDate>Thu, 06 Jun 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/three-questions/</guid>
      <description>&lt;p&gt;The following is &lt;a href=&#34;https://blog.newrelic.com/engineering/writing-api-three-questions/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;blog post&lt;/a&gt;
that was written from a speech I presented about questions to consider when writing an API.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Writing a functional API is relatively easy, but writing a good one
that’s functional and empowers your users takes planning and
patience. Designing a good API is about creating a sense of clarity
and simplicity—it’s the bridge between your intention and your users.&lt;/p&gt;
&lt;p&gt;Like most software development, building an API is a creative process;
it’s impossible to completely define a hard-and-fast set of rules that
will work in all cases. Nevertheless, three key questions—derived from
what I consider the key characteristics of a good API—can serve you
well as functional guideposts as you design and write your API:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is your API’s usage discoverable?&lt;/li&gt;
&lt;li&gt;Is your API composable?&lt;/li&gt;
&lt;li&gt;Is your API safe to use?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s take a closer look at each question.&lt;/p&gt;
&lt;h2 id=&#34;is-your-apis-usage-discoverable&#34;&gt;Is your API’s usage discoverable?&lt;/h2&gt;
&lt;p&gt;In his famous book, &lt;a href=&#34;https://www.amazon.com/Design-Everyday-Things-Revised-Expanded-ebook/dp/B00E257T6C&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;The Design of Everyday
Things&lt;/a&gt;,
Don Norman coined the term discoverability. &amp;ldquo;When we interact with a
product,&amp;rdquo; Norman wrote, &amp;ldquo;we need to figure out how to work it. This
means discovering what it does, how it works, and what operations are
possible.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Take doors, for example: We interact with these standard physical
objects every day. Often, based on the presence of affordances like
knobs, handles, and push bars, it’s pretty clear how to open or close
a door. But on occasion, a door’s design will suggest the opposite of
how it actually works, and, as a result, we require instructions
before we can properly use it. Just think of how many times you pulled
a handle that actually needed to be pushed.&lt;/p&gt;
&lt;p&gt;When we use a door the wrong way, we feel silly and stupid, but it’s
not our fault. Actually it’s the design that’s bad.&lt;/p&gt;
&lt;p&gt;Something similar can happen with a poorly designed API.&lt;/p&gt;
&lt;p&gt;Consider the last API you used. How did you learn to use it? Did you
read all the documentation first, or did you just jump right in? Maybe
you weren’t sure about all of the parameters, so you sent in null for
a few values and guessed at others. Did the API throw an error message
when you did something wrong, or did it fail silently without any
feedback?  Did the error message clearly define which parameters were
optional and which were not? Did you just keep plugging away until you
got it right?&lt;/p&gt;
&lt;p&gt;This is how most users will learn your API.&lt;/p&gt;
&lt;p&gt;Your users are going to learn just enough to bootstrap themselves, and
then they’ll figure the rest out as they go. With this fact in mind,
you can help them along the away by increasing your API’s
discoverability. You can do this through documentation; adhering to
conceptual models; and using concise, symmetrical language.&lt;/p&gt;
&lt;h3 id=&#34;assume-your-users-wont-read-the-documentationuntil-they-need-to&#34;&gt;Assume your users won’t read the documentation—until they need to&lt;/h3&gt;
&lt;p&gt;Just because your users won’t read your documentation doesn’t mean
that you don’t need to provide it. You definitely do. But don’t design
your API with the assumption that everyone will read the docs before
they use it.&lt;/p&gt;
&lt;p&gt;Some users would rather experiment than look up an answer in the
docs. Every time I use Java’s substring() method, for example, I can
never remember if the second value is an offset or a length, so I just
write a little program to try it out both ways. This is usually
quicker for me, and more fun, than looking up the answer.&lt;/p&gt;
&lt;p&gt;In many cases, users who’ve learned to distrust documentation won’t
read the docs anyway, at least not until they get
desperate. Documentation is notorious for being out of date or just
wrong. Now, this obviously isn’t true of all documentation, but think
of how many times you’ve consulted documentation—or a help system or
knowledge base—and found that either it provided answers that were
totally useless, or it didn’t provide any related answers at
all. Plenty of documentation does a poor job of anticipating the
questions users might ask or how they might ask them. Additionally,
even if users have a sense of what task they want to achieve, they may
lack the exact vocabulary or use different terms for that task than
the docs, which can make searching difficult.&lt;/p&gt;
&lt;p&gt;You should also provide plenty of examples in your
documentation—because users want them. Typically, examples are the
first things users look for when learning a new API. Only after they
gain a little context will they go look at the rest of the
documentation. Examples are how users come to understand your API as a
whole.&lt;/p&gt;
&lt;h3 id=&#34;create-a-conceptual-model-of-how-your-api-works&#34;&gt;Create a conceptual model of how your API works&lt;/h3&gt;
&lt;p&gt;Don Norman explains that a conceptual model is &amp;ldquo;an explanation,
usually highly simplified, of how something works.&amp;rdquo; Conceptual models
are not schematics, and they should relate to other known conceptual
models.&lt;/p&gt;
&lt;p&gt;A good example of a conceptual model is the file system structure used
on personal computers. File systems, like those on Mac and Windows
operating systems, were intentionally based on the concept of files
and folders that we were already familiar with in the physical
world. This made it easy for non-technical users to understand and
discover how to copy, store, and retrieve files on their PCs.&lt;/p&gt;
&lt;p&gt;Even today, Unix uses this conceptual model of files and folders
anytime a user attaches a device (e.g. a phone or external hard drive)
to an operating system, which has completely eliminated the need for
users to &amp;ldquo;discover&amp;rdquo; a new API every time they attach a device.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Objects&amp;rdquo; in object-oriented programming are another example of a
conceptual model. They’re specifically called objects so that we think
of them as self-defining entities. Just as a ball object on the
computer might support a bounce method, as well as other methods like
throw, a ball in real life, through its design, also supports bounce
and throw operations. In data-oriented programming, however, you don’t
get this conceptual model, so you’re more likely to have a bounce
function that will throw an error if you send it anything other than a
ball.&lt;/p&gt;
&lt;p&gt;Another example of working within conceptual models is the use of
&amp;ldquo;object&amp;rdquo; in object-oriented programming. In this programming model,
objects represent physical objects from the real world, such as
servers, databases, and load balancers, and developers create
relationships between those objects via APIs.&lt;/p&gt;
&lt;h3 id=&#34;use-clear-consistent-and-symmetrical-language&#34;&gt;Use clear, consistent, and symmetrical language&lt;/h3&gt;
&lt;p&gt;In addition to documenting your API, you should also develop and
publish a terminology dictionary for your API—and then use it
consistently. For example, I commonly see APIs use terms like host and
hostName, and account and accountId, almost interchangeably. Forcing
your users to guess what the right call might be, or constantly
changing the language, does not promote discoverability.&lt;/p&gt;
&lt;p&gt;Like conceptual models, symmetrical language helps users work with
your API with certain expectations in place. If your language is
symmetrical, an open operation will be balanced with a close, and an
add operation will be balanced with a delete.&lt;/p&gt;
&lt;p&gt;In Python, for example, you use pop to remove an element, so the
expectation would be that you’d use push to add an element, as that’s
how it works in most other languages. Instead, Python uses append… and
there’s plenty of Google search results from people confused by this
poor discoverability.&lt;/p&gt;
&lt;h2 id=&#34;is-your-api-composable&#34;&gt;Is your API composable?&lt;/h2&gt;
&lt;p&gt;When you build a composable API, you are letting your users select
components of the API and use them in whatever pattern they want.&lt;/p&gt;
&lt;p&gt;Small and composable methods are easier to describe and document than
larger methods that contain a long chain of steps and caveats. They’re
also easier to run regression and end-to-end tests against.&lt;/p&gt;
&lt;p&gt;Most importantly, though, employing composable components gives your
users the tools they need to build their own workflows with your
API. You can’t predict all your users’ needs, so don’t force them into
one execution pattern. Instead, create composable components and then
use your examples to show how to combine them into larger execution
patterns.&lt;/p&gt;
&lt;p&gt;For example, consider the following methods:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lastName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;vs.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setFirstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;setLastName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lastName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The second option is more composable than the first, as the second
method allows you to easily update the value for lastName. With the
first method you would first have to fetch the value of firstName so
you could send it back in with the new value for lastName.&lt;/p&gt;
&lt;p&gt;The second option is also more extensible, as you can easily add a
method to set the middle name:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setMiddleName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;middleName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, the second option is also 100% backwards compatible with
existing code. If you were to update the first method to&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;setName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;firstName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;middleName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lastName&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;you’d break the existing code.&lt;/p&gt;
&lt;p&gt;Both you and your users will undoubtedly enjoy the free backwards
compatibility, as building from smaller, composable components makes
it much easier to extend your API as it grows; and to continue
supporting support old operations alongside new ones.&lt;/p&gt;
&lt;h2 id=&#34;is-your-api-safe-to-use&#34;&gt;Is your API safe to use?&lt;/h2&gt;
&lt;p&gt;Ensuring that your API is safe to use—that it won’t behave differently
than users expect or break their workflows— is related to the
discoverability of an API. But safety is so important that I want to
call out the topic separately. When you publish your API, you create a
relationship with your users that should be based on trust and
transparency. Here’s how to make that happen:&lt;/p&gt;
&lt;h3 id=&#34;practice-the-principle-of-least-astonishment&#34;&gt;Practice the principle of least astonishment&lt;/h3&gt;
&lt;p&gt;The principle of least astonishment tells us that a component of a
system should behave in a way that most users will expect it to
behave. The behavior should not astonish or surprise users.&lt;/p&gt;
&lt;p&gt;The setDate method in GNU’s Coreutils, for example, surprises me every
time I use it because I expect a set method to set a value and not
alter it. If you set the year to any value less than 68, it
automatically adds 2000 to the value; and if you set any value between
68 and 100, it automatically adds 1900. Every time I use this method,
I’m astonished and have to re-read the documentation to make sure I’m
using it correctly.&lt;/p&gt;
&lt;h3 id=&#34;follow-the-contract&#34;&gt;Follow the contract&lt;/h3&gt;
&lt;p&gt;Don’t try to interpret what you think your user is trying to do. For
example, if your API expects a number, and the user provides a string,
don’t try to parse a number out of the string. You aren’t doing anyone
any favors: What happens when users enter an empty string: Is that 0
or null?&lt;/p&gt;
&lt;p&gt;Design your API so that it’s deterministic and strict.&lt;/p&gt;
&lt;h3 id=&#34;trust-nothing-and-fail-fast&#34;&gt;Trust nothing and fail fast&lt;/h3&gt;
&lt;p&gt;Similarly, your API should verify everything that users send, and
immediately fail on errors. More specifically, garbage-in should not
equal garbage-out. Garbage-in should fail. If your users are calling
your methods with incorrect values, they may be in discovery mode,
intentionally testing the boundaries and trying to figure out what is
possible.&lt;/p&gt;
&lt;p&gt;Help them understand what’s possible and what isn’t.&lt;/p&gt;
&lt;p&gt;Plan to version from the start and aggressively deprecate old versions
If you change the signature or external behavior of your API, version it.&lt;/p&gt;
&lt;p&gt;And when you do roll an API’s version forward, dedicate time and
resources to aggressively migrate users. If that’s not possible, try
to rewrite older versions so they proxy to the new
implementation. These steps will help avoid creating technical
debt—which, like financial debt, definitely accrues interest over
time. The longer an outdated version of your API sits around, the more
ingrained it becomes in your user base, and the harder it will be to
move users off of it. Set a migration date, and make it happen.&lt;/p&gt;
&lt;p&gt;If you release a version that is likely to change quickly, make that
fact explicit by tagging it as &amp;ldquo;incubating,&amp;rdquo; &amp;ldquo;unstable,&amp;rdquo; or &amp;ldquo;beta.&amp;rdquo;
This helps provide breathing room if you need to turn off old versions
of your API as you release new ones.&lt;/p&gt;
&lt;h3 id=&#34;separate-your-api-from-your-implementation&#34;&gt;Separate your API from your implementation&lt;/h3&gt;
&lt;p&gt;Finally, publish your API version separately from its
implementation. The implementation is likely to change faster than the
API, so don’t tie the two together.&lt;/p&gt;
&lt;p&gt;When versioning a library, for example, the API and its implementation
are in the same package, so you can’t help but release them at the
same time. But you can at least use semantic versioning to make it
clear which parts are backwards compatible.&lt;/p&gt;
&lt;p&gt;For a service, though, you can publish an API separately from its
implementation. In fact, there are plenty of tools, including Apache
Thrift, FlatBuffers, and Swagger, that allow you to write your API
separately. With these tools, you write your spec and then build your
implementation so that it implements the spec.&lt;/p&gt;
&lt;h3 id=&#34;nail-that-first-impression&#34;&gt;Nail that first impression&lt;/h3&gt;
&lt;p&gt;Your API is often a user’s first impression of your system. Spend time
on discoverability, composability, and safety to make sure that first
impression is a good one. Proper planning and design is critical to
the effectiveness and success of your API. Taking the time to think
things through will help to make your API a first-class feature—not a
mere afterthought or means to an end.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Slides</title>
      <link>https://hrakaroo.com/slides/example/</link>
      <pubDate>Tue, 05 Feb 2019 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/slides/example/</guid>
      <description>&lt;h1 id=&#34;create-slides-in-markdown-with-hugo-blox-builder&#34;&gt;Create slides in Markdown with Hugo Blox Builder&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://hugoblox.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hugo Blox Builder&lt;/a&gt; | &lt;a href=&#34;https://docs.hugoblox.com/content/slides/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Documentation&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;features&#34;&gt;Features&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Efficiently write slides in Markdown&lt;/li&gt;
&lt;li&gt;3-in-1: Create, Present, and Publish your slides&lt;/li&gt;
&lt;li&gt;Supports speaker notes&lt;/li&gt;
&lt;li&gt;Mobile friendly slides&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;controls&#34;&gt;Controls&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Next: &lt;code&gt;Right Arrow&lt;/code&gt; or &lt;code&gt;Space&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Previous: &lt;code&gt;Left Arrow&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Start: &lt;code&gt;Home&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Finish: &lt;code&gt;End&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Overview: &lt;code&gt;Esc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Speaker notes: &lt;code&gt;S&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Fullscreen: &lt;code&gt;F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Zoom: &lt;code&gt;Alt + Click&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://revealjs.com/pdf-export/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;PDF Export&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;code-highlighting&#34;&gt;Code Highlighting&lt;/h2&gt;
&lt;p&gt;Inline code: &lt;code&gt;variable&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Code block:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;porridge&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;blueberry&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;porridge&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;blueberry&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nb&#34;&gt;print&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Eating...&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;math&#34;&gt;Math&lt;/h2&gt;
&lt;p&gt;In-line math: $x + y = z$&lt;/p&gt;
&lt;p&gt;Block math:&lt;/p&gt;
&lt;p&gt;$$
f\left( x \right) = ;\frac{{2\left( {x + 4} \right)\left( {x - 4} \right)}}{{\left( {x + 4} \right)\left( {x + 1} \right)}}
$$&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;fragments&#34;&gt;Fragments&lt;/h2&gt;
&lt;p&gt;Make content appear incrementally&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{% fragment %}} One {{% /fragment %}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{% fragment %}} **Two** {{% /fragment %}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{% fragment %}} Three {{% /fragment %}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Press &lt;code&gt;Space&lt;/code&gt; to play!&lt;/p&gt;
&lt;span class=&#34;fragment &#34; &gt;
  One
&lt;/span&gt;
&lt;span class=&#34;fragment &#34; &gt;
  &lt;strong&gt;Two&lt;/strong&gt;
&lt;/span&gt;
&lt;span class=&#34;fragment &#34; &gt;
  Three
&lt;/span&gt;
&lt;hr&gt;
&lt;p&gt;A fragment can accept two optional parameters:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;class&lt;/code&gt;: use a custom style (requires definition in custom CSS)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;weight&lt;/code&gt;: sets the order in which a fragment appears&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;speaker-notes&#34;&gt;Speaker Notes&lt;/h2&gt;
&lt;p&gt;Add speaker notes to your presentation&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{% speaker_note %}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; Only the speaker can read these notes
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;-&lt;/span&gt; Press &lt;span class=&#34;sb&#34;&gt;`S`&lt;/span&gt; key to view
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  {{% /speaker_note %}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Press the &lt;code&gt;S&lt;/code&gt; key to view the speaker notes!&lt;/p&gt;
&lt;aside class=&#34;notes&#34;&gt;
  &lt;ul&gt;
&lt;li&gt;Only the speaker can read these notes&lt;/li&gt;
&lt;li&gt;Press &lt;code&gt;S&lt;/code&gt; key to view&lt;/li&gt;
&lt;/ul&gt;

&lt;/aside&gt;
&lt;hr&gt;
&lt;h2 id=&#34;themes&#34;&gt;Themes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;black: Black background, white text, blue links (default)&lt;/li&gt;
&lt;li&gt;white: White background, black text, blue links&lt;/li&gt;
&lt;li&gt;league: Gray background, white text, blue links&lt;/li&gt;
&lt;li&gt;beige: Beige background, dark text, brown links&lt;/li&gt;
&lt;li&gt;sky: Blue background, thin dark text, blue links&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;night: Black background, thick white text, orange links&lt;/li&gt;
&lt;li&gt;serif: Cappuccino background, gray text, brown links&lt;/li&gt;
&lt;li&gt;simple: White background, black text, blue links&lt;/li&gt;
&lt;li&gt;solarized: Cream-colored background, dark green text, blue links&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;

&lt;section data-noprocess data-shortcode-slide
  
      
      data-background-image=&#34;/media/boards.jpg&#34;
  &gt;

&lt;h2 id=&#34;custom-slide&#34;&gt;Custom Slide&lt;/h2&gt;
&lt;p&gt;Customize the slide style and background&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-markdown&#34; data-lang=&#34;markdown&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{&amp;lt; slide background-image=&amp;#34;/media/boards.jpg&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{&amp;lt; slide background-color=&amp;#34;&lt;span class=&#34;ni&#34;&gt;#0000FF&lt;/span&gt;&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;{{&amp;lt; slide class=&amp;#34;my-style&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;custom-css-example&#34;&gt;Custom CSS Example&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s make headers navy colored.&lt;/p&gt;
&lt;p&gt;Create &lt;code&gt;assets/css/reveal_custom.css&lt;/code&gt; with:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-css&#34; data-lang=&#34;css&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;reveal&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;h1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;reveal&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;h2&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;reveal&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;section&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;h3&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;k&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;navy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h1 id=&#34;questions&#34;&gt;Questions?&lt;/h1&gt;
&lt;p&gt;&lt;a href=&#34;https://discord.gg/z8wNYzb&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Ask&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://docs.hugoblox.com/content/slides/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Documentation&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>SQL is a terrible API</title>
      <link>https://hrakaroo.com/post/sql-terrible-api/</link>
      <pubDate>Thu, 01 Nov 2018 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/sql-terrible-api/</guid>
      <description>&lt;h3 id=&#34;a-long-time-ago&#34;&gt;A long time ago&amp;hellip;&lt;/h3&gt;
&lt;p&gt;In 1974 the first relational database was created and along with it a
new user interface language was developed to make it easy to query the
data. This language (or rather sub-language) was called Structured
English Query Language or SEQUEL. SEQUEL would later evolved into SQL
and become the preeminent language for querying relational
databases. In the 45+ years since it&amp;rsquo;s introduction SQL has evolved
slightly but it is still largely similar to the version introduced in
the 70s. What has changed during that time is how we interact with the
database. Today, SQL is used significantly more by applications
querying the database than by end users. This usage pattern has
created its own ecosystem of utilities and libraries which help
applications build SQL queries.  One of the more popular libraries for
building SQL queries in Java is &lt;a href=&#34;https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/querycriteria.html&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Hibernate&amp;rsquo;s Criteria Builder&lt;/a&gt;
which uses an annotation pre-processor and a builder pattern to
facilitate building SQL queries.  (Java&amp;rsquo;s Persistence API was based
heavily on Hibernate&amp;rsquo;s Criteria Builder). But, at the end of the day
the builder still generates a basic SQL string which is sent to the
target database, parsed and then executed. Hibernate is just one
example, but there are hundreds of other libraries that exist across
various languages which all seek to provide the same basic
functionality. To provide an API which abstracts away the actual
building of an SQL query.&lt;/p&gt;
&lt;p&gt;So why do these all tools exist? Is an SQL query so complicated to
construct that we need libraries and utilities to help us? In general,
no, most SQL queries are relatively straightforward to construct. The
problem is that SQL was designed for humans and not computers. The
inclusion of the word &amp;lsquo;English&amp;rsquo; in the original name was not by
chance, SQL was intended to be similar enough to English that it would
be self descriptive and would only require minimal transformation from
the spoken question to the actual query. SQL was not written to make
querying easy from other applications. In order to query a database
from an application, the application needs to build the SQL query
programmatically at run time, which means that all errors in the SQL
query string are also going to be discovered at run time.&lt;/p&gt;
&lt;h3 id=&#34;an-impedance-mismatch&#34;&gt;An Impedance Mismatch&lt;/h3&gt;
&lt;p&gt;Compiled languages offer an enormous advantage over interpreted
languages in that you can be confident that, if the application
compiles, then it does not contain any syntax errors. This assurance
removes an entire class of errors that exist in pure scripting
languages. Yet, by having an application generate SQL you are
re-introducing the possibility of a run time syntax
error. Furthermore, the errors which do get introduced are almost
always related to the construction of the SQL query rather than actual
column names or keywords in the query. Meaning the column names and
keywords are not as likely to be the source of syntax errors as they
don&amp;rsquo;t normally change based on the user request. For example, an
application which allows the searching of available flights is less
likely to return different a type of data depending on the destination
city. You may get back more or less result, but it will usually be the
same basic set of information. Syntax errors around the column names
and keywords are often found through basic testing and fixed.&lt;/p&gt;
&lt;p&gt;The more common (and harder to find) syntax errors are introduced in
the construction of the query itself.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Did you remember a space after the &amp;lsquo;SELECT&amp;rsquo; and &amp;lsquo;FROM&amp;rsquo; keywords?&lt;/li&gt;
&lt;li&gt;Did you join your select fields with a comma, but remember to not
include a comma after the last one?&lt;/li&gt;
&lt;li&gt;If this is the first filter then we need to add the &amp;lsquo;WHERE&amp;rsquo; keyword,
but if this is the second one we need to add the &amp;lsquo;AND&amp;rsquo; keyword, and
don&amp;rsquo;t forget about the parentheses.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These types of syntax errors are often introduced based on the users
search criteria and can be significantly more difficult to find with
basic testing due to the sheer number of permutations.  This is the
real advantage of using a helper library like Hibernate Criteria
Builder. They provide an assurance that if you use their libraries,
the generated SQL will be free of &lt;em&gt;construction syntax errors&lt;/em&gt;. This
assurance is most often achieved through massive test coverage and a
responsive development team which quickly patches any errors that are
found. It would be safe to say that in the 45+ years since the
introduction of SQL, millions of lines of code have been written in
various languages, all attempting to work around this same basic
problem. It&amp;rsquo;s a huge waste of intellectual effort for what is
essentially a self inflicted problem.&lt;/p&gt;
&lt;h3 id=&#34;where-to-go-from-here&#34;&gt;Where to go from here&lt;/h3&gt;
&lt;p&gt;SQL and similar English based DSLs (domain specific languages) are
immensely powerful for building complex queries quickly.  As an end
user, nothing is more frustrating than trying to build a complex
search criteria through a form based UI.  First enter the subject,
then select the predicate, then enter the object, then click the
plus sign to add another filter, rinse and repeat.  This becomes
incredibly tedious very quickly and makes building complex groupings
all but impossible.&lt;/p&gt;
&lt;p&gt;This is where an English based DSL really shines.  It allows a
seasoned user to rapidly create complex queries as they are doing it
interactively and have to do minimum mapping from what they are trying
to search for to the actual query they are running (just as SQL was
initially designed for).  But that is where the DSL should remain, as
a tool for the &lt;em&gt;end user&lt;/em&gt;. Do not just create an API which takes the
DSL directly as, at best, you will be recreating the same impedance
mismatch and likely forcing your users to create the same set of
&amp;ldquo;builder&amp;rdquo; libraries.&lt;/p&gt;
&lt;p&gt;Instead, define a flexible structure which can be used to describe a
query. JSON, XML or even one of the compressed protocols like Thrift
or Flatbuffers could work. Users can then create their query
programmatically and completely remove the risk of introducing a
construction based error because they forgot a space or included a
comma at the wrong location.  Your API can either accept this
structure directly for querying, or you can split it up and have
different endpoints accept different categories of queries based on
the structure of the return type.  Either way, creating a well defined
structure programmatically is straight forward and can easily be
supported from a multitude of languages.&lt;/p&gt;
&lt;p&gt;As for your UI, you are going to have to create a parser for your DSL
anyhow which is going to build an intermediary representation of your
query.  If you merge your intermediary representation with your defined
query structure it should be trivial to show the user what they would
need to send to the API in order to reproduce their query in the API.&lt;/p&gt;
&lt;p&gt;In this scenario your query language can continue to evolve separate
from your query structure.  You can even support other English based
DSLs as long as they can all generate the same query structure.  But
what ever you do, keep the two domains separate.  SQL is a terrible API.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Creating Simple and Effective Guidelines for Code Reviews</title>
      <link>https://hrakaroo.com/post/code-reviews/</link>
      <pubDate>Tue, 09 Oct 2018 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/code-reviews/</guid>
      <description>&lt;p&gt;The following is a &lt;a href=&#34;https://blog.newrelic.com/engineering/code-review-guidelines/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;blog post&lt;/a&gt;
that was written from a speech I gave about how to set the ground
rules for code reviews.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Following New Relic’s Project Upscale—an innovative reorganization
intended to make our development teams more autonomous—the engineering
organization formed several new teams, one of which was the New Relic
Database (NRDB) team. As the name implies, the NRDB team is
responsible for the development of our events database, which powers
the New Relic Insights tool as well as several other products.&lt;/p&gt;
&lt;p&gt;When we formed the NRDB team, it included several senior-level
software engineers. This was a highly skilled and very passionate
group of developers reviewing one another’s pull requests.&lt;/p&gt;
&lt;h2 id=&#34;when-passion-turns-toxic&#34;&gt;When passion turns toxic&lt;/h2&gt;
&lt;p&gt;Being passionate about your work is one of New Relic’s core values. In
this case, however, we may have experienced too much of a good thing:
our code reviews soon became collision points, and we increasingly
turned to passive-aggressive communications to settle our differences.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;Comments made during a code review&#34; srcset=&#34;
               /post/code-reviews/code_review_comments_hu_518e191e1df4761f.webp 400w,
               /post/code-reviews/code_review_comments_hu_9552cdb752183957.webp 760w,
               /post/code-reviews/code_review_comments_hu_d4fce5df8419933e.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/code-reviews/code_review_comments_hu_518e191e1df4761f.webp&#34;
               width=&#34;700&#34;
               height=&#34;337&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;In the example on the left, the reviewer left the PR in an in-between
state. They didn’t explicitly reject it, but they didn’t approve it
either. In the example on the right, the reviewer made a highly
subjective request, and the author just made the change, but from
their tone you can kind of guess that they didn’t appreciate the
feedback.&lt;/p&gt;
&lt;p&gt;As a result, the NRDB team’s developers grew increasingly frustrated,
team trust eroded, and several members (myself included) contemplated
switching to other teams. We were in trouble.&lt;/p&gt;
&lt;h2 id=&#34;refining-our-processand-saving-the-team&#34;&gt;Refining our process—and saving the team&lt;/h2&gt;
&lt;p&gt;We decided as a team to take a step back; we resolved to figure out
what was going on, why it was happening, and what we could do to fix
it. Since most of our frustration was tied to our code reviews, we
started by asking a simple question: how could we give one another
more effective and constructive feedback?&lt;/p&gt;
&lt;p&gt;We answered the question by developing four basic guidelines for code
reviews. We think you’ll find them useful, too, but before we spell
them out, we want to share the full story behind what happened to
divide our team and what was really as stake for us.&lt;/p&gt;
&lt;h2 id=&#34;a-flawed-approach-to-the-code-review-process&#34;&gt;A flawed approach to the code review process&lt;/h2&gt;
&lt;p&gt;Many of our challenges were related to the differences between
objective and subjective feedback in our code reviews. Being able to
differentiate clearly between these two types of feedback can be
critical to the success of a code review, and to the effectiveness of
a development team. In too many cases, we weren’t handling subjective
feedback in a constructive manner—in fact, just the opposite was true.&lt;/p&gt;
&lt;p&gt;We probably aren’t the only ones who struggle with this issue. Many
developers are trained from the start to downplay differences between
the two types of feedback. In fact, students in academic software
engineering programs rarely learn how to give or receive critical
feedback of any sort.&lt;/p&gt;
&lt;p&gt;When I went to school, this certainly was the case. The computer
science curriculum focused on algorithm analysis, data modeling, and
problem solving. Our instructors treated code review as a functional
quality-assurance task; they rarely presented it as a creative
process. Code review feedback tended to be straightforward: The code
either worked, or it didn’t. Because of this kind of training—or
rather, lack of training— many software engineers still treat all
aspects of code reviews as completely objective activities.&lt;/p&gt;
&lt;p&gt;It’s useful to contrast this approach with the one employed in an
academic creative writing program. There, instructors conduct
workshops that include training on how to give critical
feedback. Creative writing instructors understand that giving and
receiving critical feedback is an essential part of the creative
process. They also understand, however, that critical feedback can be
harmful and create resentment unless it is handled properly. The goal
is to provide feedback in a positive and constructive way that helps
to hone a writer’s ideas, enhance their creativity, and leave both
parties enriched by the process.&lt;/p&gt;
&lt;h2 id=&#34;the-struggle-over-subjectivity&#34;&gt;The struggle over subjectivity&lt;/h2&gt;
&lt;p&gt;Many facets of a code review, however, are not straightforward. In
particular, there are issues that demand subjective assessments for
which there are no “correct” answers. This is where the rigid emphasis
on code review as a totally objective activity, and the failure to
consider the creative nature of software development, can become a
problem.&lt;/p&gt;
&lt;p&gt;Many elements of a modern code review process are now fully
automated. Editors and IDEs will find syntax errors, evaluate Boolean
logic, and warn about infinite loops. As a result, the bugs that
survive are much harder to find, especially when you’re at the end of
the process and are just looking at a code snippet with limited
context.&lt;/p&gt;
&lt;p&gt;Editors and IDEs, however, can’t detect—or prevent developers from
focusing on—subjective issues such as confusing method names,
questionable style preferences, and bad variable formatting. And when
we dislike and disagree with what we find in such cases, we often
forget that these &amp;ldquo;flaws&amp;rdquo; are subjective matters of opinion—not
objective matters of fact.&lt;/p&gt;
&lt;p&gt;This approach also makes it easy to forget that a debate over
subjective issues during a code review can get emotional and heated
very quickly.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;Opinions made during code reviews&#34; srcset=&#34;
               /post/code-reviews/code_review_opinions_hu_953b9aba1d3fdd0f.webp 400w,
               /post/code-reviews/code_review_opinions_hu_2d0572476d6b8c6c.webp 760w,
               /post/code-reviews/code_review_opinions_hu_eaa1e4a1f9e3d731.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/code-reviews/code_review_opinions_hu_953b9aba1d3fdd0f.webp&#34;
               width=&#34;700&#34;
               height=&#34;304&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Some teams try to regulate this problem out of existence by creating
style guides that make objective rules out of subjective
preferences. This approach rarely succeeds: software development is
full of subjective choices, and there is no way to cover every
subjective choice that developers may face in the course of project.&lt;/p&gt;
&lt;p&gt;When a team lacks a clear communication channel for subjective
feedback, the problem gets even worse. Reviewers may mix their
subjective and objective comments without acknowledging the
differences; here too, the process can end in resentment, frustration,
and a breakdown in team communication.&lt;/p&gt;
&lt;h2 id=&#34;our-four-guidelines-for-code-reviews&#34;&gt;Our four guidelines for code reviews&lt;/h2&gt;
&lt;p&gt;This brings us back to the guidelines we developed to govern the
subjective elements of the NRDB team’s code review process.&lt;/p&gt;
&lt;p&gt;First, as a preliminary to our four guidelines, we agreed to define
&lt;em&gt;who is ultimately responsible for the correct execution of any code
changes.&lt;/em&gt; This was important to us because in a subjective debate, the
opinions of the person who has the ultimate responsibility—in other
words, verifying code execution— should carry the most weight. As a
result, we decided that &amp;ldquo;The author of the code change is responsible
for the correct execution of the change.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;This may seem obvious, but not all teams work that way. Some teams,
for example, treat the review process as a QA process where the
reviewer is ultimately responsible for verifying correct execution.&lt;/p&gt;
&lt;p&gt;We found that subjective comments were most often presented as
objective feedback at the pull request stage of the process. As a
result, this is where we focused our code review guidelines.&lt;/p&gt;
&lt;p&gt;In creating these rules, we laid a foundation for team members to
clearly identify what a code reviewer should look for, and how to give
both subjective and objective feedback. Here are the guidelines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The reviewer should identify errors that will cause an issue in production.&lt;/strong&gt;
It’s a code review, after all, so the reviewer should identify missing
semicolons, unending loops, or missing error handling. Reviewers
aren’t responsible for finding all such errors (that’s still the
responsibility of the author), but they should be on the lookout for
obvious issues that will break the system if they’re are deployed into
production. Such issues are a valid reason to block the pull request.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The reviewer should verify that the stated goal of the code change aligns with the changes being made.&lt;/strong&gt;
If the author submits a pull request that says they’re making changes
to the networking code of a service, reviewers should expect that all
of the changes are in and around the service’s networking code. This
seems obvious, but it’s no secret that developers have a tendency to
try to pack in multiple changes in such cases. This isn’t even
necessarily a wrong practice, as long as the changes are mostly
co-located. When you align a code change to its stated goal, however,
you make it easier to determine if the pull request potentially
submits any new bugs. Here, too, we agreed that failing to align the
code change with its stated goal would justify blocking the pull
request.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The reviewer should verify that any changes align with the team’s coding standards.&lt;/strong&gt;
I’ll cover this more in a bit, but as an example, if the team has
decided that all variables must use camel case, and the reviewer finds
a variable that does not use camel case, they should block the pull
request.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The reviewer should look for anything they personally disagree with.&lt;/strong&gt;
This guideline addresses any comment which the first three rules do
not cover. We want reviewers to give feedback, even if it’s not
covered by the first three rules. We didn’t want our guidelines to
suppress feedback, which is essential for how we learn from one
another. Because these comments are clearly subjective, however, we
agreed that they do not justify blocking the pull request.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To remove all confusion, we ask that reviewers specifically call out their comments as either blocking or non-blocking; and to add those comments as tags in their reviews. For example:&lt;/p&gt;
&lt;p&gt;Objective comments&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Blocking: You are missing a semicolon.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Blocking: This loop never ends.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Blocking: You are missing some error handling here&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Subjective comments&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;Non-blocking: Your method name is not clear enough.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Non-blocking: You should put the open curly brace on the line above.&amp;rdquo;&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Non-blocking: You should use camel case for your variable here and not snake case.&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;working-within-our-code-review-guidelines&#34;&gt;Working within our code review guidelines&lt;/h2&gt;
&lt;p&gt;As we adopted these guidelines, the team had the most difficulty with
the fourth one. Adopting this meant we had to accept two conditions:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The code our team produced did not need to be uniform.&lt;/strong&gt;
This meant overcoming a trend in our industry that says you should
strive to remove all fingerprints from your code that identifies who
wrote what part. We found the ROI on following this trend was pretty
low, and trying to do so just led us back into the same subjective
debate: If a developer writes code in a manner slightly different than
their peer would, does that mean the code is incorrect? Clearly, we
decided, that wasn’t the objective case.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If a reviewer adds non-blocking feedback, the author should take the time to consider it.&lt;/strong&gt;
Early on, some team members were concerned that authors would simply
ignore all non-blocking comments, as their code was no longer blocked
by subjective feedback. Our solution, then, was to reiterate that &amp;ldquo;we
trust our teammates.&amp;rdquo; If, as reviewers, we took the time to enter a
comment, we trusted that the author would take the time to read and
consider it.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;These both were contentious points, and the team spent a long time
debating them. But ultimately, we found that the only way to work
through these issues successfully is to live with the guidelines and
give them a chance.&lt;/p&gt;
&lt;h2 id=&#34;sponsoring-a-coding-standard&#34;&gt;Sponsoring a coding standard&lt;/h2&gt;
&lt;p&gt;So, what are a reviewer’s options if they see something which they
passionately feel shouldn’t be in the code, especially if their
concern isn’t an &amp;ldquo;objective&amp;rdquo; rule violation they can block on? For
such concerns, we agreed that a reviewer could choose to sponsor an
addition to the team’s coding standards.&lt;/p&gt;
&lt;p&gt;Every two weeks, we hold a retrospective meeting where team members
are welcome to suggest changes or additions to our coding
standards. There are two restrictions to this activity:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;We cannot describe coding standards in subjective language.&lt;/strong&gt;
For example, a sponsor can’t say, &amp;ldquo;variables must not be ambiguous,&amp;rdquo;
as ambiguity is subjective. But, the sponsor could add a standard that
states, &amp;ldquo;variables must use Hungarian notation,&amp;rdquo; as this is objective
and easily enforceable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;If you sponsor a coding standard, you must support it.&lt;/strong&gt;
The sponsor must provide documentation and training as needed. If
there was a plugin or other tool the team needs to installed, the
sponsor is responsible for supporting it. This restriction ensures the
sponsor is passionate about anything they want to add to the team’s
coding standards.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;finding-respect-and-compromise-in-code-reviews&#34;&gt;Finding respect and compromise in code reviews&lt;/h2&gt;
&lt;p&gt;After agreeing to these guidelines, we cleared all our existing coding
standards and started over. For the first few weeks it was hard to
break old habits, and we had to remind several team members to add the
blocking and non-blocking tags during their pull request reviews. But
once we got rolling with the new guidelines, we saw a number of
successes.&lt;/p&gt;
&lt;p&gt;First, by forcing reviewers to clearly identify those comments that
were subjective, we noticed a change in how reviewers phrased their
comments.Reviewers can no longer demand changes that meet their
preferences; instead, they must request changes politely, and explain
why they’re requesting the change. When we provide more explanation
and context in this manner we create an environment that makes it
easier for teammates to learn from one another. Plus, asking for
changes, rather than demanding them, shows respect and acknowledges
that the code’s author has valid feelings about their work, as well.&lt;/p&gt;
&lt;p&gt;We also noticed that when a reviewer did write a non-blocking comment
asking for a change, the author typically made the requested change or
came up with a compromise—even though the author had the option of
ignoring the comment. This demonstrates why asking for changes, rather
than demanding them, builds stronger teams: the author feels less
resentful, and the reviewer feels that the author genuinely
appreciated their feedback.&lt;/p&gt;
&lt;p&gt;We’ve identified a few other terrific benefits from this process. By
limiting the scope of what qualifies as a blocking comment, for
example, we reduced the time it took us to approve and merge changes,
which resulted in greater overall project velocity. We have also
reduced the time required to onboard new new team members and to get
them up to speed with our code review process.&lt;/p&gt;
&lt;p&gt;We have also updated our training materials to reflect our new code
review process: We distribute one page that documents our guidelines,
and another page that documents our coding standards. New team members
now know exactly what they should be looking for and how best to
communicate their suggestions.&lt;/p&gt;
&lt;p&gt;We also expected the number of coding standards to increase greatly as
reviewers sponsored new standards for items they could no longer block
on. At the beginning, we did adopt several new coding standards, but
after an initial burst, the number of new agreements fell off
significantly. We concluded that since reviewers felt that authors
were taking into consideration their subjective feedback, they did not
feel as motivated to &amp;ldquo;convert&amp;rdquo; them to objective constraints based on
their point of view.&lt;/p&gt;
&lt;p&gt;















&lt;figure  &gt;
  &lt;div class=&#34;d-flex justify-content-center&#34;&gt;
    &lt;div class=&#34;w-100&#34; &gt;&lt;img alt=&#34;The number of proposals made: expected vs. actual&#34; srcset=&#34;
               /post/code-reviews/code_review_proposals_hu_dcc8f5e008c0cbe8.webp 400w,
               /post/code-reviews/code_review_proposals_hu_f9d73bb91cab935b.webp 760w,
               /post/code-reviews/code_review_proposals_hu_9dd0eb393afb8b01.webp 1200w&#34;
               src=&#34;https://hrakaroo.com/post/code-reviews/code_review_proposals_hu_dcc8f5e008c0cbe8.webp&#34;
               width=&#34;700&#34;
               height=&#34;241&#34;
               loading=&#34;lazy&#34; data-zoomable /&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 id=&#34;these-guidelines-aid-in-team-autonomy&#34;&gt;These guidelines aid in team autonomy&lt;/h2&gt;
&lt;p&gt;The most important thing about these guidelines is that they support
team autonomy; in no way do these guidelines dictate which coding
standards teams should adopt. Teams are free to choose their own style
guides, and they decide how strict they want to to be. These
guidelines simply explain how to define coding standards and how
reviewers should look for and give feedback.&lt;/p&gt;
&lt;p&gt;We have come to appreciate the role that a strong and effective
feedback process can have on building team morale, increasing team
trust and communication, and improving development velocity. We
implemented guidelines to strengthen the feedback process and to
address issues that put the process at risk—and so far, I think we’re
getting exactly what we hoped to get from these improvements.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Trying to solve Java&#39;s Gordian Knot</title>
      <link>https://hrakaroo.com/post/sane-java-dependency-management/</link>
      <pubDate>Fri, 01 Jun 2018 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/sane-java-dependency-management/</guid>
      <description>&lt;p&gt;Java&amp;rsquo;s dependency management suffers from the dreaded diamond
dependency issue. And while this issue is not unique to Java, it is,
perhaps, more acute due to the precompiled nature of Java&amp;rsquo;s Jar files.&lt;/p&gt;
&lt;h2 id=&#34;a-quick-refresher&#34;&gt;A Quick Refresher&lt;/h2&gt;
&lt;p&gt;If you are unfamiliar with this issue, here is a
quick refresher:&lt;/p&gt;
&lt;p&gt;Suppose there exists a library which contains a useful method that
takes a string as its parameter:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;helpful&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;a&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This library is published to an artifactory as&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : common : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, suppose there exists two other libraries which both use this
helpful method&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Dog&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;method1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;helpful&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;dog&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Cat&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;method2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;helpful&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cat&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each of these are also published to artifactory as:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : dog : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : cat : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And each of these have a transitive dependency on Common.  Okay, now
you decide to build your service which uses Dog and Cat so your
dependency tree looks like&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : dog : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        com.hrakaroo : common : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : cat : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        com.hrakaroo : common : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All is good.  But now the folks who created Common come out with a new
version which changes the signature of helpful and adds a boolean
flag.  So the new version looks like&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;class Common {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    void helpful(String a, boolean flag) {
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        ...
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And knowing they are going to break some things publish this under a new version in artifactory&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : common : 2.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The folks who made the Cat library realize this new flag will fix a
bug they have had so they update to it as&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;class&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Cat&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;method2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Common&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;na&#34;&gt;helpful&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cat&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And publish it under&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : cat : 2.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But the Dog library makers don&amp;rsquo;t need the new functionality so they
don&amp;rsquo;t bother to update.&lt;/p&gt;
&lt;p&gt;And finally, you decide to update your service to use the newest version
of Cat which has the bug fix you need.  This changes
your dependency tree to:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : dog : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        com.hrakaroo : common : 1.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    com.hrakaroo : cat : 2.0
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        com.hrakaroo : common : 2.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In Java 8 you can not bring in the same dependency more than once with
different versions. There are two common ways to deal with this, and they
are both wrong.&lt;/p&gt;
&lt;p&gt;First, you can not do anything. (This is probably the most popular
solution.) In this situation gradle will pick one (usually the latest
version) and use that as its version. In this case it will select
&lt;code&gt;com.hrakaroo : common : 2.0&lt;/code&gt; which means that when your service calls
Dog.method1 it will give you a &lt;em&gt;runtime exception&lt;/em&gt; as the JVM will be
unable to find the definition for &lt;code&gt;helpful(String)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Or, if you are using gradle, you can use it&amp;rsquo;s force tag and force the
version down to &lt;code&gt;com.hrakaroo : common : 2.0&lt;/code&gt; which means that when
your service calls Cat.method2 it will give you a &lt;em&gt;runtime exception&lt;/em&gt; as
the JVM will be unable to find the definition for
&lt;code&gt;helpful(String, boolean)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The only &amp;ldquo;correct&amp;rdquo; solution here is to use gradle&amp;rsquo;s
failOnVersionConfict() which will fail to compile your project unless
both your dog and cat dependencies use the same version. This means
you will be forced to fix the issue before your project can compile,
but this may not be practical as a large project has lots of moving
parts and compatible versions may not be available.
Additionally, &lt;code&gt;failOnVersionConflict()&lt;/code&gt; doesn&amp;rsquo;t understand semantic
versioning so it will fail on PATCH level differences which often
makes this a very painful and non-practical solution.&lt;/p&gt;
&lt;p&gt;Most people just choose to go with the plug-and-pray approach where
they just hope they never call a code path which encounters a
definition which doesn&amp;rsquo;t exist.&lt;/p&gt;
&lt;h2 id=&#34;cutting-the-knot&#34;&gt;Cutting the knot&lt;/h2&gt;
&lt;p&gt;As the two easy solutions are wrong and the one correct solution is
impratical, the only real answer here is to avoid the problem
altogether. When building a library, limit your dependencies.&lt;/p&gt;
&lt;p&gt;Building a service requires a different approach from building a
shared library. From the technologies you use to the way you version
and test it are substantially different. Services are often just thin
wiring together of different frameworks and libraries while libraries
are more single tasked. And yet, they too often I don&amp;rsquo;t see people
appreciate this difference. Instead they hack together libraries like
they do services.&lt;/p&gt;
&lt;h3 id=&#34;stick-to-vanilla-java&#34;&gt;Stick to vanilla Java&lt;/h3&gt;
&lt;p&gt;I really like the Kotlin language, but I don&amp;rsquo;t think it has a place
(yet) in shared libraries. Part of what makes Kotlin fun are all
of the extension and infix libraries which are all packaged in the
kotlin stdlib dependency.  Any time you have more than two kotlin
library dependencies you are just about assured to have a version
conflict on the kotlin stdlib.  Libraries should be written in Java
to remove as many dependencies as possible.&lt;/p&gt;
&lt;h3 id=&#34;avoid-huge-common-or-utility-libraries&#34;&gt;Avoid huge common or utility libraries&lt;/h3&gt;
&lt;p&gt;Apache&amp;rsquo;s commons-lang3 is a fantastic library, but too often I&amp;rsquo;ve seen
brought in so that the developer can use the &lt;code&gt;StringUtils.join()&lt;/code&gt;
method. Not only is this method &lt;em&gt;trivial&lt;/em&gt; to write, but with Java 8
this can be done directly off the stream using
&lt;code&gt;.collect(Collectors.joining(...))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The same can be said for Google&amp;rsquo;s Guava library, which is an enormous
library that often makes non-compatible changes. In one library I
reviewed the author had brought in a dependency on Guava so that they
could use the &lt;code&gt;Preconditions&lt;/code&gt; checks. While I think this type of
defensive programming is good, the precondition checks can easily be
re-created in your own library.&lt;/p&gt;
&lt;h3 id=&#34;copy-and-attribute&#34;&gt;Copy and attribute&lt;/h3&gt;
&lt;p&gt;Providing the license allows it, it is also okay to simply copy
sections of your dependent library directly into your own shared
library and remove the dependency. Be sure to attribute where you got
it from, but otherwise copying small to medium sized dependencies is
okay.&lt;/p&gt;
&lt;h3 id=&#34;relocate-and-attribute&#34;&gt;Relocate and attribute&lt;/h3&gt;
&lt;p&gt;Finally, if all of the above are failing for you and, again if the
licensing allows it, you can use a tool like shadow/shade to relocate
a dependent library directly into you own library. These tools can
will rebuild your resulting jar so that your dependencies are
no longer transitive and all references to their old location have been
changed to somewhere in your package.  So, for example, you could
relocate &lt;code&gt;com.apache.commons&lt;/code&gt; to &lt;code&gt;com.hrakaroo.apache.commons&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;This will increase your jar size so it should really be used as the last
resort, but it will guarantee that no one can later change the depenency
version to something which is incompatable.&lt;/p&gt;
&lt;p&gt;Whatever your approach, you should take the time when creating a shared
library to minimize it&amp;rsquo;s transitive dependencies as much as possible.
By doing so you will help minimize the risk to developers which use your
library of creating their own diamon dependency nightmare.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>The Dance Recital</title>
      <link>https://hrakaroo.com/post/the-dance-recital/</link>
      <pubDate>Sun, 01 Oct 2017 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/the-dance-recital/</guid>
      <description>&lt;p&gt;The following is the speech I wrote for the Humorous Speaking Contest
for Toastmasters.  I placed first for the Division award but had to
back out of competing at the Area level due to a travel conflict.&lt;/p&gt;
&lt;p&gt;This speech is intended to be performed so it is not a formal writing.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;My wife, who usually took our 10 year old daughter to her dance
recitals, had been asked to speak at a conference that coincided with
a recital, so I was tasked with taking our daughter instead.  While
this might stress out some fathers, it so happened that I was an
experience dancer dad.  My work schedule was very flexible so I had
become primarily responsible for taking our daughter to her dance
classes.  I had spent hours at the dance studio and I’d seen it all.
From temper tantrums, to crying and yelling and throwing things… and
that was just the moms.  I wasn’t worried in the least.&lt;/p&gt;
&lt;p&gt;My first task was to take her to get her makeup for the recital.
&amp;ldquo;Black eyeliner, red lipstick.&amp;rdquo;  That seemed straightforward, but when
we got to the makeup counter we found out there isn&amp;rsquo;t just one black
eyeliner.  There’s regular black, black out, onyx black, darkest
black, ferocious black and blackest black, just to name a few. I must
not be the most color aware person as all the different shades of
black looked pretty similar to me.  There were also water proof
versions of everything which I ruled out instantly as we were
certainly not going swimming.  So we eventually narrowed it down to
darkest black and blackest black, which seems like an absurd
differentiation.  I don’t know how you get to darkest without also
being the blackest but far be it from me to question a multi-billion
dollar industry.  After much back and forth we finally settled on
blackest black as we figured that way no one could complain it wasn’t
black enough.  The lipstick was a bit easier as the instructions had
included a specific shade number.&lt;/p&gt;
&lt;p&gt;However, it wasn’t until the day of the recital that I realized
neither my daughter nor I had ever actually applied either eyeliner or
lipstick.  These days you could probably find an instruction video
on-line, but this was pre-YouTube days.  We decided to tackle the
eyeliner first.  For those that don&amp;rsquo;t know, an eyeliner pencil is
essentially a sharpened stick which you need to get really close to
the eye.  The entire concept seems barbaric and on my first attempt I
couldn’t bring myself to actually get right up next to her eye.  This
left her with a ring around her eye.  Knowing this wasn’t quite
correct I tried to fix it by coloring from the edge of the ring closer
in towards her eye.  Although in theory this worked, the end result
was far more goth than I had intended.&lt;/p&gt;
&lt;p&gt;The lipstick proved to be almost as challenging as the eyeliner,
although less dangerous.  To me, lipstick seems like a sticky crayon
and your lips have a natural line on them that separates them from the
rest of your face, so I figured if I just colored in between the lines
I would be good to go.  What I didn’t account for was that there is no
natural stopping point inside your mouth and I spent a good while in
front of the mirror making various lip poses trying to get a sense of
how far in I should color.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Dad!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;None of this was sitting well with my daughter who was becoming more
and more panicked with each facial gesture.  After deciding on a
general plan I proceeded to put on the lipstick much like you would
color in a coloring book, with rapid back-and-forth type motion.
Again, although in theory this worked it left her with a very thick
layer of lipstick on her lips.&lt;/p&gt;
&lt;p&gt;Still, with the makeup done we rushed off to the dance recital.  I got
her checked in and took my seat in the audience.&lt;/p&gt;
&lt;p&gt;The recital started normally but it didn’t take long before the hot
overhead lights, heavy costumes and general physical activity started
to take their toll.  Like most of the dancers, my daughter started to
sweat … heavily.  This is apparently what waterproof eyeliner is for.
Without it the sweat mixed with the thick ring I had applied and
proceeded to run down her cheeks, giving her an Alice Cooper look.
Meanwhile, the lipstick I had applied in her mouth had been rubbing
against her teeth turning them a frightening shade of red and causing
her smile to take on a menacing grimace.  This added new meaning to
the dance as it appeared my daughter was melting into a nightmarish
black swan, threatening to eat the other dancers.&lt;/p&gt;
&lt;p&gt;Stuck in the audience I could only watch with mounting horror as the
thick layer of lipstick started to work itself outside her lips and
smear around her face.  My first thought was &amp;ldquo;my wife is going to kill
me.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Thankfully some of the other moms took pity on her and tried to clean
her up between routines.  When my wife returned and saw the recital
photos she decided that she would never miss another dance recital
again.  My daughter recovered but has learned that dad is not who you
go to for makeup advice.  Still, whenever my kids ask me if they
should wear the blue outfit or the black one, I like to respond with
&amp;ldquo;now is that the darkest black one or the blackest black one?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Thank you.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Walking The Board</title>
      <link>https://hrakaroo.com/post/walking-the-board/</link>
      <pubDate>Sun, 09 Jul 2017 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/post/walking-the-board/</guid>
      <description>&lt;p&gt;In the traditional standup format everyone stands in a circle and each
person, in order, gives an update. The updates follow the
&lt;a href=&#34;https://www.agilealliance.org/glossary/three-questions/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;agile&lt;/a&gt;
format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What they did yesterday&lt;/li&gt;
&lt;li&gt;What their plans are for today&lt;/li&gt;
&lt;li&gt;Are there any blockers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have a number of problems with this format.&lt;/p&gt;
&lt;h3 id=&#34;context-switching&#34;&gt;Context Switching&lt;/h3&gt;
&lt;p&gt;People don’t usually stand in any particular order so the updates from
person to person often jump between the project(s) the team is working
on and support tasks. Additionally, if we’ve sized our tasks
correctly, then a single person could potentially be working on a new
task each day. This makes sense to the person giving the update, but
for everyone else, trying to track actual progress becomes
increasingly difficult.&lt;/p&gt;
&lt;h3 id=&#34;project-status&#34;&gt;Project Status&lt;/h3&gt;
&lt;p&gt;Related to context switching, it’s nearly impossible tell the overall
project status from this type of update. Even if you could figure out
how many tickets were closed, without looking at the kanban board it’s
nearly impossible to get even a sense of what is left todo on the
project. Plus, this format does nothing to encourage people to
actually update the kanban board, compounding the issue.&lt;/p&gt;
&lt;h3 id=&#34;public-speaking&#34;&gt;Public Speaking&lt;/h3&gt;
&lt;p&gt;Public speaking does not come naturally to everyone, even when
presenting just to the team. I’m much more relaxed with it now, but
there was a time when this type of setting would cause me enough
stress and I would ignore whatever was being said and just rehearse
what I was going to say. Then, after I gave my update, I would spend
rest of the standup obsessing over the things I had messed up or
forgotten to say.&lt;/p&gt;
&lt;h3 id=&#34;competition-for-busiest&#34;&gt;Competition for Busiest&lt;/h3&gt;
&lt;p&gt;Without focus, the standup can become a passive competition for who is
the busiest person.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;I’ve got a meeting with a customer today to talk over a feature they want added.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Well I’ve got two meetings today to talk over features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;I’ve got meetings pretty much all day long so I’m not likely going to get much else done.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It sounds silly, but it&amp;rsquo;s something I&amp;rsquo;ve seen happen at multiple
companies, especially if people are feeling insecure (imposter
syndrome). It can take the form of lamenting (but really bragging)
about the number of meetings, or making the tasks you completed sound
like herculean efforts. It&amp;rsquo;s pointless and unhealthy for the team.&lt;/p&gt;
&lt;h3 id=&#34;forced-update&#34;&gt;Forced Update&lt;/h3&gt;
&lt;p&gt;Finally, it forces everyone to talk when they may not want to. Maybe
they just got back from vacation and are still working at getting
caught up. There have also been times in my career where my personal
life has taken over and I’ve needed to take some time off, be it
medical or family related. At the update I don’t want to go into
details about what I’m dealing with. In fact, I may be at work
specifically to take a break from the other stresses going on. The
last thing I want to do is be forced to talk, or worse, explain why I
don’t want to talk.&lt;/p&gt;
&lt;p&gt;As an individual contributor I find this standup format stressful and
as a project lead and manager I find it unhelpful. Instead I prefer
using the standup time to walk the kanban board.&lt;/p&gt;
&lt;h2 id=&#34;walking-the-board&#34;&gt;Walking the board&lt;/h2&gt;
&lt;p&gt;With most ticket tracking systems you can use swim lanes to split up
your kanban board into project tasks and support tasks. Then, starting
with the top swim lane, go down the &lt;strong&gt;Done&lt;/strong&gt; column and each each
owner can give an update for their ticket. (This also avoids that
initial awkward moment of trying to figure out who should be the first
to speak.)&lt;/p&gt;
&lt;p&gt;Once the &lt;strong&gt;Done&lt;/strong&gt; column is finished, move to column to the left
(likely &lt;strong&gt;In Progress&lt;/strong&gt; or &lt;strong&gt;Blocked&lt;/strong&gt;) and repeat the
process. Continue until you are done with all &lt;em&gt;assigned&lt;/em&gt; tickets for
that swim lane. Rinse and repeat for the next swim lane until all
updates are done. The context switching is much less and the team can
focus on what is being said instead of trying to memorize what they
are going to say.&lt;/p&gt;
&lt;p&gt;(Since &lt;strong&gt;Done&lt;/strong&gt; is often an end state and you don’t necessarily want
to re-walk this entire list every time, some teams will create a
&lt;strong&gt;Closed&lt;/strong&gt; column and use the standup as an opportunity to move tasks
from &lt;strong&gt;Closed&lt;/strong&gt; to &lt;strong&gt;Done&lt;/strong&gt;.)&lt;/p&gt;
&lt;h3 id=&#34;closing-comments&#34;&gt;Closing comments&lt;/h3&gt;
&lt;p&gt;One critique of this format is that it is possible for a team member
not working on a task to feel left out. There are a couple of ways to
handle this: First although standup is suppose to be project focused,
so I’ve always stressed that if you don’t have any updates for the
project it is okay to not talk. (This assumes your team is a safe
space where individuals won’t feel like they are being judged if they
don’t talk.) But, if an individual &lt;em&gt;really&lt;/em&gt; wants to explain to the
team why they haven’t picked up any tasks, they should save that for
the post-standup discussion. If, in the rare case, an individual is
checking out and not picking up any tasks, that is something the
manager should bring up during their 1:1 and not in front of the team.&lt;/p&gt;
&lt;p&gt;As a project lead and manager I find walking the board to be
significantly more useful as I can tell exactly the progress of the
project (as I’m looking at the tickets) and I can also be sure that
tickets for the project are up to date. As an individual contributor I
don’t feel like I have to prepare as much for this standup and I can
just attend and focus on what everyone else is saying.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>One Seat Open</title>
      <link>https://hrakaroo.com/project/one-seat-open/</link>
      <pubDate>Wed, 27 Apr 2016 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/one-seat-open/</guid>
      <description>&lt;p&gt;One Seat Open, an invitation system for limited seating events.&lt;/p&gt;
&lt;h2 id=&#34;history&#34;&gt;History&lt;/h2&gt;
&lt;p&gt;This project primarily came from a personal frustration in hosting my
own game nights.  Most invitation systems I researched were open ended
(no wait list support) or were so chock full of ads that I could
barely navigate them.  At the same time I was also looking for a
project with which to learn front end development as I have been
mostly focused on backed development. So this seemed like a perfect
opportunity to build my own service.&lt;/p&gt;
&lt;h2 id=&#34;development&#34;&gt;Development&lt;/h2&gt;
&lt;p&gt;After researching several frontend frameworks I eventually settled on
Vue.js with the Vuetify plugin for several reasons.  Vue.js was
established enough to have good tutorials, training videos and a
robust presence on Stack Overflow for questions.  It also had an easy
ramp up and it didn&amp;rsquo;t originate from Facebook.  (Petty, I know, but I
have a personal distain of Facebook).&lt;/p&gt;
&lt;h2 id=&#34;status&#34;&gt;Status&lt;/h2&gt;
&lt;p&gt;In November of 2019 I finished the initial version of and started
to host a couple of game nights on the system.  It was working pretty
well but then the pandemic hit and I halted my home games, which halted
my motiviation for working on the service.  So it sat mostly idle until
recently when I decided to start working on it again.&lt;/p&gt;
&lt;p&gt;I initially wrote this service in Kotlin while I was doing a lot of
Kotlin development at work. After changing companies I switched to
primarily using Go for my work development and haven&amp;rsquo;t really done
a lot of Kotlin development since then.&lt;/p&gt;
&lt;p&gt;I still really like the Kotlin language, but it is much easier to
develop in a language you are currently using. Plus, I often use my
personal projects as a way to explore different solutions to technical
challenges at work, so aligning the two can be useful.&lt;/p&gt;
&lt;p&gt;All that said, I&amp;rsquo;ve decided to rewrite the project completely in
Go with Vue3, but still using the Vuetify plugin. However, I&amp;rsquo;ve decided
to keep this code base closed source for the time being.  I&amp;rsquo;m not 100%
sure yet what I want to do with  it once/if I finish it and want to
keep my options open for now.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Rebound</title>
      <link>https://hrakaroo.com/project/rebound/</link>
      <pubDate>Sat, 10 Feb 2007 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/rebound/</guid>
      <description></description>
    </item>
    
    <item>
      <title>Soda Water Ray Tracer</title>
      <link>https://hrakaroo.com/project/soda-water-ray-tracer/</link>
      <pubDate>Sun, 21 Nov 2004 00:00:00 +0000</pubDate>
      <guid>https://hrakaroo.com/project/soda-water-ray-tracer/</guid>
      <description></description>
    </item>
    
  </channel>
</rss>
