  <?xml version="1.0"?>
  <rss version="2.0">
    <channel>
      <title>Hugh Bien</title>
      <link>https://hughbien.com</link>
      <description>Programming Crystal, JavaScript, Unix, Vim.</description>  <item>
    <title>Responsive Web Design</title>
    <link>https://hughbien.com/responsive-web-design</link>
    <pubDate>Sun, 13 Jan 2013 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <p>A responsive website delivers an appropriate design to the device that accesses
it.  A mobile phone screen would render a different design than a large desktop
monitor.</p>
<p>A quick test to determine whether a site is responsive or not is to resize your
browser window.  Does the layout noticeably change?  If it does, it mights the
website design is responsive.</p>
<p>Try resizing the browser window on these sites:</p>
<ul>
<li><a href="http://www.barackobama.com/">Barack Obama</a></li>
<li><a href="http://twitter.github.com/bootstrap/">Bootstrap</a></li>
<li><a href="http://www.bostonglobe.com/">The Boston Globe</a></li>
<li><a href="http://zurb.com/">ZURB</a></li>
</ul>
<h2>Media Queries</h2>
<p>You've probably heard of <a href="http://www.w3.org/TR/css3-mediaqueries/">CSS Media Queries</a>
before.  Media queries allow developers to conditionally deliver CSS:</p>
<pre><code class="language-css">@media print {
  .no-print { display: none; }
}
</code></pre>
<p>The above snippet will hide elements with class <code>no-print</code> for visitors printing
out a website.  These elements will still be present on the screen.</p>
<p>Turns out, media queries can use the device width as a condition:</p>
<pre><code class="language-css">@media screen and (max-width: 320px) {
  /* targets screen widths &lt;= 320px */
  .desktop-only { display: none; }
  .mobile-only { display: block; }
}

@media screen and (min-width: 321px) and (max-width: 500px) {
  /* 321px &lt;= screen width &lt;= 500px */
}
</code></pre>
<p><code>min-width</code> and <code>max-width</code> are the most widely used for responsive web design,
but there are a few other useful ones:</p>
<ul>
<li><code>orientation</code>, which can be <code>portrait</code> or <code>landscape</code></li>
<li><code>aspect-ratio</code>, a width to height ratio in this format <code>1280/720</code></li>
<li><code>color</code>, device bit depth</li>
</ul>
<h2>The Viewport</h2>
<p>Although a mobile phone's screen size may be 320px wide, it still tries to
render a scaled down version of the page at 980px.  For example, the
<a href="http://www.nytimes.com/">New York Times</a> will render on mobile phones as it
does on desktops — just with everything scaled smaller.</p>
<p>In order for media queries to work correctly, you'll have to force the browser
to render a non-scaled version.  This can be done with the <code>meta</code> tag:</p>
<pre><code class="language-html">&lt;meta name=&quot;viewport&quot; content=&quot;initial-scale=1.0,width=device-width&quot; /&gt;
</code></pre>
<p>The above says don't try to scale this website and use the exact device width
for rendering.  Now our media queries will work as expected.</p>
<h2>Flexible Layout</h2>
<p>I tend to use CSS inside <code>max-width</code> media queries to override a site's default
CSS.  A common approach is to have a fixed width layout for larger screens and
a fluid width layout for smaller screens:</p>
<pre><code class="language-css">/* fixed width 960px and centered website */
body { text-align: center; }
#frame { width: 960px; margin: 0 auto; text-align: left; }

/* make it fluid on smaller screens */
@media screen and (max-width: 960px) {
  #frame { width: auto; margin: 0; }
}
</code></pre>
<p>There are a lot of layout tweaks you can do for smaller screens.  Unfloat images
where it makes sense.  Wrap long lines in code blocks to prevent horizontal
scroll bars.  Superfluous elements can disappear.</p>
<pre><code class="language-css">@media screen and (max-width: 320px) {
  img.previously-floated-image { float: none; }
  pre.long-code { white-space: pre-wrap; }
  .superfluous-element { display: none; }
}
</code></pre>
<h2>Flexible Fonts</h2>
<p>I like to use larger fonts on smaller screens.  Instead of overriding each
individual element in a media query, it's easier to use the
<a href="http://www.alistapart.com/articles/elastic/">elastic method</a>.</p>
<p>For the elastic method, the <code>body</code>'s font size is set using <code>px</code>.  All other
elements whose font size differs should use <code>em</code>:</p>
<pre><code class="language-css">body { font-size: 14px; }
h1 { font-size: 2em; }  /* 2 x 14px = 28px */
h2 { font-size: 1.5em; } /* 1.5 x 14px = 21px */
</code></pre>
<p>Now the media query can simply change a single element's font size instead of
dozens of font sizes:</p>
<pre><code class="language-css">@media screen and (max-width: 320px) {
  body { font-size: 18px; }
  /* other elements using em are now based off of 18px */
}
</code></pre>
<h2>Some Considerations</h2>
<p>Consider using <strong>larger target areas</strong> on smaller screens.  Desktops and laptops
have the luxury of precise cursors.  Mobile phones and tablets viewers use their
less precise fingers.</p>
<p>Along with losing the mouse cursor, <strong>all hover interactions are loss</strong>.  Does
your site use a lot of tooltips?  You'll have to figure out a way to teach users
without using tooltips.</p>
<p>Earlier, I made the assumption that a smaller width meant low bandwidth.
There are a lot of <strong>assumptions based on viewport width</strong> we can make, but it's
best to back up these assumptions with web analytics.</p>
<p><strong>Is it a good business decision to make a mobile friendly version?</strong>  Before
you start, make sure a significant portion of your viewers are visiting your
site with a small screen.</p>
<h2>Further Reading</h2>
<p>Hands down, my favorite book on this subject is
<a href="http://www.abookapart.com/products/responsive-web-design">Responsive Web Design by Ethan Marcotte</a>.  It's the perfect balance of information and brevity.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Bootstrapping Design Review</title>
    <link>https://hughbien.com/bootstrapping-design-review</link>
    <pubDate>Mon, 25 Feb 2013 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <p><a href="http://bootstrappingdesign.com/">Bootstrapping Design by Jarrod Drysdale</a> is a
concise design guide for bootstrappers and hackers.  I enjoyed the
pragmatic approach — just enough theory mixed with a large amount
of practical tips.</p>
<p>Jarrod covers a lot in 155 pages:</p>
<ul>
<li>layout</li>
<li>visual hierarchy</li>
<li>proximity/space</li>
<li>typography</li>
<li>color</li>
<li>design process</li>
<li>stealing</li>
<li>miscellaneous tips</li>
<li>evaluating your own design</li>
</ul>
<h2>Some Tips</h2>
<p>The book is full of useful tips, but here were some of my favorites.</p>
<p>Stick to conventions — a ton of sites already use the standard 3-section
layout with a header, main area, and sidebar.  Visitors are already used to it,
can you use it too?</p>
<p>Align everything — whether elements are consecutive or have hundreds of
items between them, they still need to be aligned.</p>
<p>Either contrast A LOT or don't contrast at all — design gets awkward when
there are small breaks from repetition of sizes/colors/fonts/layout.  Only
break repetition if it's your intention for an element to stand out.</p>
<p>Proximity affects pacing — break apart elements for some breathing room.
Use adequate line heights for readability.</p>
<p>Try sticking to two fonts — one serif and one sans-serif for contrast,
otherwise it's awkward when two different fonts are somewhat similar.  Use
one for headlines and the other for long texts.</p>
<h2>Further Reading</h2>
<p>It's ideal for anyone who wants to roll their own design, whether that's for a
software UI or a sales website.  You can get the book at
<a href="http://bootstrappingdesign.com">Bootstrapping Design</a>.</p>
<p>Another classic book on the same subject is <a href="http://amzn.to/WXmFd1">The Non-Designer's Design Book</a>.
It includes more theory and foundation, but is still pragmatic for developers
who want to learn design for making stuff.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Podcasts for Bootstrappers</title>
    <link>https://hughbien.com/podcasts-for-bootstrappers</link>
    <pubDate>Sat, 20 Apr 2013 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>Waiting for the Caltrain twice a day was getting repetitive and I needed
something to keep me busy.  My last on-site contract was in Palo Alto, which
meant a 1.5 hour commute by light rail and train.  Why not try listening to
a podcast?  And then I was hooked.</p>
<h2>Product People</h2>
<p>Want to launch a product?  Learn from product authors who have done it before.
Hosted by Justin Jackson and Kyle Fox,
<a href="http://productpeople.tv/">Product People</a> interviews people who make their
living building beautiful products.</p>
<p>Each interview is split into two digestible episodes.  The first is a life
story and the second has actionable tips.</p>
<p>I cannot recommend this podcast enough.   Their show consistently features
prominent product bootstrappers with great advice:</p>
<ul>
<li>Patrick McKenzie (<a href="http://productpeople.tv/2012/12/19/patio11-part1/">Part 1</a>, <a href="http://productpeople.tv/2012/12/26/patio11-part2/">Part 2</a>)</li>
<li>Sacha Greif (<a href="http://productpeople.tv/2013/01/02/sacha-greif-part1/">Part 1</a>, <a href="http://productpeople.tv/2013/01/09/ep08-sacha-greif-2/">Part 2</a>)</li>
<li>Rob Walling (<a href="http://productpeople.tv/2013/01/16/ep09-rob-walling-part1/">Part 1</a>, <a href="http://productpeople.tv/2013/01/23/ep10-rob-walling-part2/">Part 2</a>)</li>
<li>Brennan Dunn (<a href="http://productpeople.tv/2013/01/30/ep11-brennan-dunn-part1/">Part 1</a>, <a href="http://productpeople.tv/2013/02/06/ep12-brennan-dunn-part2/">Part 2</a>)</li>
<li>Jason Fried (<a href="http://productpeople.tv/2013/02/27/ep15-jason-fried/">Play</a>)</li>
<li>Nathan Barry (<a href="http://productpeople.tv/2013/03/06/ep16-nathan-barry-part1/">Part 1</a>, <a href="http://productpeople.tv/2013/03/13/ep17-nathan-barry-part-2/">Part 2</a>)</li>
</ul>
<h2>Kalzumeus Podcast</h2>
<p>Quality over quantity.  That's what comes to mind when I think of Patrick McKenzie
and Keith Perhac's
<a href="http://www.kalzumeus.com/category/podcasts/feed/">Kalzumeus Podcast</a>.</p>
<p>If you're on
<a href="http://news.ycombinator.com">Hacker News</a>, you probably know Patrick McKenzie
by his handle — patio11.  Actually, he probably replied to one of your
comments on HN.</p>
<p>Topics include pricing, business, marketing, products, consulting, and more.
Here were my favorite episodes — practically all of them:</p>
<ul>
<li><a href="http://www.kalzumeus.com/2012/05/18/kalzumeus-podcast-ep-2-with-amy-hoy-pricing-products-and-passion/">Amy Hoy: Pricing, Product, And Passion</a></li>
<li><a href="http://www.kalzumeus.com/2012/10/10/kalzumeus-podcast-3-growing-consulting-practices-with-brennan-dunn/">Brennan Dunn: Growing Consulting Practices</a></li>
<li><a href="http://www.kalzumeus.com/2012/09/17/ramit-sethi-and-patrick-mckenzie-on-getting-your-first-consulting-client/">Ramit Sethi: Getting Your First Consulting Client</a></li>
<li><a href="http://www.kalzumeus.com/2012/09/21/ramit-sethi-and-patrick-mckenzie-on-why-your-customers-would-be-happier-if-you-charged-more/">Ramit Sethi: Why Your Customers Would Be Happier If You Charged More</a></li>
</ul>
<h2>Startups for the Rest of Us</h2>
<p>Rob Walling and Mike Taber have over one hundred episodes worth of tips for
launching software products over at
<a href="http://www.startupsfortherestofus.com/">Startups for the Rest of Us</a>.</p>
<p>They're both expert micropreneurs — each has their own personal conglomerate of
products ranging from auditing software to a wedding website builder.</p>
<p>Both hosts also built their personal conglomerates via bootstrapping.  They
started out doing consulting work and slowly transitioned into launching products.
Listen to their stories:</p>
<ul>
<li><a href="http://www.startupsfortherestofus.com/episodes/episode-53-how-we-left-our-jobs-part-1">How Mike Taber Left His Job</a></li>
<li><a href="http://www.startupsfortherestofus.com/episodes/episode-54-how-we-left-our-jobs-part-2">How Rob Walling Left His Job</a></li>
</ul>
<h2>Start Small, Stay Small</h2>
<p><a href="http://www.startupbook.net/">Start Small, Stay Small</a> isn't a podcast.  It's an
ebook/audiobook for developers who want to launch profitable software
businesses.</p>
<p>Written and narrated by Rob Walling, this audiobook covers:</p>
<ol>
<li>What's a micropreneur and what's bootstrapping?</li>
<li>How to effectively use niches to your advantage.</li>
<li>How to make a sales website.</li>
<li>How to market a niche product.</li>
<li>How to outsource to give yourself more time.</li>
</ol>
<h2>TechZing Podcast</h2>
<p><a href="http://techzinglive.com/">TechZing</a> is an informal chat show for hackers —
with hosts Justin Vincent and Jason Roberts.</p>
<p>This show isn't specific to bootstrappers, but they have several interesting episodes:</p>
<ul>
<li><a href="http://techzinglive.com/page/479/79-tz-interview-%E2%80%93-patrick-mckenzie-optimize-this">Patrick McKenzie: The Long Tail of Optimization</a></li>
<li><a href="http://techzinglive.com/page/563/94-tz-interview-amy-hoy-how-to-build-a-product-empire">Amy Hoy: How to Build a Product Empire</a></li>
<li><a href="http://techzinglive.com/page/683/111-tz-panel-amy-hoy-patrick-mckenzie">Amy Hoy and Patrick McKenzie: Audiences, SEO, and Design</a></li>
<li><a href="http://techzinglive.com/page/257/techzing-56-rob-walling-the-micropreneur-academy">Rob Walling: How to Dramatically Increase Your Odds</a></li>
<li><a href="http://techzinglive.com/page/1226/217-tz-interview-rob-walling-hittail-drip-and-so-much-more">Rob Walling: Success with HitTail</a></li>
<li><a href="http://techzinglive.com/page/722/122-tz-interview-ruben-gomez-bidsketch">Ruben Gamez: Lessons Learned Building Bidsketch</a></li>
<li><a href="http://techzinglive.com/page/46/techzing-4-the-sicilian-valley-bada-boom-bada-bing">Peldi Guilizzoni: Balsamiq</a></li>
<li><a href="http://techzinglive.com/page/1080/190-tz-interview-ted-pitts-harry-hollander-moraware">Ted Pitts and Harry Hollander: Bootstrapping Moraware</a></li>
<li><a href="http://techzinglive.com/page/1169/206-tz-interview-corey-mass-the-birdy">Corey Maass: Bootstrapping The Birdy</a></li>
<li><a href="http://techzinglive.com/page/187/techzing-38-pete-michaud-retired-at-25">Pete Michaud: Retired at 25</a></li>
</ul>
<h2>The Business of Freelancing Podcast</h2>
<p>What does freelancing have to do with bootstrapping?  Freelancing/consulting
is a common strategy used for income while bootstrapping a product.</p>
<p><a href="http://brennandunn.com/category/podcast/">The Business of Freelancing Podcast</a>
by Brennan Dunn (and up until recently Eric Davis) is packed with business
techniques and tips.  Both hosts have used freelancing/consulting as their
main source of income while they launched ebooks and a SaaS product.</p>
<p>Be sure to check out:</p>
<ul>
<li><a href="http://brennandunn.com/episode-003-pricing-methods/">Episode 3: Pricing Methods</a></li>
<li><a href="http://brennandunn.com/episode-004-nathan-barry/">Episode 4: Nathan Barry</a></li>
<li><a href="http://brennandunn.com/episode-005-content-marketing/">Episode 5: Content Marketing</a></li>
<li><a href="http://brennandunn.com/episode-007-obie-fernandez/">Episode 7: Obie Fernandez</a></li>
</ul>
<p><strong>At the end of the day, your product launch won't happen by listening to podcasts.</strong>
These podcast hosts and interviewees give terrific and actionable advise.  It's
your job to execute.  Good luck!</p>

      ]]>
    </description>
  </item>  <item>
    <title>SEO Tactics</title>
    <link>https://hughbien.com/seo-tactics</link>
    <pubDate>Mon, 13 May 2013 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>Search engines have been a terrific source of traffic for a few of my sites.
Small tweaks resulted in slightly more traffic.  Here are a few SEO
tactics:</p>
<h2>Keywords in Titles, Headers, and URL Paths</h2>
<p>Perhaps the most important on page factor is the text between your <code>&lt;title&gt;</code> and
<code>&lt;h1&gt;</code> tags.  Make sure your target keywords are part of the page title and
headers.  Also make sure the title/headers aren't overloaded with too many
keywords.</p>
<p>The URL path should include targeted keywords.  It's better to use a flat
hierarchy instead of a nested one.</p>
<pre><code>http://example.com/kitchen-knives/
</code></pre>
<p>is better than:</p>
<pre><code>http://example.com/blog/2013/5/kitchen-knives/
</code></pre>
<h2>Trailing Slash Consistency</h2>
<p>According to search engines, these two URLs are considered two separate resources:</p>
<pre><code>http://example.com/kitchen-knives
http://example.com/kitchen-knives/
</code></pre>
<p>The only difference being the trailing slash.  If backlinks are split between
both URLs, it's essentially splitting the ranking in half.  Choose one URL
structure and stick with it.</p>
<p>Use a <code>301</code> permanent redirect in case someone links to the wrong page.  A <code>301</code>
permanent redirect also tells search engines that the URLs point to the same
resource and should be ranked as such.</p>
<h2>Crawler Hints</h2>
<p>Use microformats — they're useful to get relevant information onto search
engine result pages.  If your site includes rich snippets for reviews, Google
will show stars in their search results.  Learn more about <a href="http://support.google.com/webmasters/bin/answer.py?hl=en&amp;answer=99170">rich
snippets</a>.</p>
<p>Use meta description — although meta tags don't effect rankings, meta
descriptions are sometimes used in search result descriptions.
Google doesn't always use the <code>&lt;meta name=&quot;description&quot;&gt;</code> text, it's only used when the text is deemed relevant to
the search phrase.</p>
<p>Use sitemaps — see the <a href="http://www.sitemaps.org/">Sitemap Specification</a>
for details.  Many blog engines have plugins to automatically generate sitemaps
for you.</p>
<h2>Put Something Up as Early as Possible</h2>
<p>Before your website is finished, buy the domain and put up a landing page
immediately because website age matters in rankings.  In some industries, the
<a href="http://en.wikipedia.org/wiki/Sandbox_effect">Google Sandbox Effect</a> can really
hurt.  It's best to let your site brew.</p>
<h2>Get Backlinks from Relevant Sites</h2>
<p>Find relevant websites and ask them to talk about your product.  This works best
if you have something to offer to their audience, like a coupon code or a free
giveaway.</p>
<p>Some blogs may accept guest posts. At the beginning or end of a guest post,
you'll be given some &quot;about the author&quot; space.  Here's where you can get a
backlink to your website.  Make sure the anchor text includes the targeted
keywords.</p>
<p>Directories like <a href="http://ezinearticles.com/">EzineArticles</a> accepts articles
about almost anything.  In return for writing an article, they give you a
resource box where you can link back to your website.</p>
<h2>Site Directories</h2>
<p>Site directories were used before search engines became the popular way to find
content.  Websites listed in site directories get a large boost in rankings.
<a href="http://www.dmoz.org/">Dmoz</a> is a popular directory.  The <a href="http://dir.yahoo.com/?skw=directory">Yahoo
Directory</a> is another one, but has a fee.</p>
<h2>Content Marketing</h2>
<p>Start a blog and write useful content.  With good content, your audience will
naturally link to you.  This is the best thing you can do to acquire backlinks.</p>
<p>Content marketing requires an initial spark of backlinks before it can grow, so
it's a good idea to use some of the above tips first.</p>
<p>Before you write any content, use
<a href="https://adwords.google.com/o/Targeting/Explorer?__c=1000000000&amp;__u=1000000000&amp;ideaRequestType=KEYWORD_IDEAS">Google's Keyword Tool</a>
to make sure people are actually searching for your targeted keywords.  Check the <code>[Exact]</code> match
type option in the left column.</p>
<p>Check out the competition for your target keywords.  For example, search for:</p>
<pre><code>allintitle: &quot;kitchen knives&quot;
</code></pre>
<p>to see how many websites are currently targeting the same keywords.</p>
<h2>Target the Long Tail</h2>
<p>Pick long-tail keyword phrases which contain more words, have a smaller number
of monthly searches, but also have less competition.</p>
<p>The phrase &quot;best kitchen knives&quot; has less monthly searches than &quot;kitchen
knives&quot;, but it'd be easier to rank for it.  Use a <a href="http://www.hittail.com/">long tail keyword tool like
HitTail</a>, which automatically tells you what to write
about to acquire traffic.</p>
<h2>Content Tips</h2>
<p>Write evergreen content.  Evergreen content means content that ages well.
It's content that will be relevant years later.</p>
<p>Publish everywhere! If you have the time and resources, consider releasing
content on: YouTube, podcasts, app stores, Amazon marketplace, and more.  People
are searching for content everywhere.</p>
<p>As a bonus, content hosted on <code>youtube.com</code> or <code>amazon.com</code> will rank better on
Google compared to your own domain name.  This is because YouTube and Amazon
have more authority than your site.</p>
<h2>Good Luck!</h2>
<p><strong>Remember that SEO is a game of momentum.</strong>  Getting started is daunting, but
each tweak will improve your rankings a bit.  With each small improvement, your
momentum builds and ranking becomes easier.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Thyme Pomodoro Timer</title>
    <link>https://hughbien.com/thyme-pomodoro-timer</link>
    <pubDate>Mon, 05 Aug 2013 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>I've started using the <a href="http://pomodorotechnique.com/">Pomodoro Technique</a>
as a productivity booster.</p>
<p>For fun, I also ended up making a console Pomodoro Timer called
<a href="https://hughbien.com/thyme/">Thyme</a>.  There's tmux integration baked right in.  You can
also extend it with Ruby.</p>
<p>Get it with:</p>
<pre><code>$ gem install thyme
</code></pre>
<p>Watch a timer in your tmux status bar:</p>
<pre><code class="language-ruby"># vim ~/.thymerc
set :tmux, true

# vim ~/.tmux.conf
set-option -g status-right '#(cat ~/.thyme-tmux)'
set-option -g status-interval 1
</code></pre>
<p>The <code>~/.thymerc</code> file actually runs any Ruby source code.  Thyme has pre/post
hooks, so you can run arbitrary code:</p>
<pre><code class="language-ruby">before do
  `mplayer ~/flight-of-the-bumble-bee.mp3 &amp;`
end

after do |seconds_left|
  `notify-send -u critical &quot;Thyme's up!&quot;`
end
</code></pre>
<p>There's also an <code>option</code> method, in case you wanted to add new options to the
<code>thyme</code> binary:</p>
<pre><code class="language-ruby">option :t, :today, 'open today sheet' do
  `vim ~/.thyme_today.md &lt; \`tty\` &gt; \`tty\``
end
</code></pre>
<p>The source code is available via <a href="https://github.com/hughbien/thyme">GitHub</a>.
Hope you enjoy it!</p>

      ]]>
    </description>
  </item>  <item>
    <title>A History of Computing</title>
    <link>https://hughbien.com/a-history-of-computing</link>
    <pubDate>Wed, 01 Oct 2014 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>Less than two months from today, <a href="http://en.wikipedia.org/wiki/The_Imitation_Game">The Imitation Game</a>
is set to release in the US!  I'm excited to see Benedict Cumberbatch — of Sherlock fame —
play Alan Turing.  This got me thinking about the history of computers and wondering about the first
computer.  This led to a fun evening of reading Wikipedia.</p>
<p>Here's a brief history of computing.</p>
<h2>Charles Babbage's Analytical Engine</h2>
<p>1822 — Charles Babbage proposed an idea to the Royal Astronomical Society of London,
what if he could automate a computer's job?</p>
<p>During this time, a &quot;computer&quot; was a person who computed mathematical calculations.  Teams were
assembled for tedious work.  The results were placed into a huge data table, for later usage.  The
earliest known computers calculated data for dealing with celestial objects — e.g. timing
Halley's Comet.  But it wasn't a prestigious job.  It was the opposite.</p>
<blockquote>
<p>The human computer is supposed to be following fixed rules; he has no authority to deviate from
them in any detail.  <span>— Alan Turing</span></p>
</blockquote>
<p>Babbage's idea intrigued the British government, who also employed human computers.  Labor was
expensive and error prone.  An automated device would solve both problems.  The government funded
Babbage's idea and he began work immediately on the <strong>Difference Engine</strong>.</p>
<p>Babbage designed the Difference Engine to be a mechanical device that tabulated data for polynomial
functions, e.g. y = x<sup>2</sup> + 4x + 2.  Mechanical columns would be used as input, which you
could rotate to be any decimal number.  A hand crank would spin the gears to produce an output.</p>
<p>While designing his Difference Engine, he realized that a more general purpose machine could be
made.  Instead of inputting only numbers as data, he could input both instructions and data.  The
instructions could be delivered via punch cards — a technique taken from mechanical looms of
the time.  He called this second machine the <strong>Analytical Engine</strong>, which is widely regarded as the
beginning of computers as we see them now.</p>
<p>Although designed, Babbage was never able to complete construction of his two engines.  The
machinery was too complicated for its time and funding was eventually withdrawn.</p>
<h2>Ada Lovelace's Algorithm</h2>
<p>1833 — Ada Lovelace is introduced to Charles Babbage through a mutual friend.  He shows
his work and they quickly become friends.</p>
<p>Lovelace was a mathematician and logician.  In 1842, Lovelace was hired to translate a French paper,
a transcript of a seminar given by Babbage, to English.  She included her own notes with the
translation.</p>
<blockquote>
<p>The Analytical Engine might act upon other things besides number, were objects found whose mutual
fundamental relations could be expressed by those of the abstract science of operations...
<span>— Ada Lovelace</span></p>
</blockquote>
<p>Included in her notes were instructions for the Analytical Engine.  It was an algorithm for
computing the Bernoulli numbers — the first algorithm specifically made for a computer, or
the first computer program.</p>
<p>Through her work as a mathematician, Ada Lovelace becomes the world's first computer programmer.</p>
<h2>Claude Shannon's Digital Circuits</h2>
<p>1937 — Claude Shannon finishes his master's thesis at MIT.  His paper, <em>A Symbolic Analysis of
Relay and Switching Circuits</em>, introduced the world to digital circuits.</p>
<p>As an undergraduate, Shannon took a course on boolean algebra — a branch of logic that dealt
with the values true/false and operations on those values.  If you're a software programmer, you're
all too familiar with boolean logic.  We use it everyday.  If you're not a software programmer,
you've probably heard about how computers work with binary — ones and zeros.  Those ones/zeros
can also represent true/false.</p>
<p>Shannon showed how you could arrange electrical switches to perform boolean algebra, the underlaying
concept of digital circuits.  It later turned out that using digital circuits to perform math was
much more reliable than the mechanical/analog approach.</p>
<h2>Konrad Zuse's Z3</h2>
<p>1938 — Konrad Zuse had been designing and building his Z1 machine for the past 3 years in
Berlin.  Like Babbage's engines, input and output were specified using decimal numbers.  Unlike
Babbage's design, the input was translated to binary before calculations were performed.</p>
<p>Unfortunately, the Z1 was unreliable when used.  Its mechanical parts required precise
synchronization, which turned out to be unpredictable.  Zuse uses his newly found knowledge to
create successors to his machine.</p>
<p>In 1941, Zuse finishes construction of the Z3.  Whereas the Analytical Engine was never completed,
the Z3 was the world's first working programmable binary computer.</p>
<p>In 1943, Zuse's machines would be destroyed during the Allied air raids.</p>
<h2>Alan Turing's Turing Machine</h2>
<p>1939 — exactly one day after the United Kingdom entered World War II, Alan Turing steps foot
onto Bletchley Park.  He begins his cryptography work to decode Germany's encrypted messages.</p>
<p>After World War I, Germany began encoding secret messages using a mechanical device called the
Enigma machine.  It was a electro-mechanical device with a keyboard for input.  The Enigma's
appearance resembled a typewriter in a briefcase.</p>
<p>Turing and his team quickly developed a machine to decrypt German messages, which they called the
Bombe.  By the end of World War II, over 200 Bombes were constructed.  Winston Churchill credited
Turing as the single largest contributor to the Allies' victory.  But his work during the war wasn't
his only contribution.  Perhaps even a larger contribution was the <strong>Turing Machine</strong>.</p>
<p>Let's rewind a few years to 1936.  Turing publishes his paper, <em>On Computable Numbers, with an
Application to the Entscheidungsproblem</em>, that introduces the world to an &quot;a-machine&quot; (it wasn't
renamed to Turing machine until years later).</p>
<p>The Turing machine is a concept, not a physical reality.  It consists of:</p>
<ul>
<li>tape, which the machine operates on by reading/writing symbols onto it</li>
<li>state register, stores the current state of the machine</li>
<li>table, instructions for the machine to execute at any given state</li>
</ul>
<p>What Turing describes isn't actually a computer but the act of computation itself.  The concept is
so important in computer science that the term &quot;Turing complete&quot; was created.  Any system is Turing
complete if it can simulate a Turing machine.  For example, a programming language is considered
complete if it can manipulate memory and has conditional logic.</p>
<p>Before the World War II, computers were constructed for a single purpose.  But after the war,
efforts switched to building a Turing complete, general purpose computer.  Turing himself attempted
to build one in 1945, but it was never finished.</p>
<h2>The United States Army's ENIAC</h2>
<p>1947 — the ENIAC was switched on for the first time.  It was the first electronic, Turing
complete computer.  It took 3 years and $500,000+ to construct.</p>
<p>The ENIAC, or Electronic Numerical Integrator And Computer, was primarily designed to calculate
mathematical tables used in military artillery.  It was built using thousands of vacuum tubes and
crystal diodes.  Punch cards were used for input and output.</p>
<p>Programming the ENIAC was incredibly complex compared to today's standards.  After creating an
algorithm, a team of engineers would have to translate the instructions by flipping switches and
re-routing cables on the machine.  The process usually took days.</p>
<p>The ENIAC operated for 8 years until October 2, 1955.</p>
<h2>So... Which Was the First Computer?</h2>
<p>It depends on how you define a computer.</p>
<p>Charles Babbage early work on the Analytical Engine could be considered the first computer, except
it was never completed.  If you're ever in Mountain View, I highly recommend visiting the <a href="http://www.computerhistory.org/">Computer
History Museum</a> where they finished construction of Babbage's
Difference Engine.</p>
<p>Konrad Zuse's Z3 is often cited as the world's first computer.  It was fully operational and
programmable.  Except it wasn't fully electronic and wasn't designed to be Turing complete.  Later
on, in 1998, it was proved to be Turing complete using a few hacks.</p>
<p>The ENIAC could also be considered the first computer.  It was functional, electronic, and
Turing complete.  What more could you ask for?</p>
<p>But if anyone asks which was the first computer, just tell them it was the abacus.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Raise and Rescue</title>
    <link>https://hughbien.com/raise-and-rescue</link>
    <pubDate>Thu, 14 Jan 2016 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <p>Exception handling lets developers create a separate flow for special cases.
It keeps code clean. For example:</p>
<pre><code class="language-ruby">def print_file(filename)
  if File.exist?(filename)
    if File.readable?(filename)
      File.open(filename) { |f| puts(f.read) }
    else
      puts &quot;Can't read file: #{filename}&quot;
    end
  else
    puts &quot;File doesn't exist: #{filename}&quot;
  end
end
</code></pre>
<p>The method's original intent is hidden! Let's extract the error handling:</p>
<pre><code class="language-ruby">def print_file(filename)
  File.open(filename) { |f| puts(f.read) }
rescue Errno::ENOENT
  puts &quot;File doesn't exist: #{filename}&quot;
rescue Errno::EACCES
  puts &quot;Can't read file: #{filename}&quot;
end
</code></pre>
<p>The special cases are moved to the bottom and intent becomes clear. We use the basics of exceptions
every day. If we get to know the internals a bit, we might learn new tricks.</p>
<p>Let's deep dive into Ruby's exceptionally useful world of error handling.</p>
<h2>How Does Raise Work?</h2>
<p><code>raise</code> is just a method on <code>Kernel</code>, which is a module that gets included by the base class <code>Object</code>.
That's why you have access to it everywhere. Since it's just a method, you can override it.
Let's look at the three ways to raise:</p>
<pre><code class="language-ruby">raise
raise(string)
raise(exception_class_or_obj, optional_message, optional_stack_trace)
</code></pre>
<p>Calling without any arguments will default to raising a <code>RuntimeError</code>. If you're in the context of
another error it will re-raise:</p>
<pre><code class="language-ruby">begin
  method_that_raises(ArgumentError)
rescue ArgumentError =&gt; error
  logger.info(&quot;Error: #{error}&quot;)
  raise # re-raises the argument error
end
</code></pre>
<p>Calling with a string will instantiate a new <code>RuntimeError</code> and set the error message. It's useful
for some quick debugging.</p>
<p>Raise is much more useful when you provide context, such as an exception class/object and message:</p>
<pre><code class="language-ruby">raise ArgumentError.new(&quot;must be positive integer but was provided: #{num}&quot;)
raise ArgumentError, &quot;must be positive integer but was provided: #{num}&quot;
raise ArgumentError, &quot;must be positive integer but was provided: #{num}&quot;, caller(0)
</code></pre>
<p>The three lines above are identical. That last argument is a stack trace — Ruby uses an array
of strings. <code>caller(0)</code> will provide the current stack trace.</p>
<p>Raise doesn't explicitly instantiate a new exception object. Instead, it calls <code>#exception</code>:</p>
<pre><code class="language-ruby">def raise(exception_class_or_obj, message, stack_trace)
  error = exception_class_or_obj.exception(message)
  # ...
end
</code></pre>
<p>After being raised, program execution is bubbled up to the matching rescue block.</p>
<h2>How Does Rescue Work?</h2>
<p><code>rescue</code> attempts to match with a raised exception. Rescue blocks should be short. Its intent is
to handle the exception and bring your code back to a good state. If that's not possible, fail with
an explicit message.</p>
<p>Whereas raise defaults to <code>RuntimeError</code>, rescue defaults to <code>StandardError</code>. The two lines below
are identical:</p>
<pre><code class="language-ruby">rescue
rescue StandardError
</code></pre>
<p>Explicitly passing an exception will handle that class and its children. <code>RuntimeError</code> is a child
of <code>StandardError</code>, so the default rescue will handle it.</p>
<p>It's usually frowned upon to <code>rescue Exception</code>. <code>Exception</code> is the base class of all exceptions
in Ruby, including system errors such as <code>SignalException</code>. Rescuing it means users won't be able
to <code>Ctrl-C</code> out of your program.</p>
<p>Internally, Ruby uses the <code>===</code> class method to determine if an exception should be rescued or not.
You can actually define your own handler:</p>
<pre><code class="language-ruby">module CustomExceptionHandler
  def self.===(exception)
    exception.message =~ /42/
  end
end

begin
  raise RuntimeError, &quot;answer to the universe: 42&quot;
rescue CustomExceptionHandler =&gt; error
  # handle error
end
</code></pre>
<h2>Some Useful Patterns</h2>
<p><strong>Guard your method with raises near the top.</strong> This provides clear preconditions to callers:</p>
<pre><code class="language-ruby">def my_picky_method(left, right)
  raise ArgumentError(&quot;#{left} must be less than #{right}&quot;) if left &gt;= right
  # ...
end
</code></pre>
<p><strong>Keep error handling near the bottom.</strong> This lets your method's intent stay clear and prioritizes
the normal flow of execution:</p>
<pre><code class="language-ruby">def my_clear_method(key)
  process_some_data(key)
rescue KeyError =&gt; error
  logger.info(&quot;Unexpected key: #{error.message}&quot;)
rescue TypeError =&gt; error
  logger.info(&quot;Unexpected type: #{error.message}&quot;)
end
</code></pre>
<p><strong>Define your own exceptions.</strong> Think about creating a hierarchy of custom exceptions for your
application. You'll allow more precision for exception handling and uncaught exceptions will have
much better explanations. Also consider providing more context, such as a user id:</p>
<pre><code class="language-ruby">class AppBaseError &lt; StandardError; end;
class APIError &lt; AppBaseError; end;
class ClientError &lt; AppBaseError; end;
class ServerError &lt; AppBaseError; end;
class UserNotFoundError &lt; AppBaseError
  attr_reader :user_id
  def initialize(message='User not found', user_id=nil)
    @user_id = user_id
    super(message)
  end
end
</code></pre>
<p><strong>Implement <code>#exception</code> for some syntactic sugar.</strong> Remember that <code>raise</code> works with any object
or class that defines the <code>#exception</code> method:</p>
<pre><code class="language-ruby">class APIRequest
  def exception(message)
    error = APIError.new(message)
    error.request = self
    error
  end
end

request = APIRequest.new
request.do_something()
raise request if request.retries &gt; 10
</code></pre>
<p><strong>Override raise.</strong> Raise is just a method which can be overridden like any other. Think about which
contexts this might be useful in:</p>
<pre><code class="language-ruby">module RaiseLogger
  def raise(exception_class_or_obj=RuntimeError, message=nil, trace=nil)
    logger.info(exception_class_or_obj)
    super
  end
end

class UsersController
  include RaiseLogger
  # ...
end
</code></pre>
<p>Learning the internals of exception handling helps us create useful patterns. It's also fun! But at
the end of the day, raise and rescue are just tools. We use them to write cleaner code.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Setting Up Nginx with Ansible</title>
    <link>https://hughbien.com/nginx-with-ansible</link>
    <pubDate>Mon, 12 Jun 2017 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>During most of my career, I almost never setup a server from scratch. I'm usually building on top of
others' Ansible playbooks. I've always felt a gaping hole in knowledge in terms of sysadmin and ops
tasks.</p>
<p>So two years ago, I picked up <a href="https://book.serversforhackers.com">Servers for Hackers</a> which took a
terrific bottom up approach on how to setup and run a server. I've been using it as a reference
to setup servers for my side projects, I highly recommend it.</p>
<p>Here's what I used to get Nginx up and running, with SSL, for both static sites and applications
needing a reverse proxy. I'm using Ubuntu 16 LTS.</p>
<h2>Site Configuration</h2>
<p>I keep these tasks under a <code>webserver</code> role.</p>
<p>For the <code>site.yml</code> file, I keep a list of sites for each host group under the <code>sites</code> and
<code>sites_nginx</code> vars. The latter maps Nginx templates to their destination.</p>
<pre><code>- hosts: example
  roles:
    - webserver
  vars:
    sites:
      - example.com
    sites_nginx:
      - { src: &quot;nginx.conf&quot;, dest: &quot;nginx.conf&quot; }
      - { src: &quot;nginx-example.conf&quot;, dest: &quot;sites-available/example.com&quot; }
</code></pre>
<h2>Let's Encrypt SSL Tasks</h2>
<p><a href="https://letsencrypt.org">Let's Encrypt</a> is amazing! I can get a free SSL certificate, automate its
installation, and even automate renewal.</p>
<p>In <code>roles/webserver/tasks/ssl.yml</code>:</p>
<pre><code>- name: install apt-transport-https
  apt: pkg=apt-transport-https state=present update_cache=true

- name: generate dhparams file
  command: openssl dhparam -out /etc/ssl/certs/dhparam.pem 4096
  args:
    creates: /etc/ssl/certs/dhparam.pem

- name: install certbot
  apt: pkg=certbot state=present update_cache=true

- name: install python3-certbot-nginx
  apt: pkg=python3-certbot-nginx state=present update_cache=true

- name: check if certificate exists
  stat:
    path: /etc/letsencrypt/live//cert.pem
  register: letsencrypt_cert
  with_items: &quot;&quot;

- name: stop nginx for certbot
  service: name=nginx state=stopped
  with_items: &quot;&quot;
  when: item.stat.exists == False

- name: generate new certificate
  shell: &quot;certbot certonly --standalone --noninteractive --agree-tos --rsa-key-size 4096 --email YOUR_EMAIL_ADDRESS@EXAMPLE.COM -d &quot;
  with_items: &quot;&quot;
  when: item.stat.exists == False

- name: start nginx for certbot
  service: name=nginx state=started
  with_items: &quot;&quot;
  when: item.stat.exists == False

- name: add crontab to renew certificates
  cron: name=&quot;renew ssl&quot; minute=&quot;30&quot; hour=&quot;8&quot; weekday=&quot;0&quot; job=&quot;certbot renew --pre-hook \&quot;service nginx stop\&quot; --post-hook \&quot;service nginx start\&quot; &gt;&gt; /var/log/le-renew.log&quot;
</code></pre>
<p>This installs <code>certbot</code> and its prerequisites, generates the private key and SSL cert, and adds
a cronjob to renew the certificate. Thank you, Let's Encrypt!</p>
<p>Using a 4096 key size will take longer to generate, but is a good trade-off for security.</p>
<h2>Nginx Handler and Tasks</h2>
<p>Make sure Nginx is started with <code>roles/webserver/handlers/nginx.yml</code>:</p>
<pre><code>- name: start nginx
  service: name=nginx state=started
</code></pre>
<p>Then for <code>roles/webserver/tasks/nginx.yml</code>:</p>
<pre><code>- name: install nginx
  apt: pkg=nginx state=present update_cache=true
  notify:
    - start nginx

- name: remove default sites-available and sites-enabled
  file: path=/etc/nginx//default state=absent
  with_items:
    - { directory: 'sites-available' }
    - { directory: 'sites-enabled' }

- name: setup nginx configurations
  copy: src=../files/ dest=/etc/nginx/ owner=root group=root mode=644
  with_items: &quot;&quot;

- name: setup sites-enabled
  file: src=/etc/nginx/sites-available/
        dest=/etc/nginx/sites-enabled/
        state=link
        owner=root
        group=root
        force=yes
  with_items: &quot;&quot;
</code></pre>
<p>This will install Nginx and copy over the configuration file for each site. It follows the standard
pattern of keeping configurations in <code>sites-available</code> directory and linking them from <code>sites-enabled</code>.
I keep one main <code>nginx.conf</code> along with a different configuration for each site.</p>
<h2>Nginx Conf</h2>
<p>For the standard <code>roles/webserver/files/nginx.conf</code>:</p>
<pre><code>user www-data;
worker_processes 1; # usually one per core
pid /run/nginx.pid;

events {
  worker_connections 1024;
}

http {
  # Defaults
  sendfile on;
  tcp_nopush on;
  tcp_nodelay on;
  client_body_timeout 12;
  client_header_timeout 12;
  keepalive_timeout 15;
  send_timeout 10;
  types_hash_max_size 2048;
  include /etc/nginx/mime.types;
  default_type application/octet-stream;

  # Logging
  access_log /var/log/nginx/access.log;
  error_log /var/log/nginx/error.log;

  # Compression
  gzip on;
  gzip_comp_level  2;
  gzip_min_length  1000;
  gzip_proxied     expired no-cache no-store private auth;
  gzip_types       text/plain application/x-javascript text/xml text/css application/xml;

  # Virtual Hosts
  include /etc/nginx/conf.d/*.conf;
  include /etc/nginx/sites-enabled/*;
}
</code></pre>
<p>For individual sites, the configuration should live at <code>roles/webserver/files/nginx-example.conf</code>.
A server block should do a permanent 301 redirect to SSL (handling non-SSL connections):</p>
<pre><code>server {
  listen 80 default_server;
  listen [::]:80 default_server;
  server_name example.com;

  root /usr/share/nginx/html;
  index index.html;

  location ~ /.well-known {
    allow all;
  }

  location / {
    return 301 https://$host$request_uri;
  }
}
</code></pre>
<p>The next server block should handle SSL connections. I've added some lines to handle trailing slashes,
caching asset files (I usually fingerprint these), and handling 405/500 errors:</p>
<pre><code>server {
  listen 443 ssl;
  listen [::]:443 ssl;
  server_name example.com;

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_prefer_server_ciphers on;
  ssl_dhparam /etc/ssl/certs/dhparam.pem;
  ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
  ssl_session_timeout 1d;
  ssl_session_cache shared:SSL:50m;
  ssl_stapling on;
  ssl_stapling_verify on;

  # to remove trailing slash
  rewrite ^(/.*)\.html(\?.*)?$ $1$2 permanent;
  rewrite ^/([^.]*)/$ /$1 permanent;

  root /home/deploy/example;
  index index.html;

  location ~* \.(jpg|jpeg|png|gif|ico|css|js|eot|ttf|woff|woff2)$ {
    expires max;
    try_files $uri =404;
  }

  location ~ ^/([^.]*)/?$ {
    expires 1h;
    try_files $uri $uri.html $uri/index.html =404;
  }

  error_page 404 =404 /404;
  error_page 500 502 503 504 =500 /500;
}
</code></pre>
<p>The actual directory holding your HTML files and web assets is at <code>/home/deploy/example</code> here.
Enabling these SSL Ciphers should get you 100% at <a href="https://www.ssllabs.com">SSL Labs</a>.</p>
<p>For reverse proxies, use the <code>proxy_pass</code> directive. In the case below, I'm mapping routes that
start with <code>/api</code> to port <code>8000</code>:</p>
<pre><code>location ~ ^/api/.*$ {
  proxy_pass http://localhost:8000;
}
</code></pre>
<h2>Conclusion</h2>
<p>I hope that's helpful! I keep a single repository with my deploy playbooks. When I need to add a
new site, I simply add a new <code>nginx-*.conf</code> file and update the <code>sites</code> and <code>sites_nginx</code> vars.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Half Dome</title>
    <link>https://hughbien.com/half-dome</link>
    <pubDate>Sun, 16 Sep 2018 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <img class="image-full image-height-auto" src="https://hughbien.com/images/blog/2018-09-16-half-dome.626453235da57c7754e223b07a60b08f.jpg" alt="At the top of Half Dome" />
<p>After 7 miles and 4,800 feet in elevation gain, this was the most satisfying view of my life.</p>
<h2>San Francisco → Yosemite</h2>
<p>We left on Friday afternoon. Everyone had taken off early from work, so we could get an early
night sleep at our cabin. Google Maps told us to expect a 3.5 hour drive. But after traffic, the
short road trip ended up being close to 5 hours long.</p>
<p>On the way, we stopped by a grocery store to pick up dinner for the night. Plus snacks and meals for
the hike. This was my first long hike, so I had a few questions for the experienced hikers. How much
should we eat for dinner and breakfast? Carbs or fats? What's the restroom and toilet paper
situation during the hike? Rocks were the answer for that last question. That had to be a joke.</p>
<p>At the cabin, we had dinner and slept early around 8pm. The alarm was set for 4am.</p>
<h2>Half Dome</h2>
<p>After parking the cars and walking away, we went back to make sure no snacks were left behind. Had to
make sure bears weren't interested in our cars. I brought a headlamp, which was super useful since
the sun wasn't up yet.</p>
<p>We took the <a href="https://www.yosemitehikes.com/yosemite-valley/mist-trail/mist-trail.htm">Mist Trail</a>
going up. It passed <a href="https://www.nps.gov/yose/planyourvisit/vernalnevadatrail.htm">Vernal and Nevada Falls</a>,
where most of the mist comes from. Honestly, even a short hike to these falls would have been enough
for me. The waterfalls were beautiful. The trail included half a mile of huge granite steps, each
step was maybe two feet tall. The steps were slippery from the mist.</p>
<p>After the falls, the hike felt drawn out at times. I zoned out. We took a few breaks to snack on
our candy (gummies!) and nuts. I was already tired by the time we reached the cables.</p>
<p>At first glance, the <a href="https://www.nps.gov/yose/planyourvisit/halfdome.htm#onthisPage-2">Half Dome Cables</a>
were slightly frightening. It looked like a long line of ants, going up an almost vertical
wall. It was a slog getting up. The hardest parts came from maneuvering around groups taking breaks.
On the way up, everyone got plenty words of encouragement from the hikers heading down. A lot of
&quot;you're almost there&quot;! We all brought heavy duty gloves, which helped with gripping the cables.</p>
<p>After 7 miles and 4,800 feet in elevation gain, we finally made it to the top! It was extremely windy.
The view of the entire valley was amazing. We all took photos at the peak, everyone was being extra
cautious — reminding everyone else to please not fall off. We also had our lunch here, some
packed breakfast burritos.</p>
<p>On the way down, we took the <a href="https://en.wikipedia.org/wiki/John_Muir_Trail">John Muir Trail</a> which
was less steep and easier on our knees. The entire hike was about 14.5 miles round trip.</p>
<h2>Camp 4 Bouldering</h2>
<p>The next day, we went to <a href="https://www.mountainproject.com/area/105833471/camp-4-boulders">Camp 4</a>
for a quick bouldering session. I was extra excited to come here, especially after watching
<a href="https://www.imdb.com/title/tt3784160/">Valley Uprising</a>. I got to see the famous Midnight Lightning problem!</p>
<p>I felt completely out of my league here. Outdoor bouldering was tough. What guidebooks
called jugs here felt more like crimps. We nicknamed them the &quot;Yosemite Jug&quot;. Can't wait to come
back after a few more years of climbing though!</p>

      ]]>
    </description>
  </item>  <item>
    <title>San Francisco</title>
    <link>https://hughbien.com/san-francisco</link>
    <pubDate>Sat, 10 Nov 2018 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <div class="image-gallery">
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2018-11-10-oakland.62c136d34de95f9e48700289c7ac075e.jpg" alt="View of SF from Oakland" />
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2018-11-10-lands-end.480cc354149f3f788aed059765ccae2a.jpg" alt="Land's End" />
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2018-11-10-palace.ea4a929c39655357743bdced11a4fbdd.jpg" alt="Palace of Fine Arts" />
</div>
<p>I've lived in the Bay Area my most of my life, minus 4 years in San Diego. But living in San
Francisco has been the most memorable part.</p>
<p>The city is densely packed, only covering 7 by 7 miles of land. It's no secret that it's a tech hub
— the majority of people I met were software engineers or worked at a tech company, just like
me. San Francisco has so much to offer. I always felt like I didn't have enough time to adequately
experience it. These were the spots I spent most of my time at.</p>
<h2>Climbing</h2>
<p><a href="https://touchstoneclimbing.com/dogpatch-boulders/">Dogpatch Boulders</a> — my second home!
Starting climbing was one of the best decisions of my life. It's addictive. After being sedentary
for most of my twenties, it was something I needed. The gym gets crowded during the evenings after
work, but it's also a fun social hobby to work on problems with other climbers. They also throw fun
competitions like Battle of the Bay.</p>
<p><a href="https://touchstoneclimbing.com/mission-cliffs/">Mission Cliffs</a> — membership at Dogpatch Boulders
will let you come here too. Plenty of roped routes to choose from, but it gets crowded during evenings
just like Dogpatch.</p>
<p><a href="https://planetgranite.com/sf/">Planet Granite</a> — plenty of roped routes and a larger bouldering
section than Mission Cliffs. It's located in the corner of the city, at the Presidio. When you top
out, you get a great view of the bay.</p>
<h2>Lindy Hop</h2>
<p><a href="https://www.920special.com">920 Special</a> — the instructors here did an amazing job teaching
Lindy Hop. Every Thursday evening, there are multiple classes for all skill levels, including
complete beginners like me! After class is the social dance that runs from ~9:20pm to midnight.</p>
<p><a href="http://www.lindyinthepark.com">Lindy in the Park</a> — LitP has been running for over two decades!
It runs every Sunday, 11am to 2pm, at Golden Gate Park. There's a short lesson at the beginning of each dance.</p>
<p><a href="https://www.sfbootleggersball.com">Bootleggers Ball</a> — it's a once-a-month event in the Mission.
Remember to dress classy! It's like you've gone back in time to the 1920's, dancing to live jazz.</p>
<h2>Bars, Coffee Shops, and Bakeries</h2>
<p><a href="https://www.southernpacificbrewing.com">Southern Pacific Brewing Co</a> — I'm not a big drinker,
these days averaging about one drink every other month or so. But SoPac was my go-to spot while living
in San Francisco. After every climbing session at Mission Cliffs, my buddy Kevin and I headed here
for dinner. Try the glazed Brussels sprouts, they're delicious!</p>
<p><a href="https://fillmorestreetsf.com/dining/coffee-houses/cafe-murano/">Cafe Murano</a> — I have a weekly
habit of working from coffee shops. Once a week, I'll work out of two or three cafes around the city.
Cafe Murano was one of my favorite spots to work.</p>
<p><a href="http://www.atlascafe.net">Atlas Cafe</a> — before a climbing session at Mission Cliffs, this was
a great place to pick up coffee. On Friday evenings, they have live jazz, so it was a good spot for
dinner and music.</p>
<p><a href="https://www.tripadvisor.com/Restaurant_Review-g60713-d840473-Reviews-Blue_Danube_Coffee_House-San_Francisco_California.html">Blue Danube Coffee House</a> —
it's in the Inner Richmond neighborhood and is another great spot to work (or read). When the weather
is sunny, they'll leave their front window open (it's a huge window) to get a breeze in.</p>
<p><a href="https://bpatisserie.com">B Patisserie</a> — hands down, my favorite bakery in the entire world.
There are so many delicious things here. I lived within walking distance and it was a struggle every
day not buying their entire menu. Get there Kouign-Amann!</p>
<p><a href="https://arsicault-bakery.com/home">Ariscault</a> — while B Patisserie was my favorite bakery,
I thought Ariscault had the best croissants in San Francisco. I have no idea how they get it so
flaky. I'd recommend every single croissant there.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Bishop, Zion, and Red Rocks</title>
    <link>https://hughbien.com/bishop-zion-red-rocks</link>
    <pubDate>Tue, 28 May 2019 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>Road trip! I took a week off work to drive around California, Nevada, and Utah with three friends.
We all met at the climbing gym and were excited to do some climbing on this trip.</p>
<p>I flew into San Francisco and went to my college friends' wedding. We started our road trip that
same night. Seriously, we hopped into a car headed to Bishop in wedding attire. This was also the
same night as the Game of Thrones series finale, so we watched that in the back seat.</p>
<h2>Bishop</h2>
<p>We got to Bishop around 3 or 4am. It was 30 degrees cold, we were tired, but we had to setup our tents
still. It was tough to do in the dark, but after ~10 minutes or so the tents were up and I was off
to sleep. Except it was incredibly windy the entire night, I thought there was a bear attacking the
tent. Or my friends were messing with me by hitting the tent. Could have been either one.</p>
<div class="image-gallery">
  <img class="image-full image-height-auto" src="https://hughbien.com/images/blog/2019-05-28-bishop.2ecf4a70627404f928c870f506020928.jpg" alt="Bouldering at Bishop" />
</div>
<p>Bishop had hundreds of boulders scattered across the desert. The granite rocks were rough on our
fingers. Crimps felt like sharp glass edges. We mostly stayed around
<a href="https://www.mountainproject.com/area/105799640/happy-boulders">Happy Boulders</a> and
<a href="https://www.mountainproject.com/area/105876417/birthday-boulders">Birthday Boulders</a>.
It was tough but fun.</p>
<h2>Zion</h2>
<p>Leaving Bishop, it was a 7 hour drive to Springdale, Utah. We spent our first day just scouting the
area, planning on how we would spend the next few days in Zion and the surrounding parks.</p>
<div class="image-gallery">
  <img class="image-full image-height-auto" src="https://hughbien.com/images/blog/2019-05-28-angels-landing-trail.6fd0be6c9ac0d600b0071b39a3848b22.jpg" alt="Angels Landing Trail" />
  <img class="image-half image-height-auto" src="https://hughbien.com/images/blog/2019-05-28-angels-landing.529a6acd0e8faba9fb33edafa98bb0e1.jpg" alt="Angels Landing" />
  <img class="image-half image-height-auto" src="https://hughbien.com/images/blog/2019-05-28-kanarra-canyon.f5218dcd1dcf721e5dd4fdf0ddbcd97b.jpg" alt="Kanarra Canyon" />
</div>
<p>We ended up splitting our time in a few areas:</p>
<p><a href="https://www.alltrails.com/trail/us/utah/angels-landing-trail">Angels Landing</a> —
the hike itself was only about 5 miles. There was a steep, scrambly area with chains that scared me
a bit. There was a crowd of hikers, so it was tough sharing the same space near the top. A trail runner
ran downhill, which amazed me. The view from the peak was amazing. The valley floor was covered with
green vegetation.</p>
<p><a href="https://www.alltrails.com/trail/us/utah/kanarra-creek-canyon-trail">Kanarra Creek</a> —
the Narrows were flooded, so we picked another slot canyon to hike. We did this one immediately after
descending Angels Landing. It was 6 miles total, but felt more like twice the length! This was my
first water hike and I never realized how much slower it takes hiking through a running river. At
the end was a gorgeous waterfall running over an old ladder that we had to climb. I didn't expect it,
but this was probably my favorite part of the trip. I'll need to look up more slot canyons to hike.</p>
<p><a href="https://www.mountainproject.com/area/106028834/moes-valley">Moe's Valley</a> — we came here
twice. The first time was for night climbing, which was visually tough but the cold air felt relaxing.
The sandstone felt so much smoother on our skin than Bishop's granite. The grading was nicer too,
we could actually send problems here!</p>
<p><a href="https://www.nps.gov/brca/index.htm">Bryce Canyon</a> — a short drive away from Zion, we saw
Thor's hammer here and the rest of the amphitheater. We came near sunset, so we could stargaze.
Bryce Canyon is a certified international dark sky park.</p>
<h2>Red Rocks</h2>
<p>On our way back to San Francisco, we stopped by the Red Rocks in Las Vegas. These boulders were
made of Aztec Sandstone and were also nice on our skin.</p>
<p>We mostly stuck around the <a href="https://www.mountainproject.com/area/105937674/monkey-bar-boulder">Monkey Bar Boulder</a>.
I completely forgot this was where <a href="https://www.mountainproject.com/route/107185645/plumbers-crack">Plumber's Crack</a>
was, so we didn't check it out. But that's just a good excuse to come back!</p>
<p>From Red Rocks, we drove to San Luis Obispo where we camped out for the night before returning
to San Francisco.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Adapt Method Parameters</title>
    <link>https://hughbien.com/adapt-method-parameters</link>
    <pubDate>Mon, 12 Aug 2019 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>Since reading <a href="https://pragprog.com/titles/agcr/confident-ruby/">Confident Ruby</a>, I've been seeing
the four distinct parts of my methods:</p>
<ol>
<li>collecting input</li>
<li>performing work</li>
<li>delivering output</li>
<li>handling failures</li>
</ol>
<p>Keeping these parts atomic makes the story strong, so I do my best to not unconsciously mix them up.
Lately, I've been thinking about using adapters at the collecting input stage. This seems to make
the work performance code more clear.</p>
<h2>What's an Adapter?</h2>
<p>The <a href="https://refactoring.guru/design-patterns/adapter">Adapater Design Pattern</a> further separates
interface from implementation, to the point where you can have a single interface for multiple
implementations. Frameworks use adapters all the time. For example, ORMs like ActiveRecord. As the
application developer, you'll use ActiveRecord's interface. But under the hoods, ActiveRecord uses
the adapter pattern and can substitute in either Postgres, MySQL, or SQLite3 for the implementation.</p>
<p>While the pattern is useful for simplifying multiple implementations, it can be used to force a
single interface too. For example, jQuery does a great job at this by always adapting matched
elements to a collection. This happens regardless if you have zero, one, or multiple matched elements.</p>
<p>Here's what manipulating the DOM looks like in vanilla JavaScript:</p>
<pre><code class="language-js">// hiding one element
document.querySelector(&quot;#my-div&quot;).style.display = &quot;none&quot;;

// hiding multiple elements
document.querySelectorAll(&quot;.my-div&quot;).forEach((div) =&gt; {
  div.style.display = &quot;none&quot;;
});

// verifying element exists to avoid null pointer exceptions
let div = document.querySelector(&quot;#maybe-div&quot;);
if (div) {
  div.style.display = &quot;none&quot;;
}
</code></pre>
<p>Here's what it looks like with jQuery:</p>
<pre><code class="language-js">// hiding one element
$(&quot;#my-div&quot;).hide();

// hiding multiple elements
$(&quot;.my-div&quot;).hide();

// no need to verify element exists, jQuery no-ops for no matched elements
$(&quot;#maybe-div&quot;).hide();
</code></pre>
<p>Of course there are drawbacks. Maybe we didn't want to silently fail for no matched elements. We
also introduce extra complexity with the adapter implementation. You'll have to decide if the
trade-off is worth it.</p>
<h2>Examples for Method Parameters</h2>
<p>On a smaller scale, we can use adapters for collecting input in our methods. This can open up our
methods to a wide range of inputs, while keeping our implementation simple.</p>
<p>Consider this method, which allows for a single id or multiple ids as a parameter:</p>
<pre><code class="language-rb">def perform_users(user_id_or_ids)
  if user_id_or_ids.is_a?(Enumerable)
    user_id_or_ids.each do |user_id|
      # implementation here
    end
  else
    user_id = user_id_or_ids
    # repeated implementation here on single user id
  end
end
</code></pre>
<p>Ruby has conversion functions, like <code>Array()</code>, that are part of its standard library. These are all
idempotent. We'll use it to clean up the example above:</p>
<pre><code class="language-rb">def perform_users(user_id_or_ids)
  Array(user_id_or_ids).each do |user_id|
    # implementation here
  end
end
</code></pre>
<p>Some conversion functions you can use include: <code>Array()</code>, <code>Integer()</code>, <code>Float()</code>, <code>String()</code>,
<code>URI()</code>.</p>
<p>In JavaScript, you can use constructors like <code>Number()</code> and <code>String()</code> to get the same effect. But
be careful with <code>Array()</code>:</p>
<pre><code class="language-js">function performUsers(userIdOrIds) {
  Array(userIdOrIds).forEach((userId) =&gt; {
    // implementation here
  });
}

performUsers(&quot;1&quot;); // works as expected
performUsers([&quot;1&quot;, &quot;2&quot;]); // wraps in a secondary array
performUsers(1); // does a no-op on an empty array
</code></pre>
<p>That's because the <code>Array()</code> constructor isn't idempotent (it will wrap an Array in new one). Also
when called with a single integer argument, it will return an empty array using that argument as
the array's length.</p>
<p>In languages that support dynamic dispatch, like Crystal, it may be more clear to extract input
conversions to its own method:</p>
<pre><code class="language-rb">def perform_users(user_id : Int32)
  perform_users([user_id])
end

def perform_users(user_ids : Array(Int32))
  user_ids.each do |user_id|
    # implementation here
  end
end
</code></pre>

      ]]>
    </description>
  </item>  <item>
    <title>Chicago</title>
    <link>https://hughbien.com/chicago</link>
    <pubDate>Wed, 15 Jan 2020 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <div class="image-gallery">
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2020-01-15-bean.9d14a85c076e4b287293538e516b822a.jpg" alt="The Bean" />
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2020-01-15-trail.0e4c92d8d7a749f89cf3548e489682dd.jpg" alt="Lakefront Trail" />
  <img class="image-third image-height-auto" src="https://hughbien.com/images/blog/2020-01-15-first-ascent.da3e8adbd00daf775d3088e249079c63.jpg" alt="First Ascent, Humboldt Park" />
</div>
<p>Chicago is beautiful. It was a shock moving here from California. My first winter here was the polar
vortex. I did the boiling water challenge, throwing boiling water into the cold air to see it
vaporize instantly. It really does feel like two different cities between winter and summer.</p>
<p>The city is huge! There's so much I haven't done yet. Most of the spots I go to are located somewhere
next to a Blue Line station, since that's the line I live next to. I'm looking forward to exploring
more of the city, but for now here are my favorite spots.</p>
<h2>Climbing, BJJ, Muay Thai, and Lindy Hop</h2>
<p><a href="https://faclimbing.com">First Ascent</a> — I'm a huge fan of bouldering and First Ascent is a
great place to climb. There's four locations in Chicago: Humboldt Park, Block37, Uptown, and
Avondale. Twice a year, there's bouldering league. It's a 6-8 week competition between teams. Teams
need to come up with their own (punny) name, there are themed weeks with costumes, it's a blast.</p>
<p><a href="https://brooklynboulders.com/west-loop/">Brooklyn Boulders</a> — located in West Loop, Brooklyn
Boulders has both roped routes and bouldering. The walls feel like glass though. I can't smear on
them at all. I'm a huge fan of their aesthetics though!</p>
<p><a href="https://www.carlsongracieheadquarters.com">Carlson Gracie HQ</a> — the absolute best place to
learn BJJ and Muay Thai in Chicago. They're large enough to have separate beginner and advanced
classes, so you can get started with the fundamentals in beginner class. Muay Thai classes are right
before BJJ, so you can stop in and do both in one evening.</p>
<p><a href="https://supremetitan.com">Titan Gym</a> — I took a few Muay Thai classes here too. It's a fun
and friendly environment. The teachers are very welcoming. They offer a variety of classes, from
Krav Maga to Kali.</p>
<p><a href="https://seechicagodance.com/organization/big-city-swing">Big City Swing</a> — Lindy Hop classes
for most weekdays. It's located in a small dance studio right next to the Western station. They have
social dance nights once a month.</p>
<h2>Coffee Shops, Shows, and Food</h2>
<p><a href="https://sipofhope.com">Sip of Hope</a> — I've continued my habit of working from coffee shops
at least once a week. Sip of Hope is my favorite place to work in Chicago. There's plenty of seating,
the Chai Latte is delicious, and their profits go towards a good cause.</p>
<p><a href="http://cafemustache.com">Cafe Mustache</a> — half coffee shop, half bar, depending on what
time you arrive. I'm a big fan of their breakfast bagel. I also like the old-school records vibe.
Plus when you're done here, you can just head down Milwaukee Ave to the next coffee shop.</p>
<p><a href="https://colectivocoffee.com">Colectivo Coffee</a> — there are several locations, but the one
I usually went to was right next to Cafe Mustache. I'm a big fan of their Matcha Latte. There's
plenty of bar seating. It's right next to the California Blue Line station.</p>
<p><a href="https://www.thewormhole.us/category/the-shop/">Wormhole Coffee</a> — located in Wicker Park,
walking into this coffee shop is like walking back in time. The room is filled with memorabilia from
80s and 90s pop culture.</p>
<p><a href="http://www.oromocafechicago.com">Oromo Cafe</a> — there are two locations, Bucktown and Lincoln
Square. The Turkish pastries are delicious. They have a big selection of coffees and lattes, from
Turkish coffee to a Horchata Latte.</p>
<p><a href="http://http://sawadacoffee.com">Sawada Coffee</a> — the absolute best place to get a Matcha
Latte in Chicago. Also one of my favorite spots to work in. It shares a space with Green Street
Meats, where you can have BBQ in a barn.</p>
<p><a href="https://freehandhotels.com/chicago/cafe-integral/">Cafe Integral</a> — if I had a house with a
study, Cafe Integral is what I'd want it to look like. In River North, this coffee shops is in a
hotel. It's located right off of Michigan Ave.</p>
<p><a href="https://www.secondcity.com">Second City</a> — I take all my friends visiting Chicago to the
Second City. It's my favorite comedy club. They're famous for their improv and sketch comedy.</p>
<p><a href="https://greenmilljazz.com">Green Mill</a> — live jazz in Uptown, from big names to local bands.
This was Al Capone's favorite lounge in Chicago. He had a booth reserved specifically for him. Also
right next to First Ascent Uptown.</p>
<p><a href="http://smallcheval.com">Small Cheval</a> — both Small Cheval and Au Cheval have my favorite
burgers (of all time). Au Cheval in West Loop has a fine dining experience and usually comes with
a long line. Small Cheval has a more casual experience, but the burger is just as delicious.</p>
<h2>Outdoors</h2>
<p><a href="https://www.chicagoparkdistrict.com/parks-facilities/lakefront-trail">Lakefront Trail</a> — the
most scenic trail I've ever been on. Biking the Lakefront Trail is my favorite summer activity. You
can rent bikes on the trail, or rent it from a nearby Divvy station. Just a warning, those Divvy
bikes are a little bit heavier (they're designed to survive Chicago winters). There are dozens of
stops along the trail, where you can enjoy lunch and enjoy the view.</p>
<p><a href="https://www.chicagoriverwalk.us">Riverwalk</a> — downtown Chicago is gorgeous. A huge part of
that is the Chicago River. When it's not winter, walking along the river is a relaxing experience.
There's also Art on the Mart during summer nights. During summers, be sure to try the kayaking.</p>
<p><a href="https://www.chicago.gov/city/en/depts/dca/supp_info/millennium_park.html">Millennium Park</a> —
Chicago really likes its outdoor festivals and most of them are at Millennium Park. Even if there's
not a festival, it's fun to just grab a coffee and walk around the downtown park.</p>
<p><a href="https://www.chicagoparkdistrict.com/parks-facilities/lincoln-abraham-park">Lincoln Park</a> —
it's a huge park next to the Lake, with a great view of the skyline. It's also where the free Lincoln
Park Zoo is.</p>
<p><a href="https://www.chicagoparkdistrict.com/parks-facilities/south-lagoon-flower-garden">South Lagoon</a> —
I went stand-up paddle boarding here twice. When I first arrived, I didn't think much of it. We launched
off from an area filled with boats. But after paddling under a bridge, going further down the lagoon,
you'll get one of the best views of the Chicago skyline in the entire city.</p>

      ]]>
    </description>
  </item>  <item>
    <title>Developer Financial Independence</title>
    <link>https://hughbien.com/developer-financial-independence</link>
    <pubDate>Thu, 09 Apr 2020 12:00:00 -0700</pubDate>
    <description>
      <![CDATA[
        <p>I made a website — <a href="https://developerfi.com">DeveloperFI</a>! It's for software developers
interested in FIRE.</p>
<h2>What's FIRE?</h2>
<p>FIRE is an acronym for Financial Independence, Retire Early. It's important to separate the two.
Reaching financial independence gives you the freedom to: start a business, work part-time, pursue
hobbies full-time, focus on a charity, spend time with family, travel, or retire early. All without
worrying about your finances.</p>
<p>The 4% rule is a general rule that many FIRE seekers use to determine how much they need to save up.
It comes from the <strong>safe withdrawal rate</strong>, which says you can annually withdraw 4% of your saved
portfolio without running out. This means you need to save up 25x times our annual spending. For
example, if you spend $40k a year, you'll need to save up $1M.</p>
<h2>LeanFI, FatFI, CoastFI, BaristaFI</h2>
<p>A few sub-communities popped out of the main FIRE group. They each have their own strategy, depending
on their lifestyle, earning opportunities, and values.</p>
<p><strong>LeanFI</strong> — takes a leaner approach, accelerating your time to financial independence by
reducing how much you spend. Pursuers tend to spend below $40k/year and target $1M saved for their
FI number.</p>
<p><strong>FatFI</strong> — takes the opposite approach to LeanFI, wanting to make no sacrifices in their
lifestyles during retirement. There's no set amount, but pursuers tend to spend above $200k/year
and target above $5M saved. This might mean staying employed longer to take advantage of your high
income years as a senior individual contributor or manager.</p>
<p><strong>CoastFI</strong> — this strategy involves saving enough money where investment returns should be
enough by the time you hit your retirement age. For example, saving $250k by the time you're 30 years
old. According to the <a href="https://www.thebalance.com/what-is-the-rule-of-72-how-can-it-help-you-double-your-money-453756">Rule of 72</a>,
your portfolio should double about every 10 years. So it should be $1M by the time you're 50 years old.
CoastFI means saving that initial $250k by the time you're 30, then &quot;coasting&quot; at an easier job that
might pay less. As long as you don't spend your savings, you should be set for retirement.</p>
<p><strong>BaristaFI</strong> — similar to CoastFI, someone who reaches BaristaFI isn't ready to retire yet.
This strategy involves saving money in a high income (but perhaps also high stress) job before switching
to a lower income (but also lower stress) job. Pursuers of BaristaFI will use their income to
supplement the income they make from their investment portfolio.</p>
<h2>Check out DeveloperFI!</h2>
<p>If any of this sounds interesting, take a look at <a href="https://developerfi.com">DeveloperFI</a>! Right now,
it's a collection of tips about: using tax advantaged accounts (401k/HSA/IRA), participating in ESPP,
investing in index funds, diversifying with lazy portfolios, and more. I'm hoping to add more
calculator tools (or even a command-line tool) later on.</p>

      ]]>
    </description>
  </item>  <item>
    <title>DKIM and SPF for Emails on SES</title>
    <link>https://hughbien.com/dkim-spf-ses</link>
    <pubDate>Thu, 10 Dec 2020 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <p>If your application sends transactional or marketing emails, <strong>you must setup both DKIM and SPF.</strong>
Failing to do so will hurt your email deliverability. Your messages will end up in spam.</p>
<p>I've recently had to setup DKIM/SPF for <a href="https://useboutique.com">Boutique</a>. The application uses
Amazon SES, which has pretty straightforward steps. I'll be using Amazon SES as the email
service provider in this article.</p>
<h2>What are DKIM and SPF?</h2>
<p>DKIM (DomainKeys Identified Mail) and SPF (Sender Policy Framework) are both email authentication
methods. They let email receivers know that an incoming email from that domain has been authorized
by the domain owners.</p>
<p>DKIM uses asymmetric cryptography to sign the email before it's sent out. The public key is listed
on the domain's DNS records, where email clients can find it to verify any emails.</p>
<p>SPF adds a list of authorized IP addresses to a domain's DNS records. When an email client receives
an email, it can check the domain's DNS to verify the sender's IP address is on that list.</p>
<h2>What domains should I send email from?</h2>
<p>The best practice is to send your transactional emails from a different subdomain than your marketing
emails. For example: <code>@transactions.example.com</code> for transactional emails and <code>@marketing.example.com</code>
for marketing emails.</p>
<p>Transactional emails are sent after a user action. They're expected and tend to have a much better
reputation than marketing emails. Splitting the two keeps their reputation scores separate. You
don't want your &quot;confirm account&quot; or &quot;reset password&quot; emails ending up in customers' spam folder.</p>
<h2>Setting up DKIM</h2>
<p>From your AWS console:</p>
<ol>
<li>go to the SES management console</li>
<li>in the sidebar, under &quot;Identity Management&quot;, go to &quot;Email Addresses&quot;</li>
<li>click on the email address you want to verify</li>
<li>click DKIM and click &quot;Generate DKIM Settings&quot;</li>
</ol>
<p>This will generate a table of DKIM CNAME records. You'll need to log into your domain registrar and
add these three CNAME records. An example row for the <code>marketing.example.com</code> subdomain will look like:</p>
<ul>
<li>Type: <code>CNAME</code></li>
<li>Hostname: <code>abcdefghijklmnopqrstuvwxyz123456._domainkey.marketing.example.com</code></li>
<li>Value: <code>abcdefghijklmnopqrstuvwxyz123456._domainkey.marketing.example.com</code></li>
</ul>
<p>Please note, that some registrars automatically append the apex domain name to the hostname portion of
the CNAME record. So the actual value you should paste in would be:</p>
<pre><code>abcdefghijklmnopqrstuvwxyz123456._domainkey.marketing
</code></pre>
<p>Since your registrar will automatically append <code>.example.com</code> to it.</p>
<h2>Setting up SPF</h2>
<p>All domains require the same TXT record to use Amazon SES as an email service provider. See
<a href="https://docs.aws.amazon.com/ses/latest/DeveloperGuide/send-email-authentication-spf.html">Authenticating Email with SPF in Amazon SES</a>
for a full guide. Here's the TLDR:</p>
<ol>
<li>go to your domain registrar</li>
<li>add a TXT record with <code>@</code> as the hostname</li>
<li>use <code>v=spf1 include:amazonses.com ~all</code> for the value</li>
</ol>
<p>You need to have a TXT record for each subdomain. So <code>@</code> can be used if you're sending from the
apex (eg <code>example.com</code>), but if you're sending from <code>marketing.example.com</code> you'll need to add another
TXT record with <code>marketing</code> as the hostname.</p>
<p>Also if you already have an SPF record, you can add additional domains to it with the <code>include:</code>
directive. The format below adds two email service providers:</p>
<pre><code>v=spf1 include:example.com include:amazonses.com ~all
</code></pre>

      ]]>
    </description>
  </item>  <item>
    <title>Deliberate Practice and the Hero's Journey</title>
    <link>https://hughbien.com/deliberate-practice</link>
    <pubDate>Sat, 06 Mar 2021 12:00:00 -0800</pubDate>
    <description>
      <![CDATA[
        <p>After reading <a href="https://www.goodreads.com/book/show/26312997-peak">Peak</a> by K. Anders Ericsson and
Robert Pool, I've been thinking a lot about how to apply their advice to software engineering and the
craft of programming.</p>
<p>In sports like climbing, BJJ, or weightlifting there's quite a few structured training
programs to choose from. Climbers have <a href="https://www.99boulders.com/beginner-hangboard-training">hangboarding</a>
and <a href="https://www.trainingbeta.com/4x4s/">4x4s</a>. BJJ students have a common class format: warm ups,
techniques/drilling, and rolling. Lifters can choose a program like
<a href="https://startingstrength.com">Starting Strength</a>, which dictates which exercises to perform at
how many sets/reps and what weight to use.</p>
<p>As a software engineer, I have no idea how to practice to get better. Could we create a similar
structured system? In this article, I'm mostly thinking out loud about how to become a better
software engineer.</p>
<h2>Naive Practice</h2>
<p>When you first start a new sport, game, or skill, you can get better just by participating. If
you're completely new to chess, you'll improve just by playing more chess games.</p>
<p>Ericsson calls this naive practice. It's just blindly repeating the same tasks over and over again,
without much strategic thought. There's no focus on specific skills you need to improve, no goal
setting, and no overall planning. Naive practice will get a person to reach an acceptable level of
performance — but additional years won't lead to improvement.</p>
<p>Naive practice is perfectly fine and sometimes even recommended! When we first start programming,
simply picking up a tutorial and writing programs is enough to improve.</p>
<h2>Purposeful Practice</h2>
<p>When you reach a plateau with naive practice, you'll want to start purposeful practice.
This involves:</p>
<ul>
<li>breaking down the skill into chunks</li>
<li>working on specific goals, especially to fix your weaknesses</li>
<li>focusing intensely on improving a single chunk via practice (with full conscious attention)</li>
<li>using an immediate/specific feedback system (ideally with quantitative measurements)</li>
<li>continually push yourself to more challenging levels, outside your comfort zone</li>
</ul>
<p>A baseball player's naive practice would be simply playing baseball games everyday. For Purposeful
Practice, the player can pick a specific skill to improve — let's say batting. She can use an
automated batting cage, record her swings on video for later feedback, keep statistics on her
hits/misses, and keep a qualitative journal after each session. She can continually push herself
by slowly increasing the speed of the pitch.</p>
<p>How can this be applied to software engineering and programming?</p>
<p><strong>Breaking it down into specific chunks</strong> — languages (Ruby, Crystal, Swift, JavaScript,
TypeScript, Go, Rust), topics (algorithms, data structures, web development, sysadmin, system
design, architecture, AI/ML, compilers, types, functional programming), technologies (iOS/Mac,
unix/shell, Ansible, React). Make a list of what you want to learn. Pick a specific item that you
want to get focus on.</p>
<p><strong>Focused practice</strong> — pick projects that will force you to develop your selected chunk. Since
this is practice, your projects don't have to be used in production either. Commit to
<a href="https://www.100daysofcode.com">100 Days of Code</a> focused on your chunk. Use resources like
books/screencasts to learn. Use resources like <a href="https://www.executeprogram.com">Execute Program</a>,
<a href="https://leetcode.com">LeetCode</a>, <a href="https://acloudguru.com">A Cloud Guru</a>, <a href="https://www.vim.so">Vim.so</a>
to practice.</p>
<p><strong>Feedback system</strong> — I've found these systems apply to most programming skills: code reviews,
pair programming, mentorship, and journaling. Code reviews from your teammates are a great way to
gain valuable insight, just leave a short comment on your pull request asking for specific feedback.
This works with pair programming sessions too. You can always ask your manager about how you can
improve during your 1-on-1's. Keep a journal. Ask yourself what went well, what could have been done
better, and record what you've learned.</p>
<p><strong>Continually push yourself</strong> — pick projects that are more challenging than your
previous. Look for projects that let you dip your toes into something you've never done before. Look
for projects that will require learning from a book/screencast or some guidance from a teammate.</p>
<h2>Deliberate Practice</h2>
<p>Deliberate practice is purposeful and also includes:</p>
<ul>
<li>an well defined field where the regimen for improving is known</li>
<li>an expert coach mentoring on how to improve by giving you a plan and providing feedback</li>
</ul>
<blockquote>
<p>“Deliberate practice is purposeful practice that knows where it is going and how to get there.”</p>
</blockquote>
<p>You can get far with purposeful practice. You can get further with deliberate practice. The world of
sports is full of coaches and trainers, making it easier to achieve deliberate practice.</p>
<p>I'm not sure how to mimic this in the world of software engineering. Perhaps finding mentors at your
company (including your manager), and asking for their advice/feedback. Be genuine about what skills
you want to work on. Or you can use a service like <a href="https://codingcoach.io">Coding Coach</a>.</p>
<p>On the career side, you should absolutely speak to your manager about what's expected of you and
how you can deliver more. Also check out services like <a href="https://www.pathrise.com">Pathrise</a>
or <a href="https://www.tryexponent.com">Exponent</a>, which helps with interviewing and career advice.</p>
<h2>The Hero's Journey</h2>
<p>Several years ago at an internal developer conference, <a href="http://derekdevries.com">Derek DeVries</a>
gave a talk about the Hero's Journey and how it applied to developers. He's a very smart engineer,
you can follow him <a href="https://twitter.com/devrieda">@devrieda</a>. I'll try my best not to butcher
the talk here.</p>
<p><a href="https://en.wikipedia.org/wiki/Hero's_journey">The Hero's Journey</a> is a commonly used story
template. It's usually broken into three acts. The departure, when the hero is called to adventure
and leaves his familiar settings. The initiation, when the hero faces a series of challenging
tasks until he defeats the enemy. Each obstacle is slightly out of his comfort zone, he must learn
and grow to overcome them — with the help of his mentor. The return, when the hero returns
to his familiar settings. He has achieved a new level of wisdom, gained from his experiences.</p>
<p>This same template can be applied to your tasks, projects, and career.</p>
<p>At the beginning of every sprint, I'd have a large column of stories to choose from. It's tempting
to assign myself the easiest tasks — the ones I'm most familiar with and have done before.
But doing so means I'll be refusing the call to adventure. I need to, occasionally, pick stories
that are outside my skill set. With resources like books, Stack Overflow, documentation, or existing
commits, I should be able to learn enough to complete these tasks.</p>
<p>The same applies to project selection, especially if you're leading the project. Pick projects that
fall slightly out of your abilities. Work with your teammates, team lead, and mentors to overcome
your knowledge gaps.</p>
<p>At a high level, this also applies to job selection. If you're looking to grow in your career, pick
jobs with two qualities: it's slightly out of your comfort zone and work with teammates/mentors who
can help guide you.</p>
<h2>Further Reading</h2>
<p>Thanks for reading this post! I'm a huge fan of learning how to learn. If you're interested too,
check out these resources:</p>
<ul>
<li><a href="https://www.goodreads.com/book/show/26312997-peak">Peak</a> — a tactical view on how to
practice skills</li>
<li><a href="https://www.goodreads.com/book/show/13589182-mastery">Mastery</a> — a more strategic view on
how to master crafts</li>
<li><a href="https://barbaraoakley.com/books/a-mind-for-numbers/">A Mind for Numbers</a> — learning
strategies for subjects that don't come naturally to us</li>
<li><a href="http://www.effectiveengineer.com">Effective Engineer</a> — how to make meaningful impact as a software
engineer</li>
<li><a href="https://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning/">Pragmatic Thinking and Learning</a>
— the Dreyfus Model of Skill Acquisition and how to deliberately learn</li>
</ul>

      ]]>
    </description>
  </item>  </channel>
</rss>