<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel>
  <title>Mark Hudnall's website</title>
  <link>https://markhudnall.com</link>
  <description>Mark Hudnall's piece of cyberspace</description>
  <language>en</language>
  <atom:link href="https://markhudnall.com/rss.xml" rel="self" type="application/rss+xml" />
  <item>
      <title>How many servers do we need?</title>
      <link>https://markhudnall.com/2023/06/09/concurrency-and-queuing-theory/</link>
      <guid isPermaLink="true">https://markhudnall.com/2023/06/09/concurrency-and-queuing-theory/</guid>
      <pubDate>Fri, 09 Jun 2023 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;How much concurrency do we need to handle a given throughput and response time? &lt;/p&gt;
&lt;p&gt;This is a fundamental question when scaling software systems. Since software systems are queues, let us try to answer it with queuing theory.&lt;/p&gt;
&lt;h2 id=&quot;an-initial-guess-using-littles-law&quot;&gt;An initial guess using Little&#39;s law&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Little&#39;s_law&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Little&#39;s law&lt;/a&gt; states that the average number of jobs in a system (&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;) is equal to the arrival rate of jobs (&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;λ&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;) multiplied by the average time a job spends in the system (&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;).  In other words, &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;λ&lt;/mi&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;. &lt;/p&gt;
&lt;p&gt;Little&#39;s law can provide a simple estimate of the required concurrency — we can set concurrency to the average number of jobs in the system, given some known arrival rate (throughput) and service time (response time).&lt;/p&gt;
&lt;p&gt;Consider a web server where average response time is 100ms. Suppose we&#39;d like to handle a throughput of 100 requests per second. Using Little&#39;s law, we would conclude that, on average, 10 requests are &quot;in progress&quot; from clients&#39; perspectives (either queued or actively being serviced). We might then think to set concurrency to 10 in order to effectively handle this load.&lt;/p&gt;
&lt;p&gt;However, because Little&#39;s law works with averages, it doesn&#39;t account for variability in the requests&#39; arrival and service times. In practice, requests are not uniformly distributed over time, and some requests may take longer than others. There is a non-zero probability that an incoming request might find all servers busy and need to wait. &lt;/p&gt;
&lt;p&gt;So Little&#39;s law on its own is not enough. We need to consider a more sophisticated model.&lt;/p&gt;
&lt;h2 id=&quot;erlangs-c-formula&quot;&gt;Erlang&#39;s C formula&lt;/h2&gt;
&lt;p&gt;We would like to find some level of concurrency that constrains the probability of waiting to some target. To do this, we turn to &lt;a href=&quot;https://en.wikipedia.org/wiki/Erlang_(unit)#Erlang_C_formula&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Erlang&#39;s C formula&lt;/a&gt;, which provides us with the probability that an incoming job must wait because all servers are busy&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2023/06/09/concurrency-and-queuing-theory/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;. Erlang&#39;s C formula is defined as follows:&lt;/p&gt;
&lt;div class=&quot;math-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable equation&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;mrow&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/munderover&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Here, &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; is the average number of jobs in the system, or &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;λ&lt;/mi&gt;&lt;mi&gt;W&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; as above with Little&#39;s law. &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; is the number of servers. &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; is also referred to as &lt;a href=&quot;https://en.wikipedia.org/wiki/Offered_load&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&quot;offered traffic&quot;&lt;/a&gt; — Erlang&#39;s C formula was developed in the context of efficient telephone line usage so &quot;offered traffic&quot; thus refers to the amount of traffic that could be carried given a certain volume of calls and unlimited lines. For our purposes, it is average load, or the average number of requests expected to be in-progress from clients&#39; perspectives, given a certain throughput and response time.&lt;/p&gt;
&lt;p&gt;To find the ideal level of concurrency, we can start with some number of servers (the initial guess using Little&#39;s law) and then increment it until &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; is below some desired threshold (say 5% probability of waiting). We will write a script to do this.&lt;/p&gt;
&lt;p&gt;We&#39;re faced with a challenge here, though. Due to the factorials, computing Erlang&#39;s C formula will underflow. To work around this, we will translate Erlang&#39;s C formula into log-probability space where underflows and overflows are less likely.&lt;/p&gt;
&lt;h2 id=&quot;deriving-the-logarithmic-erlang-c-formula&quot;&gt;Deriving the logarithmic Erlang C formula&lt;/h2&gt;
&lt;p&gt;We start with the original Erlang&#39;s C formula:&lt;/p&gt;
&lt;div class=&quot;math-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable equation&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;mrow&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/munderover&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;We take the natural log of each term in the formula:&lt;/p&gt;
&lt;div class=&quot;math-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable equation&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/munderover&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mfrac&gt;&lt;msup&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/msup&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mfrac&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Here, we&#39;ve used the property of logarithms &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;/&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;We further simplify the above expression:&lt;/p&gt;
&lt;div class=&quot;math-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable equation&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;mtable rowspacing=&quot;0.25em&quot; columnalign=&quot;right left&quot; columnspacing=&quot;0em&quot;&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;Γ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;munderover&gt;&lt;mo&gt;∑&lt;/mo&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;0&lt;/mn&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;/mrow&gt;&lt;/munderover&gt;&lt;msup&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;Γ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;i&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;mtr&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;mtd&gt;&lt;mstyle scriptlevel=&quot;0&quot; displaystyle=&quot;true&quot;&gt;&lt;mrow&gt;&lt;mrow&gt;&lt;/mrow&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;e&lt;/mi&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;Γ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mstyle&gt;&lt;/mtd&gt;&lt;/mtr&gt;&lt;/mtable&gt;&lt;/math&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Here, we&#39;ve used the property of logarithms, &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/msup&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo&gt;⋅&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;, and replaced factorials with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Gamma_function&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Gamma function&lt;/a&gt; (&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;Γ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; for a non-negative integer &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;), as &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;!&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt; can be computed as &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi mathvariant=&quot;normal&quot;&gt;Γ&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Note that the original denominator is now the log of a sum of exponentials. We can thus use the &lt;a href=&quot;https://en.wikipedia.org/wiki/LogSumExp&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;log-sum-exp trick&lt;/a&gt; to calculate these terms without underflow.&lt;/p&gt;
&lt;p&gt;Finally, we exponentiate the log-probability to get back to the original probability space:&lt;/p&gt;
&lt;div class=&quot;math-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable equation&quot;&gt;&lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot; display=&quot;block&quot;&gt;&lt;mrow&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mi&gt;exp&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;C&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo separator=&quot;true&quot;&gt;,&lt;/mo&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;mo stretchy=&quot;false&quot;&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h2 id=&quot;python-code&quot;&gt;Python code&lt;/h2&gt;
&lt;p&gt;Let&#39;s look at how we&#39;ll calculate the minimum concurrency needed to meet a certain throughput, response time, and probability of queuing.&lt;/p&gt;
&lt;p&gt;First, we&#39;ll choose an initial guess for the required number of servers using Little&#39;s law and a target throughput and response time. Then, we will update our initial guess using an iterative process. We will increment the number of servers until the probability of queuing, as given by our logarithmic Erlang&#39;s C formula, is below our desired max.&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;import&lt;/span&gt; math

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;log_sum_exp&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;numbers&lt;/span&gt;):
    max_num = &lt;span class=&quot;hljs-built_in&quot;&gt;max&lt;/span&gt;(numbers)
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; max_num + math.log(&lt;span class=&quot;hljs-built_in&quot;&gt;sum&lt;/span&gt;(math.exp(x - max_num) &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; x &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; numbers))

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;erlang_c&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;L, S&lt;/span&gt;):
    log_numerator = S * math.log(L) - math.lgamma(S + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) + math.log(S) - math.log(S - L)
    denominator_terms = [i * math.log(L) - math.lgamma(i + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) &lt;span class=&quot;hljs-keyword&quot;&gt;for&lt;/span&gt; i &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;range&lt;/span&gt;(S)] + [S * math.log(L) - math.lgamma(S + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;) + math.log(S) - math.log(S-L)]
    log_denominator = log_sum_exp(denominator_terms)
    erlang_c = math.exp(log_numerator - log_denominator)
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; erlang_c

&lt;span class=&quot;hljs-keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;calculate_servers&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;lambda_, W, max_prob&lt;/span&gt;):
    lambda_ = &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;(lambda_)
    W = &lt;span class=&quot;hljs-built_in&quot;&gt;float&lt;/span&gt;(W)
    L = lambda_ * W
    S = math.floor(L + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;)
    &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;f&#39;Starting with &lt;span class=&quot;hljs-subst&quot;&gt;{S}&lt;/span&gt; servers&#39;&lt;/span&gt;)
    prob = erlang_c(L, S)
    &lt;span class=&quot;hljs-keyword&quot;&gt;while&lt;/span&gt; prob &amp;gt; max_prob:
        &lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;f&#39;With &lt;span class=&quot;hljs-subst&quot;&gt;{S}&lt;/span&gt; servers, the queuing probability is &lt;span class=&quot;hljs-subst&quot;&gt;{prob}&lt;/span&gt;. Trying &lt;span class=&quot;hljs-subst&quot;&gt;{S + &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;}&lt;/span&gt; servers.&#39;&lt;/span&gt;)
        S += &lt;span class=&quot;hljs-number&quot;&gt;1&lt;/span&gt;
        prob = erlang_c(L, S)
    &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; S, prob

requests_per_second = &lt;span class=&quot;hljs-number&quot;&gt;1000.0&lt;/span&gt;
response_time_s = &lt;span class=&quot;hljs-number&quot;&gt;0.1&lt;/span&gt;
max_prob = &lt;span class=&quot;hljs-number&quot;&gt;0.05&lt;/span&gt;

num_servers, prob = calculate_servers(requests_per_second, response_time_s, max_prob)
&lt;span class=&quot;hljs-built_in&quot;&gt;print&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;f&#39;The minimum number of servers required is: &lt;span class=&quot;hljs-subst&quot;&gt;{num_servers}&lt;/span&gt; (queuing probability &lt;span class=&quot;hljs-subst&quot;&gt;{prob}&lt;/span&gt;)&#39;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When we run this script, we see the following output:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Starting with 101 servers
With 101 servers, the queuing probability is 0.8833145020395913. Trying 102 servers.
With 102 servers, the queuing probability is 0.7770966583320832. Trying 103 servers.
With 103 servers, the queuing probability is 0.6807968118039859. Trying 104 servers.
With 104 servers, the queuing probability is 0.593855705420894. Trying 105 servers.
With 105 servers, the queuing probability is 0.5157074268123086. Trying 106 servers.
With 106 servers, the queuing probability is 0.44578262621325715. Trying 107 servers.
With 107 servers, the queuing probability is 0.3835119575327338. Trying 108 servers.
With 108 servers, the queuing probability is 0.32832968002713697. Trying 109 servers.
With 109 servers, the queuing probability is 0.27967734713081166. Trying 110 servers.
With 110 servers, the queuing probability is 0.23700750028506098. Trying 111 servers.
With 111 servers, the queuing probability is 0.1997872798880628. Trying 112 servers.
With 112 servers, the queuing probability is 0.16750186339584125. Trying 113 servers.
With 113 servers, the queuing probability is 0.13965764259076413. Trying 114 servers.
With 114 servers, the queuing probability is 0.11578505827853611. Trying 115 servers.
With 115 servers, the queuing probability is 0.09544102105759475. Trying 116 servers.
With 116 servers, the queuing probability is 0.0782108608850327. Trying 117 servers.
With 117 servers, the queuing probability is 0.06370976519997625. Trying 118 servers.
With 118 servers, the queuing probability is 0.05158368436937104. Trying 119 servers.
The minimum number of servers required is: 119 (queuing probability 0.04150970303605052)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&#39;s interesting to note that our initial guess has a very high probability of queuing. If we graph our findings, we see that Erlang&#39;s C formula is decreasing and convex in &lt;span class=&quot;katex&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;mrow&gt;&lt;mi&gt;S&lt;/mi&gt;&lt;/mrow&gt;&lt;/math&gt;&lt;/span&gt;:&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/erlang-c.svg&quot; alt=&quot;erlang c&quot; loading=&quot;lazy&quot;&gt;&lt;h2 id=&quot;beyond-the-erlang-model&quot;&gt;Beyond the Erlang model&lt;/h2&gt;
&lt;p&gt;Our model here gives the impression that throughput scales linearly with concurrency. In practice, it does not.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Amdahl%27s_law&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Amdahl&#39;s law&lt;/a&gt; and the &lt;a href=&quot;https://en.wikipedia.org/wiki/Neil_J._Gunther#Universal_Law_of_Computational_Scalability&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Universal Scalability Law&lt;/a&gt; remind us that as we increase concurrency, total speedup is limited by sequential computation, as well as contention and coherence issues. &lt;/p&gt;
&lt;p&gt;Amdahl&#39;s law states that any speedup using multiple processors is limited by the time needed for the sequential fraction of the program. Similarly, for USL, our system may depend on a shared resource like a database, or require coordination to keep data consistent across multiple servers. Ultimately, the throughput we get from increasing concurrency diminishes with scale.&lt;/p&gt;
&lt;p&gt;Perhaps we could measure the contention and coherence factors empirically as we scale, in pursuit of a better model.&lt;/p&gt;
&lt;h3 id=&quot;mmc-queues&quot;&gt;M/M/c queues&lt;/h3&gt;
&lt;p&gt;The analysis also relies on the &lt;a href=&quot;https://en.wikipedia.org/wiki/M/M/c_queue&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;M/M/c queue model&lt;/a&gt;, characterized by three properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Markovian (or Poisson) arrival process:&lt;/strong&gt; Jobs arrive independently at a constant average rate. In simpler terms, the timing of the next job arrival does not depend on when the previous job arrived — this is what we mean by &quot;Markovian&quot;. The arrivals could be at any time, and they are spaced apart randomly but with a constant average rate. &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Markovian service times:&lt;/strong&gt; Similar to arrivals, the time it takes to service the next job does not depend on how long it took to service the previous job, and there is a constant average rate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;c servers:&lt;/strong&gt; There are c identical servers each operating at the same average service rate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many real-world systems don&#39;t adhere to these assumptions. For example, if clients retry requests or make sequential requests during page load, then arrivals are no longer memoryless — there are dependencies between request arrival times. Similarly, if servers contend for a common database under load, service times may no longer be independent. And if an application has peak times, then request arrival times are unlikely to be Markovian, since the arrival process depends on the time of day.&lt;/p&gt;
&lt;p&gt;The model is a simplification, and we must always keep that in mind. It can still useful, if we are judicious in how we apply it. For example, perhaps it&#39;s better to use throughput and response time at peak times, rather than their averages, even if it means over-provisioning most of the time. We must recognize that there might not be a definitive answer, but instead, an ongoing exploration that balances the tension of theory, observation, and good old tinkering.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;For an &lt;a href=&quot;https://en.wikipedia.org/wiki/M/M/c_queue&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;M/M/c&lt;/a&gt; queue. See the last section for caveats. &lt;a href=&quot;https://markhudnall.com/2023/06/09/concurrency-and-queuing-theory/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Three things about Common Lisp</title>
      <link>https://markhudnall.com/2021/02/27/three-things-about-common-lisp/</link>
      <guid isPermaLink="true">https://markhudnall.com/2021/02/27/three-things-about-common-lisp/</guid>
      <pubDate>Sat, 27 Feb 2021 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;I continue to have my mind blown by Common Lisp. Here are three things I&#39;ve been thinking about.&lt;/p&gt;
&lt;h2 id=&quot;1-error-handling&quot;&gt;1. Error handling&lt;/h2&gt;
&lt;p&gt;Common Lisp made me realize I was living with a fixed mental model of error handling. Swimming in water, so to speak. To me, error handling meant try/catch or returning errors directly from functions a la golang or promises. In either case, control is relinquished at the error-site and bubbled up the stack to an error handler. The flow of control continues with the error handler.&lt;/p&gt;
&lt;p&gt;The Common Lisp condition system, on the other hand, separates flow of control from the error handler. A low-level piece of code can define multiple ways to handle an error that it might throw. A higher-level function can then decide which path to actually take when the error is thrown. Control flow depends on that decision: it is possible to just skip the error and continue on with the low-level code, if that is one of the error handling options that the low-level code provides.&lt;/p&gt;
&lt;p&gt;This is simply not possible in the try/catch paradigm without directly handling the error at the error-site, which means we lose generality: we are now stuck with a specific error handling implementation without the ability to customize.&lt;/p&gt;
&lt;p&gt;The chapter, &lt;a href=&quot;http://www.gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Beyong Exception Handling: Conditions and Restarts&lt;/a&gt; from Practical Common Lisp explains the condition system very well.&lt;/p&gt;
&lt;h2 id=&quot;2-dynamic-variables&quot;&gt;2. Dynamic variables&lt;/h2&gt;
&lt;p&gt;Dynamic variables are like global variables with a twist. We are used to lexical scoping, where a variable is defined in some scope and can be accessed by any inner scope that is literally nested within the outer one. Dynamic variables, on the other hand, are &quot;dynamically scoped&quot;. When a dynamic variable is bound to a value, this binding lives on for the duration of that scope, surviving function calls. The binding is pushed onto a stack, so when the scope ends, the binding is popped and the variable returns to its former value. This differs from a global variable, where &quot;re-binding&quot; the variable merely sets it to a different value.&lt;/p&gt;
&lt;p&gt;I&#39;ve seen dynamic variables before in elisp and clojure. I&#39;ve almost never used them in clojure, and I&#39;ve used them sparingly in emacs when it was the only possible way to customize some value for the duration of a block. I equated dynamic variables with global variables, and I&#39;ve seen the messiness of relying on global variables.&lt;/p&gt;
&lt;p&gt;In Common Lisp... they just make sense for some reason. Maybe it&#39;s the fact that they&#39;re used so much more in Common Lisp code I&#39;ve been working with, but I find it incredibly useful to have a value that is &quot;globally&quot; accessible (but only for a certain, well-defined lifetime) and to be able to customize that variable non-locally.&lt;/p&gt;
&lt;p&gt;I see the pitfalls too. Since any function inside a scope can set a dynamic variable, it makes data flow unclear. Very easy to forget that the variable can be set non-locally and write code that assumes the value I set 3 lines above in a &lt;code&gt;let&lt;/code&gt; binding is the current one.&lt;/p&gt;
&lt;p&gt;There&#39;s a sort of similar pitfall with the condition system: my code cannot necessarily assume that throwing an error means control will end at that location.&lt;/p&gt;
&lt;h2 id=&quot;3-interactivity&quot;&gt;3. Interactivity&lt;/h2&gt;
&lt;p&gt;Common Lisp is intensely interactive. It is possible (and seemingly common), for example, to embed a &lt;a href=&quot;https://common-lisp.net/project/slime/doc/html/Setting-up-the-lisp-image.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Swank&lt;/a&gt; server in a production deployment. This allows you to connect to the running production system in a REPL. You can change live data, debug problems, hotswap code, etc. You might have also heard &lt;a href=&quot;https://www.youtube.com/watch?v=_gZK0tW8EhQ&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;the story of lisp at the JPL&lt;/a&gt;, where they debugged a problem over a REPL through space.&lt;/p&gt;
&lt;p&gt;Interactivity is at the core of the language. Installing dependencies happens at the REPL inside the running lisp process, rather than through an out-of-process package manager. Unhandled errors automatically invoke the interactive debugger rather than panicking. Standalone executables, which are quite large, are actually lisp &quot;images&quot;: dumps of all data from a running lisp process (running processes can be dumped and restored at any time).&lt;/p&gt;
&lt;p&gt;This interactivity changes the programming experience dramatically. I frequently find myself writing programs from the &quot;bottom up&quot;, starting with an inner piece that I incrementally evaluate in the REPL and build on. Moreover, the feedback loop is tight; writing lisp is &lt;em&gt;fun&lt;/em&gt;. &lt;/p&gt;
&lt;p&gt;There are drawbacks here too. The problem with &quot;images&quot; is that the textual code and the image can diverge. The symptoms are starting up a fresh REPL only to be greeted by &quot;The variable FOO is unbound&quot;. The root cause is usually having set some variable during a REPL session to a value that is defined further down in the code. A minor headache, as long as it doesn&#39;t get deployed to production.&lt;/p&gt;
</description>
    </item>
<item>
      <title>First impressions of Nix</title>
      <link>https://markhudnall.com/2021/01/27/first-impressions-of-nix/</link>
      <guid isPermaLink="true">https://markhudnall.com/2021/01/27/first-impressions-of-nix/</guid>
      <pubDate>Wed, 27 Jan 2021 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;Nix is package management from the future. But like &lt;a href=&quot;https://everything2.com/title/Starseed+Transmissions&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;other transmissions from our future selves&lt;/a&gt;, it&#39;s tough to understand what Nix really is, why it matters, and what you can do with it. &lt;/p&gt;
&lt;p&gt;I started using Nix and I&#39;m not looking back. Let me tell you how I think about Nix and why it&#39;s worth learning.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Caveat lector:&lt;/strong&gt;&lt;/em&gt; Nix is difficult to get started with on macOS. For more thoughts on this and other pain points, see &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#challenges&quot;&gt;Challenges&lt;/a&gt;. I encourage perseverance (and perhaps a stiff drink) as you explore this brain-changing technology.&lt;/p&gt;
&lt;h2 id=&quot;what-is-nix&quot;&gt;What is Nix?&lt;/h2&gt;
&lt;p&gt;Nix is a package manager like &lt;a href=&quot;https://brew.sh/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Homebrew&lt;/a&gt; or &lt;a href=&quot;https://wiki.archlinux.org/index.php/Pacman&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pacman&lt;/a&gt;. We can install ripgrep with Nix to get a feel for it:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh hljs&quot;&gt;$ nix-env --install --attr nixpkgs.ripgrep
installing &lt;span class=&quot;hljs-string&quot;&gt;&#39;ripgrep-12.1.1&#39;&lt;/span&gt;
building &lt;span class=&quot;hljs-string&quot;&gt;&#39;/nix/store/0l147binjrsqsq9bnh1zn2xnfnmxv2lf-user-environment.drv&#39;&lt;/span&gt;...
created 708 symlinks &lt;span class=&quot;hljs-keyword&quot;&gt;in&lt;/span&gt; user environment
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;ripgrep is now installed:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh hljs&quot;&gt;$ rg --version
ripgrep 12.1.1
-SIMD -AVX (compiled)
+SIMD +AVX (runtime)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where did Nix install it?&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-sh hljs&quot;&gt;$ &lt;span class=&quot;hljs-built_in&quot;&gt;which&lt;/span&gt; rg
/Users/mark/.nix-profile/bin/rg

$ &lt;span class=&quot;hljs-built_in&quot;&gt;readlink&lt;/span&gt; $(&lt;span class=&quot;hljs-built_in&quot;&gt;which&lt;/span&gt; rg)
/nix/store/43m8p9f7zbjnf959rhx5mzls5c28y2gk-ripgrep-12.1.1/bin/rg
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With Nix, package names (&lt;code&gt;43m8p9f7zbjnf959rhx5mzls5c28y2gk-ripgrep-12.1.1&lt;/code&gt;) are constructed by hashing a package&#39;s source files with the names of all its dependencies. A human-readable name is then appended to this hash. This means that if the source of a package or any of its dependencies change, the name of the package changes too.&lt;/p&gt;
&lt;p&gt;Packages reside in the &lt;strong&gt;Nix store&lt;/strong&gt;, which is just the directory &lt;code&gt;/nix/store&lt;/code&gt;. Nix requires that all package dependencies also be in the Nix store. In this way, the Nix store forms a graph where nodes are packages and edges are dependency relationships.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Nix also has an &lt;strong&gt;expression language&lt;/strong&gt;, which is used to define packages. With Nix, these package definitions are called derivations. The syntax is somewhat alienating for newcomers, so let&#39;s look at an example to familiarize ourselves. Here is the derivation for ripgrep, which is &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/text/ripgrep/default.nix&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;available on&lt;/a&gt; &lt;strong&gt;nixpkgs&lt;/strong&gt;, Nix&#39;s primary package repository:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nix hljs&quot;&gt;{ stdenv
, fetchFromGitHub
, rustPlatform
, asciidoctor
, installShellFiles
, Security
, withPCRE2 &lt;span class=&quot;hljs-operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;
, pcre2 &lt;span class=&quot;hljs-operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt;
}:

rustPlatform.buildRustPackage &lt;span class=&quot;hljs-keyword&quot;&gt;rec&lt;/span&gt; {
  &lt;span class=&quot;hljs-attr&quot;&gt;pname&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;ripgrep&quot;&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;version&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;12.1.1&quot;&lt;/span&gt;;

  &lt;span class=&quot;hljs-attr&quot;&gt;src&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; fetchFromGitHub {
    &lt;span class=&quot;hljs-attr&quot;&gt;owner&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;BurntSushi&quot;&lt;/span&gt;;
    &lt;span class=&quot;hljs-attr&quot;&gt;repo&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; pname;
    &lt;span class=&quot;hljs-attr&quot;&gt;rev&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; version;
    &lt;span class=&quot;hljs-attr&quot;&gt;sha256&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;1hqps7l5qrjh9f914r5i6kmcz6f1yb951nv4lby0cjnp5l253kps&quot;&lt;/span&gt;;
  };

  &lt;span class=&quot;hljs-attr&quot;&gt;cargoSha256&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;03wf9r2csi6jpa7v5sw5lpxkrk4wfzwmzx7k3991q3bdjzcwnnwp&quot;&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;cargoBuildFlags&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; stdenv.lib.optional withPCRE2 &lt;span class=&quot;hljs-string&quot;&gt;&quot;--features pcre2&quot;&lt;/span&gt;;

  &lt;span class=&quot;hljs-attr&quot;&gt;nativeBuildInputs&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; [ asciidoctor installShellFiles ];
  &lt;span class=&quot;hljs-attr&quot;&gt;buildInputs&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; (stdenv.lib.optional withPCRE2 pcre2)
  &lt;span class=&quot;hljs-operator&quot;&gt;++&lt;/span&gt; (stdenv.lib.optional stdenv.isDarwin Security);
  &lt;span class=&quot;hljs-operator&quot;&gt;//&lt;/span&gt; ...
  &lt;span class=&quot;hljs-attr&quot;&gt;meta&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; stdenv.lib; {
    &lt;span class=&quot;hljs-attr&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;A utility that combines the usability of The Silver Searcher with the raw speed of grep&quot;&lt;/span&gt;;
    &lt;span class=&quot;hljs-attr&quot;&gt;homepage&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;https://github.com/BurntSushi/ripgrep&quot;&lt;/span&gt;;
    &lt;span class=&quot;hljs-attr&quot;&gt;license&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; licenses; [ unlicense &lt;span class=&quot;hljs-comment&quot;&gt;/* or */&lt;/span&gt; mit ];
    &lt;span class=&quot;hljs-attr&quot;&gt;maintainers&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;with&lt;/span&gt; maintainers; [ tailhook globin ma27 zowoq ];
  };
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We are looking at an anonymous function that takes a single argument, an attribute set. An attribute set is like a hashmap where keys are strings. The attribute set used as input argument has been destructured as part of the function definition and contains the keys &lt;code&gt;stdenv&lt;/code&gt;, &lt;code&gt;fetchFromGitHub&lt;/code&gt;, &lt;code&gt;rustPlatform&lt;/code&gt;, &lt;code&gt;asciidoctor&lt;/code&gt;, etc. The function calls &lt;code&gt;rustPlatform.buildRustPackage&lt;/code&gt; to define the package, passing in another attribute set that specifies the configuration necessary to build ripgrep. In particular, it specifies that the ripgrep source (the &lt;code&gt;src&lt;/code&gt; key) should be fetched from GitHub. Many of the details are hidden inside the function &lt;code&gt;buildRustPackage&lt;/code&gt;, which ultimately specifies that &lt;code&gt;src&lt;/code&gt; should be built using &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/rust/default.nix#L186&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;cargo build&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Note that the above function only has access to its input arguments. These input arguments are either utilities functions and namespaces (like &lt;code&gt;stdenv&lt;/code&gt;, &lt;code&gt;fetchFromGitHub&lt;/code&gt;, and &lt;code&gt;rustPlatform&lt;/code&gt;) or other packages (like &lt;code&gt;asciidoctor&lt;/code&gt; and &lt;code&gt;Security&lt;/code&gt;&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;), both defined using the Nix expression language.&lt;/p&gt;
&lt;p&gt;If your package relies on other packages as build or runtime dependencies, you&#39;ll declare these dependencies using the Nix expression language, as ripgrep does above with &lt;code&gt;nativeBuildInputs&lt;/code&gt; and &lt;code&gt;buildInputs&lt;/code&gt;. Nix computes the full path to those dependencies (something like &lt;code&gt;/nix/store/&amp;lt;a-hash&amp;gt;-your-dependency&lt;/code&gt;) and makes them available through &lt;code&gt;$PATH&lt;/code&gt; or other environment variables when it actually builds your package. Nix makes sure those dependencies are built before it builds your package.&lt;/p&gt;
&lt;p&gt;So when ripgrep is built by Nix, it is actually built using Nix-packaged &lt;code&gt;rustc&lt;/code&gt; and &lt;code&gt;cargo&lt;/code&gt; 
(&lt;code&gt;/nix/store/189i8cpfdr758nplhviw59qcp539q4l2-rustc-1.49.0/bin/rustc&lt;/code&gt; and &lt;code&gt;/nix/store/d6bynqj12ak4dqqgk1wvw3pwrxym9xw3-cargo-1.49.0/bin/cargo&lt;/code&gt; respectively at the time of writing).&lt;sup&gt;&lt;a id=&quot;footnote-ref-3&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-big-idea&quot;&gt;The big idea&lt;/h2&gt;
&lt;p&gt;To recap: Nix names packages by hashing their source files along with the names of all their dependencies. Thus, updating a package&#39;s source or any of its dependencies changes this hash, resulting in a new package with a new name. Nix puts packages and their dependencies in the Nix store, so it has a global view of all packages. Since changes to packages always result in a new name, the Nix store is append-only. &lt;/p&gt;
&lt;p&gt;The implications of this are staggering:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple versions of the same package can peacefully coexist on the same system, since they are guaranteed to have different names. &lt;/li&gt;
&lt;li&gt;Once a package is built (for a given platform), it has a globally unique name and can thus be globally cached. In fact, Nix does this automatically, so most packages are actually just downloaded from &lt;a href=&quot;http://cache.nixos.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;cache.nixos.org&lt;/a&gt; rather than built from source on your computer.&lt;/li&gt;
&lt;li&gt;Rollbacks to previous versions are trivial: just change symlinks back to what they were before. Nix tools like &lt;code&gt;nix-env --rollback&lt;/code&gt; make this easy to do.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;a-motivating-example-homebrew-and-openssl&quot;&gt;A motivating example: Homebrew and openssl&lt;/h3&gt;
&lt;p&gt;To better understand this paradigm shift, consider &lt;a href=&quot;https://github.com/rbenv/ruby-build/issues/1353&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this issue&lt;/a&gt; regarding Homebrew, openssl, and rbenv or &lt;a href=&quot;https://github.com/pyenv/pyenv/issues/1686&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this similar one&lt;/a&gt; for pyenv. Or consider the dreaded &lt;a href=&quot;https://stackoverflow.com/questions/59006602/dyld-library-not-loaded-usr-local-opt-openssl-lib-libssl-1-0-0-dylib&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;dyld: Library not loaded&lt;/code&gt;&lt;/a&gt; issues when upgrading macOS. I have encountered these issues myself, resulting in hours of frustration. Multiply this by the # of engineers in your org and you have real productivity loss, especially if your org keeps dependencies up-to-date or mandates regular OS updates (which it should).&lt;/p&gt;
&lt;p&gt;Now these problems can be solved by hand. In the first case, openssl v1.0.x was end-of-lifed, so Homebrew dropped the formula that lets you build it. Unfortunately, old ruby versions rely on this old version of openssl, so it must be manually installed and the ruby build retried with &lt;code&gt;--with-openssl-dir=/path/to/openssl-v1.0.x&lt;/code&gt;&lt;sup&gt;&lt;a id=&quot;footnote-ref-4&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;. In the second case, openssl is dynamically linked at runtime, but its &lt;a href=&quot;https://softwareengineering.stackexchange.com/questions/66305/what-is-a-dylib&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;dylib&lt;/a&gt; has disappeared during an OS upgrade. The path to the nonexistent dylib needs to be changed &lt;a href=&quot;https://stackoverflow.com/a/29809480/826650&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;with &lt;code&gt;install_name_tool&lt;/code&gt;&lt;/a&gt; on the binary that uses it.&lt;/p&gt;
&lt;p&gt;But here be dragons. The &lt;a href=&quot;https://stackoverflow.com/a/59184347/826650&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;highly upvoted and accepted answer&lt;/a&gt; for the dylib issue has you re-link the old, end-of-lifed version of openssl. Another answer has you install openssl using &lt;a href=&quot;https://stackoverflow.com/a/59462770/826650&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;some random formula&lt;/a&gt;. These are &lt;strong&gt;bad ideas&lt;/strong&gt; for something like openssl. Yet, I have done these things myself because, like most developers, I just want to get on with the task at hand.&lt;/p&gt;
&lt;h3 id=&quot;so-whats-the-underlying-problem&quot;&gt;So what&#39;s the underlying problem?&lt;/h3&gt;
&lt;p&gt;Rich Hickey sums it up well in his excellent talk, &lt;a href=&quot;https://www.youtube.com/watch?v=oyLBGkS5ICk&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Spec-ulation&lt;/a&gt;. The situation is something like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Software has dependencies&lt;/li&gt;
&lt;li&gt;Dependencies are referenced by name&lt;/li&gt;
&lt;li&gt;Names are resolved by the &quot;environment&quot;&lt;/li&gt;
&lt;li&gt;The environment changes out from under you, breaking name resolution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unsurprisingly, this pattern — let&#39;s call it &quot;naming stuff&quot; — pervades software design:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Functions call other functions. Import statements set up the environment where function names can be resolved. &lt;/li&gt;
&lt;li&gt;Import statements reference other namespaces. Package declarations (&lt;code&gt;package.json&lt;/code&gt;, etc.) set up the environment where namespaces can be resolved. &lt;/li&gt;
&lt;li&gt;Package declarations reference other packages by artifact name or URL. Package registries or DNS set up the environment where artifacts can be resolved.&lt;/li&gt;
&lt;li&gt;Shells reference commands by name. &lt;code&gt;$PATH&lt;/code&gt; sets up the environment where commands can be resolved to paths in the filesystem.&lt;/li&gt;
&lt;li&gt;Filesystem paths are names too. &lt;a href=&quot;https://en.wikipedia.org/wiki/Inode&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;inodes&lt;/a&gt; set up the environment where paths can be resolved to block locations on disk.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The problem is that associations between names and their &lt;a href=&quot;https://en.wikipedia.org/wiki/Referent&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;referents&lt;/a&gt; are mutable. Names, while convenient for human beings, hide changes in the environment. The nature of the relationship between names and their referents is a &lt;a href=&quot;https://en.wikipedia.org/wiki/Ship_of_Theseus&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;longstanding philosophical question&lt;/a&gt;. Applied to programming, we might ask:&lt;/p&gt;
&lt;p&gt;If I make a breaking change to a function, is it still the same function? &lt;/p&gt;
&lt;p&gt;If I have openssl v1.0.2 and openssl v1.1.1, which one is &quot;openssl&quot;?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://semver.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;SemVer&lt;/a&gt; is supposed to resolve this dilemma by separating breaking and non-breaking changes into major and minor/patch versions. But since there is no formal relationship between artifact names and namespaces, SemVer doesn&#39;t really fix the underlying issue. This becomes clear in &quot;transitive dependency hell&quot;, when Package A depends on B and C, where B depends on D v1.0 and C depends on D v2.0. What are we importing when we write &lt;code&gt;import D&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Nix solves this problem for packages by making every name correspond one-to-one with a referent&lt;sup&gt;&lt;a id=&quot;footnote-ref-5&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-5&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt;. If a package&#39;s source or dependencies change, it is a different thing with a different name. There can be no question about which version the name &quot;openssl&quot; refers to, because Nix makes the name &quot;openssl&quot; impossible (or at least, makes it have only a single referent in the context of a given package that depends on it).&lt;/p&gt;
&lt;p&gt;In essence, Nix makes the package environment append-only. New names can be added, but existing associations between names and their referents are immutable. &lt;/p&gt;
&lt;p&gt;So to answer the age-old philosophical question, &quot;ship of Theseus&quot; is a symlink to &lt;code&gt;/nix/store/j2pic1a5ijcpbs494vdhdbafn7y50ppr-ship-of-theseus&lt;/code&gt; that is updated to &lt;code&gt;/nix/store/rmqbimj5mqnshfr3rgffk8h0flx7k911-ship-of-theseus&lt;/code&gt; when the first plank of wood is replaced.&lt;/p&gt;
&lt;h2 id=&quot;challenges&quot;&gt;Challenges&lt;/h2&gt;
&lt;p&gt;I ran into both practical and conceptual difficulties with Nix. Ultimately, my opinion is that Nix is hard to install and use for newcomers but that its problems can be overcome through education  and automation.&lt;/p&gt;
&lt;p&gt;Here is a non-exhaustive list of head-scratchers I&#39;ve encountered in my Nix journey to date.&lt;/p&gt;
&lt;h3 id=&quot;installing-on-macos-catalina-and-later&quot;&gt;Installing on macOS Catalina and later&lt;/h3&gt;
&lt;p&gt;Nix is difficult to install on macOS. The primary issue is Catalina&#39;s root filesystem is read-only, so &lt;code&gt;/nix&lt;/code&gt; cannot be created like a regular directory. Nix assumes that it has write access to &lt;code&gt;/nix&lt;/code&gt; and derivations &lt;a href=&quot;https://nixos.org/guides/nix-pills/install-on-your-running-system.html#idm140737320754336&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;use absolute paths&lt;/a&gt; when referring to other objects in the Nix store. The workaround, which has been automated &lt;a href=&quot;https://nixos.org/manual/nix/stable/#sect-macos-installation&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;by the Nix installer&lt;/a&gt;, is to create a separate volume for the Nix store and mount it at &lt;code&gt;/nix&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;But the installer did not work for me; the Nix installer creates a volume that relies on the T2 chip&#39;s encryption at rest rather than FileVault, and my laptop does not have a T2 chip. This issue is &lt;a href=&quot;https://github.com/NixOS/nix/pull/3692&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documented here&lt;/a&gt;. As suggested &lt;a href=&quot;https://nixos.org/manual/nix/stable/#sect-macos-installation-encrypted-volume&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;by the docs&lt;/a&gt;, the fix is to manually create a Filevault-encrypted volume. The installer does not do this automatically because of race conditions between services that use Nix on startup and mounting + decrypting the volume, which causes some applications to fail on startup. &lt;/p&gt;
&lt;p&gt;Thankfully, an &lt;a href=&quot;https://github.com/NixOS/nix/pull/4289&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;open PR&lt;/a&gt; is set to fix this race condition and automate creating the Filevault-encrypted volume during installation. I used the preview installer in the open PR&#39;s description to get up and running.&lt;/p&gt;
&lt;h4 id=&quot;nix-darwin&quot;&gt;nix-darwin&lt;/h4&gt;
&lt;p&gt;The next challenge was understanding and installing &lt;a href=&quot;https://github.com/LnL7/nix-darwin&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nix-darwin&lt;/a&gt;. nix-darwin&#39;s README describes it as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Nix modules for darwin, &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; for macOS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Huh? Basically, nix-darwin gives you a way codify configuration for a macOS computer using the Nix expression language. Configuration ranges from networking and users to installed applications and CLI tools. This is the same role that NixOS plays for Linux, but less tightly integrated in nix-darwin due to macOS&#39;s more restrictive platform. With NixOS, I would configure my system by editing a file located at &lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt;. With nix-darwin, the analogous file defaults to &lt;code&gt;~/.nixpkgs/darwin-configuration.nix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, to define a user, I can write:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nix hljs&quot;&gt;u&lt;span class=&quot;hljs-attr&quot;&gt;sers.users.mark&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; {
  &lt;span class=&quot;hljs-attr&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;mark&quot;&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;home&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-string&quot;&gt;&quot;/Users/mark&quot;&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;shell&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; pkgs.zsh;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And I can install system-wide packages using:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nix hljs&quot;&gt;e&lt;span class=&quot;hljs-attr&quot;&gt;nvironment.systemPackages&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; [ pkgs.ripgrep ];
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When I run &lt;code&gt;darwin-rebuild switch&lt;/code&gt;, nix-darwin updates my system to reflect any changes to the config by building new packags and updating symlinks on my &lt;code&gt;$PATH&lt;/code&gt;. If the update fails for any reason, I can roll back to the previous version instantaneously with &lt;code&gt;darwin-rebuild --rollback&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;nix-darwin is structured internally using &lt;a href=&quot;https://nixos.wiki/wiki/NixOS#Modules&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;NixOS modules&lt;/a&gt;, a convention where Nix files define available configuration options for some part of the system and then implement the configuration logic. The &lt;a href=&quot;https://daiderd.com/nix-darwin/manual/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;documentation&lt;/a&gt; helps with understanding the available configuration options.&lt;/p&gt;
&lt;p&gt;I ran into some issues installing nix-darwin that I can&#39;t remember now, but they were solved using workaround in various GitHub issues.&lt;/p&gt;
&lt;h4 id=&quot;applications-and-spotlight&quot;&gt;Applications and spotlight&lt;/h4&gt;
&lt;p&gt;A big issue I ran into was making Nix-installed macOS applications like Firefox work well with &lt;a href=&quot;https://www.alfredapp.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Alfred&lt;/a&gt;. nix-darwin symlinks macOS applications built with Nix into &lt;code&gt;~/Applications/Nix Apps&lt;/code&gt;. This works fine if you use the dock to launch applications, but Spotlight does not index symlinks and so it is not possible to open these applications using Spotlight or Alfred. &lt;/p&gt;
&lt;p&gt;At first, I tried to get Alfred to index my Nix store, but couldn&#39;t get this to work. A workaround suggested in &lt;a href=&quot;https://github.com/LnL7/nix-darwin/issues/139&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this GitHub issue&lt;/a&gt; is to use AppleScript to create macOS &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/%22https://en.wikipedia.org/wiki/Alias_(Mac_OS)%22&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;aliases&lt;/a&gt;, which, unlike symlinks, are indexed by Spotlight. Using AppleScript seems like a hack. Another user suggests a similar workaround &lt;a href=&quot;https://github.com/NixOS/nix/issues/1278#issuecomment-633934039&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;using a Swift script&lt;/a&gt; to create these aliases.&lt;/p&gt;
&lt;p&gt;I ended up combining these workarounds into a custom module. You can see the Nix module in my config &lt;a href=&quot;https://github.com/landakram/nix-config/blob/master/link-apps/default.nix&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;here&lt;/a&gt;. The module is enabled with the following line in my nix-darwin configuration:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-nix hljs&quot;&gt;s&lt;span class=&quot;hljs-attr&quot;&gt;ervices.link-apps&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; {
  &lt;span class=&quot;hljs-attr&quot;&gt;enable&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-literal&quot;&gt;true&lt;/span&gt;;
  &lt;span class=&quot;hljs-attr&quot;&gt;userName&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; config.users.users.mark.name;
  &lt;span class=&quot;hljs-attr&quot;&gt;userHome&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; config.users.users.mark.home;
};
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Applications that are installed with Nix are now aliased into &lt;code&gt;~/Applications/Nix&lt;/code&gt;, which can be indexed by Alfred. I also needed to configure Alfred to index aliases by going to &lt;code&gt;Alfred Preferences -&amp;gt; Features -&amp;gt; Default Results -&amp;gt; Extras -&amp;gt; Advanced...&lt;/code&gt; and dragging in one of the aliases in &lt;code&gt;$HOME/Applications/Nix&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It still feels like an ugly workaround, but it has worked well for my purposes ever since.&lt;/p&gt;
&lt;h3 id=&quot;paper-cuts&quot;&gt;Paper-cuts&lt;/h3&gt;
&lt;p&gt;Beyond the installation issues and conceptual hurdles (Nix&#39;s novel paradigm of content-addressing packages), Nix gave me some paper-cuts. These tidbits of &lt;a href=&quot;https://en.wikipedia.org/wiki/No_Silver_Bullet&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;accidental complexity&lt;/a&gt; raise the barrier to entry but aren&#39;t inherent to Nix&#39;s new paradigm. &lt;/p&gt;
&lt;h4 id=&quot;nix-expression-language&quot;&gt;Nix expression language&lt;/h4&gt;
&lt;p&gt;Nix requires learning the Nix expression language, a purpose-built language that isn&#39;t re-usable in other contexts. I quickly needed to understand and wield the language when I ventured beyond just installing packages with &lt;code&gt;nix-env&lt;/code&gt; into creating reproducible environment with nix-darwin + &lt;a href=&quot;https://github.com/nix-community/home-manager&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;home-manager&lt;/a&gt; and, for projects, &lt;a href=&quot;https://nixos.wiki/wiki/Development_environment_with_nix-shell&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;shell.nix&lt;/a&gt; files.&lt;/p&gt;
&lt;p&gt;Now that I&#39;m more familiar with it, I actually like it a lot. But it created a higher upfront cost to learning Nix. I could just as easily see Nix working with YAML, widely used in automation tooling, but I suppose that DSLs need to be learned irrespective of the host language, and Nix&#39;s expression language gives it the power of modularity. So maybe I&#39;m just being grumpy.&lt;/p&gt;
&lt;h4 id=&quot;everything-is-an-attribute-set&quot;&gt;Everything is an attribute set&lt;/h4&gt;
&lt;p&gt;Nix attribute sets are nice data structures. nixpkgs is basically a big, nested attribute set, hence installing a package with e.g. &lt;code&gt;nix-env -iA nixpkgs.graphviz&lt;/code&gt; involves naming a derivation nested inside the top-level &lt;code&gt;nixpkgs&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;nixpkg utility functions such as &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/93b430bc6ba3c084d66f96546dd7b95a2835eceb/lib/options.nix#L58&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;mkOption&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/93b430bc6ba3c084d66f96546dd7b95a2835eceb/pkgs/development/compilers/rust/make-rust-platform.nix#L14-L16&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;&lt;code&gt;buildRustPackage&lt;/code&gt;&lt;/a&gt; are also defined using attribute sets. &lt;/p&gt;
&lt;p&gt;The facts that everything is an attribute set, the Nix expression language is dynamically typed, and documentation is sparse make discoverability a challenge. I often find myself poking around the nixpkgs source, hunting down function definition or derivations so I can see their inputs and understand what they do. As before, this steepens the learning curve.&lt;/p&gt;
&lt;h4 id=&quot;cli-tools&quot;&gt;CLI tools&lt;/h4&gt;
&lt;p&gt;Nix has many top-level CLI tools. There is &lt;code&gt;nix&lt;/code&gt;, &lt;code&gt;nix-env&lt;/code&gt;, &lt;code&gt;nix-shell&lt;/code&gt;, &lt;code&gt;nix-store&lt;/code&gt;, &lt;code&gt;nix-build&lt;/code&gt;, &lt;code&gt;nix-channel&lt;/code&gt;, &lt;code&gt;darwin-rebuild&lt;/code&gt; (or &lt;code&gt;nixos-rebuild&lt;/code&gt;), and others, for example. The self-titled tool, &lt;code&gt;nix&lt;/code&gt;, is used to set global configuration, while the others are used for Nix&#39;s various subsystems.&lt;/p&gt;
&lt;p&gt;These CLI tools reflect Nix&#39;s breadth, but I wish there was just one top-level &lt;code&gt;nix&lt;/code&gt; command and everything else was a subcommand. &lt;code&gt;nix&lt;/code&gt; &lt;em&gt;does&lt;/em&gt; have subcommands, but there are sometimes slight differences in functionality. For example, there&#39;s &lt;code&gt;nix develop&lt;/code&gt; but also &lt;code&gt;nix-shell&lt;/code&gt;. They have differences, but which one should I be using?&lt;sup&gt;&lt;a id=&quot;footnote-ref-6&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-6&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id=&quot;now-i-have-two-build-tools&quot;&gt;&quot;Now I have two build tools&quot;&lt;/h3&gt;
&lt;p&gt;I&#39;ve been using Nix for projects as well. I think of Nix as a superset of language-specific package managers like ruby&#39;s bundler and version managers like rbenv. In addition to managing packages and language versions, I can use Nix to install CLI tools that aren&#39;t language specific. Combined with &lt;a href=&quot;https://github.com/target/lorri&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;lorri&lt;/a&gt; and &lt;a href=&quot;https://direnv.net/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;direnv&lt;/a&gt;, all the tools I need when working on a given project are available when I &lt;code&gt;cd&lt;/code&gt; into its repo.&lt;/p&gt;
&lt;p&gt;But I find that using Nix for package management in projects is awkward. It makes sense: Nix makes builds reproducible by creating a content-hash for each dependency, but we have to bring builds into Nix-land to do this. So there are additional tools that convert dependency declarations like a &lt;code&gt;Gemfile&lt;/code&gt; or &lt;code&gt;go.mod&lt;/code&gt; into a bunch of Nix derivations in a &lt;code&gt;.nix&lt;/code&gt; file.&lt;sup&gt;&lt;a id=&quot;footnote-ref-7&quot; href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-7&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;7&lt;/a&gt;&lt;/sup&gt; I could see these getting out of date, especially when I&#39;m new to Nix and my head is more in the language-specific tools (this could be solved with linting). &lt;/p&gt;
&lt;p&gt;Moreover, there are subtle differences between the language tools and Nix. For example, both ruby and python allow packaging pre-compiled, platform-specific binaries rather than having users build from source. I don&#39;t know enough about what Nix does here under the hood, but it chooses to build from source and has failed for me in cases where the language specific tool doesn&#39;t. Theoretically, Nix builds packages in isolation, so perhaps the snags come from the impure parts of Nix that bridge to a specific platform. Or perhaps some packages have build instructions hard-coded for platforms that bypass Nix&#39;s attempts to sandbox builds. Either way, it&#39;s frustrating enough that, for the most part, I use Nix to bring in language-agnostic tooling but continue to use language-specific package managers.&lt;/p&gt;
&lt;p&gt;Docker sort of solves this with &lt;a href=&quot;https://docs.docker.com/develop/develop-images/multistage-build/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;multi-stage builds&lt;/a&gt;. But Docker is more about creating reproducible &lt;em&gt;run&lt;/em&gt; environments (where the run environment can also be used to help build another container) rather than reproducible builds. It doesn&#39;t prevent you from specifying &lt;code&gt;&quot;latest&quot;&lt;/code&gt; for your dependencies, whereas Nix always requires a hash, effectively pinning versions even if one is not explicitly specified. On the other hand, for Nix to create reproducible builds, the sources must be available somewhere.&lt;/p&gt;
&lt;p&gt;Whatever, I can just pin versions using my language-specific package manager and get on fine with Docker. But I much prefer developing on the host if I can. Docker has real costs: the images can be huge and Docker is layered with complexity. Docker on macOS is resource hungry, slow, and probably always will be because it is virtualized. For deployment, I love Docker. Unfortunately, I haven&#39;t used NixOS or &lt;a href=&quot;https://github.com/NixOS/nixops&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nixops&lt;/a&gt; enough to compare my experiences there.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;
&lt;p&gt;Despite my gripes, I still think Nix is &lt;a href=&quot;https://en.wikipedia.org/wiki/Solarpunk&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;solarpunk&lt;/a&gt;. It is a category-defining tool, and I hope its ideas become the backbone of software development. It has an elegance, and I appreciate that a globally shareable cache that benefits all developers springs forth naturally from this.&lt;/p&gt;
&lt;p&gt;A few resources have been invaluable on my Nix journey. Perhaps they will be helpful to you too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/playlist?list=PLRGI9KQ3_HP_OFRG6R-p4iFgMSK1t5BHs&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Burke Libbey&#39;s &quot;Nixology&quot; videos&lt;/a&gt;. These are excellent, practical videos on using Nix on macOS. Just enough detail about what&#39;s going on under the hood to pique my interest. Start here.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://christine.website/blog/how-i-start-nix-2020-03-08&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Christine Dodrill&#39;s &quot;How I Start: Nix&quot;&lt;/a&gt;. This was a huge help for understanding Nix in projects. Christine&#39;s other posts on Nix and NixOS are fantastic too.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nixos.org/guides/nix-pills/index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Nix pills&lt;/a&gt;. The official guide. Excellent for understanding what&#39;s going on underneath but maybe not the best starting point for practical use.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lawrencedunn.io/posts/2020-03-20-how-nix-instantiation-works/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Lawrence Dunn&#39;s &quot;How Nix Derivation Instantiation Works&quot;&lt;/a&gt;: A great deep drive into how Nix derivations are instantiated, that is, how we go from Nix expressions to hashes and build instructions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://nix.dev/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nix.dev&lt;/a&gt;. Useful supplemental docs and style guide.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Good luck.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Nix provides tools for inspecting the relationships between packages. For example, I can see all the dependencies of ripgrep by running &lt;code&gt;nix-store --query --requisites /nix/store/43m8p9f7zbjnf959rhx5mzls5c28y2gk-ripgrep-12.1.1&lt;/code&gt;. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;The macOS &lt;a href=&quot;https://developer.apple.com/documentation/security&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Security framework&lt;/a&gt; packaged &lt;a href=&quot;https://github.com/NixOS/nixpkgs/blob/master/pkgs/os-specific/darwin/apple-sdk/frameworks.nix#L99&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;for Nix&lt;/a&gt;. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-3&quot;&gt;
&lt;p&gt;You can verify this by running &lt;code&gt;nix-shell &#39;&amp;lt;nixpkgs&amp;gt;&#39; -A ripgrep&lt;/code&gt; and then &lt;code&gt;which cargo&lt;/code&gt;. &lt;code&gt;nix-shell&lt;/code&gt; puts you into the environment that Nix uses to build a given package. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-4&quot;&gt;
&lt;p&gt;Actually, &lt;code&gt;ruby-build&lt;/code&gt; was reworked to stop looking for Homebrew&#39;s version of openssl so this shouldn&#39;t be necessary anymore. Instead, it will download the version of openssl that it needs if it doesn&#39;t already exist. &lt;code&gt;pyenv&lt;/code&gt; still looks for Homebrew&#39;s openssl and so still suffers from this problem. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-5&quot;&gt;
&lt;p&gt;For solving the same problem in code, I&#39;m really excited about &lt;a href=&quot;https://www.unisonweb.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Unison&lt;/a&gt;. In particular, check out the documentation on &lt;a href=&quot;https://www.unisonweb.org/docs/refactoring/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Refactoring and modifying code in Unison&lt;/a&gt;. It&#39;s a trip. Unfortunately, it&#39;s hard to imagine Unison being used widely because it is both an entirely new paradigm and programming language. Of course, Nix is both of these things as well... &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-6&quot;&gt;
&lt;p&gt;FWIW, I use &lt;code&gt;nix-shell&lt;/code&gt;. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-6&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-7&quot;&gt;
&lt;p&gt;For &lt;code&gt;Gemfile&lt;/code&gt;, it&#39;s &lt;a href=&quot;https://github.com/nix-community/bundix&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bundix&lt;/a&gt; and for &lt;code&gt;go.mod&lt;/code&gt;, it&#39;s &lt;a href=&quot;https://github.com/nix-community/vgo2nix&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;vgo2nix&lt;/a&gt;. &lt;a href=&quot;https://markhudnall.com/2021/01/27/first-impressions-of-nix/#footnote-ref-7&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 7&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Widening circles</title>
      <link>https://markhudnall.com/2019/09/02/widening-circles/</link>
      <guid isPermaLink="true">https://markhudnall.com/2019/09/02/widening-circles/</guid>
      <pubDate>Mon, 02 Sep 2019 00:00:00 GMT</pubDate>
      <description>&lt;blockquote&gt;
&lt;p&gt;I live my life in widening circles&lt;br&gt;that reach out across the world.&lt;br&gt;I may not complete this last one&lt;br&gt;but I give myself to it.  &lt;/p&gt;
&lt;p&gt;I circle around God, around the primordial tower.&lt;br&gt;I’ve been circling for thousands of years&lt;br&gt;and I still don’t know: am I a falcon,&lt;br&gt;a storm, or a great song?  &lt;/p&gt;
&lt;p&gt;– Rainer Maria Rilke (translated by Joanna Macy and Anita Barrows)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The following generative art piece is made by repeatedly drawing circles with smaller and smaller diameters. &lt;a href=&quot;https://en.wikipedia.org/wiki/Perlin_noise&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Perlin noise&lt;/a&gt; is applied to both the colors and positions to produce some variation.&lt;/p&gt;
&lt;p&gt;You can click the piece (or refresh the page) to see a new rendition of it.&lt;/p&gt;
&lt;iframe src=&quot;https://markhudnall.com/writing-assets/widening-circles/&quot; title=&quot;Widening circles — interactive generative artwork&quot; class=&quot;writing-sketch&quot; sandbox=&quot;allow-scripts&quot;&gt;&lt;/iframe&gt;&lt;p&gt;The piece was inspired by the first 2 chapters of &lt;a href=&quot;http://zenbullets.com/book.php&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Generative Art&lt;/a&gt; by Matt Pearson, which has been a joy. Some selected quotes from its prose:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Without giving it much thought, we have turned into augmented beings existing in a world that is simultaneously real and virtual.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Computing is what a stream does as it finds its way downhill toward the ocean. It’s what the planets do as they move in their orbits. It’s what our bodies do as they maintain the balance needed to keep us upright. It’s what our DNA does as it unravels. Computing is what I’m doing now as I process these ideas and output them as text—and what your brain is doing as you read the words and form your own ideas as a result.  &lt;/p&gt;
&lt;p&gt;This is why I can say, without contradiction, that while I still find computers boring, I think computing is cool. The only place computers really come into it is in attempting to simulate these computations or creating new ones to rival those of the natural world.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;With design, the intention is to produce a visual that produces the same response in everyone who sees it—the intention (such as “street,” “retro,” or “subtle”) should be mostly unambiguous. With art, you’re still aiming to produce a response, but if that response is different in different people that doesn’t matter. It’s fine for one person to like a piece while another sneers. Even better, if one viewer loves the work, we would hope another might hate it. If we can foster such an extremity of reaction, it’d be a measure of success. Perhaps the only cardinal sin of art is to be boring.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Like the landscape gardener, the lot of the generative artist is to take naturally evolving phenomena and to fashion them into something aesthetically pleasing. It’s finding that point of balance between the beautiful unruliness of the natural world and the desired order of our ape brains. A garden that is unkempt and overgrown is unpleasing to us because it’s too far into the realm of the chaotic, whereas concreting the area instead is the tidiest, most ordered of solutions, which also removes all beauty.  &lt;/p&gt;
&lt;p&gt;The sweet spot is between the two, where the grass is neat and evenly cut but still no two blades are alike or move in perfect synchronicity—where the colors of the flowers are evenly balanced, but not in a way that is exact and precise. The sweet spot is where the “art” lives.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;With generative art, the autonomous system does all the heavy lifting; the artist only provides the instructions to the system and the initial conditions.  &lt;/p&gt;
&lt;p&gt;The artist’s role in the production process may be closer to that of a curator than a creator. You create a system, model it, nurture it, and refine it, but ultimately your ownership of the work produced may be no more than a parent’s pride in the work of their offspring.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The purpose of a tool is not only to extend our capabilities; it should also enhance the flow of our creativity. The newest software gives us the power to fashion the world in ways that would have been unimaginable only 50 years ago, but there is still a long way to go in terms of a natural artistry to their use.&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
<item>
      <title>Starting Figwheel in Emacs</title>
      <link>https://markhudnall.com/2016/04/25/starting-figwheel-in-emacs/</link>
      <guid isPermaLink="true">https://markhudnall.com/2016/04/25/starting-figwheel-in-emacs/</guid>
      <pubDate>Mon, 25 Apr 2016 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;When I use Emacs to write ClojureScript, I often jack in to CIDER just to start Figwheel and get to the ClojureScript REPL. After running &lt;code&gt;M-x cider-jack-in&lt;/code&gt;, I would type in the REPL: &lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-clojure hljs&quot;&gt;user&amp;gt; (&lt;span class=&quot;hljs-name&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;use&lt;/span&gt;&lt;/span&gt; &#39;figwheel-sidecar.repl-api)
user&amp;gt; (&lt;span class=&quot;hljs-name&quot;&gt;start-figwheel!&lt;/span&gt;)
user&amp;gt; (&lt;span class=&quot;hljs-name&quot;&gt;cljs-repl&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Turns out, there&#39;s a faster way to do this using the &lt;code&gt;cider-cljs-lein-repl&lt;/code&gt; variable. Add the following to your Emacs config:&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2016/04/25/starting-figwheel-in-emacs/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-emacs-lisp hljs&quot;&gt;(&lt;span class=&quot;hljs-name&quot;&gt;setq&lt;/span&gt; cider-cljs-lein-repl
      &lt;span class=&quot;hljs-string&quot;&gt;&quot;(do (require &#39;figwheel-sidecar.repl-api)
           (figwheel-sidecar.repl-api/start-figwheel!)
           (figwheel-sidecar.repl-api/cljs-repl))&quot;&lt;/span&gt;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, when I&#39;m in a ClojureScript project, I run &lt;code&gt;M-x cider-jack-in-clojurescript&lt;/code&gt; instead of &lt;code&gt;M-x cider-jack-in&lt;/code&gt;. The value of &lt;code&gt;cider-cljs-lein-repl&lt;/code&gt; is used to start the ClojureScript REPL, so figwheel starts automatically and puts me right into the cljs REPL. Magic!&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;I put this in the &lt;code&gt;:config&lt;/code&gt; block of a &lt;a href=&quot;https://github.com/jwiegley/use-package&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;use-package&lt;/a&gt; declaration for CIDER, if you&#39;re into that sort of thing. &lt;a href=&quot;https://markhudnall.com/2016/04/25/starting-figwheel-in-emacs/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Depth-first and breadth-first thinkers</title>
      <link>https://markhudnall.com/2015/12/21/depth-first-and-breadth-first-thinkers/</link>
      <guid isPermaLink="true">https://markhudnall.com/2015/12/21/depth-first-and-breadth-first-thinkers/</guid>
      <pubDate>Mon, 21 Dec 2015 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;This piece is about working on teams.&lt;/p&gt;
&lt;p&gt;Consider this problem: you&#39;re walking along a trail in the dense forest and you need to find your way out. In front of you, the trail forks over and over again as far as you can see. The exit might be miles away, or it might be a 5-minute walk down one of the forks – you&#39;re so turned around that you have no idea.&lt;/p&gt;
&lt;p&gt;If you were a poet, you might take the road less traveled. In computing, however, this is a very clear &lt;a href=&quot;https://en.wikipedia.org/wiki/Tree_traversal&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;tree traversal problem&lt;/a&gt;, and there are two methodical ways to go about it.&lt;/p&gt;
&lt;h2 id=&quot;depth-first-search&quot;&gt;Depth-first search&lt;/h2&gt;
&lt;p&gt;With depth-first search, you go as far as you can down one path before you backtrack.&lt;/p&gt;
&lt;p&gt;For instance, you keep walking down the rightmost path, ignoring all the forks, until you reach a dead-end. Then, you backtrack to the last fork with a road you hadn&#39;t taken and try that one, following the rightmost path like you did before. Reached a dead-end? You backtrack and repeat the process. &lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/dfs.png&quot; alt=&quot;dfs&quot; loading=&quot;lazy&quot;&gt;&lt;h2 id=&quot;breadth-first-search&quot;&gt;Breadth-first search&lt;/h2&gt;
&lt;p&gt;With breadth-first search, you explore all the paths closest to you before moving deeper.&lt;/p&gt;
&lt;p&gt;For example, you walk down the path on the right until you reach a fork. If you don&#39;t see a way out, you backtrack and walk down the path on the left. Still nothing? You backtrack again, back to path on the right to explore the two paths that lead from there. Then you repeat with the path on the left.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/bfs.png&quot; alt=&quot;bfs&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;You can imagine the trade-offs of both: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;With depth-first search, you go &lt;em&gt;really&lt;/em&gt; far down one path really quickly. That works out 👍for you if the exit happens to be far away, but if the exit is down the other path and really close, you won&#39;t discover it for a while.&lt;/li&gt;
&lt;li&gt;With breadth-first search, it takes a long time to reach an exit that&#39;s far away, but you&#39;ll never miss an exit on a path that&#39;s really close by.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tree traversal algorithms like DFS and BFS are used in many computing disciplines. For example, you can represent a chess game as a tree, where each &quot;fork in the road&quot; represents a different board configuration and each road is a possible move by one of the players. If you could explore this tree quickly, you could look ahead in the game and always make a move that gives you a leg up. Armed with this tree, a computer becomes a worthy chess opponent. &lt;/p&gt;
&lt;h2 id=&quot;thinking-as-a-tree&quot;&gt;Thinking as a tree&lt;/h2&gt;
&lt;p&gt;When you&#39;re starting a project or exploring a new idea, your thinking is like a tree. Each problem you need to solve relates to the last. Each idea branches into many others, until finally, the full scope of your project or idea stands in front of you.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2015/12/21/depth-first-and-breadth-first-thinkers/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;You&#39;d obviously like to enumerate this scope. This begs a question you should ask yourself: How do &lt;em&gt;I&lt;/em&gt; naturally walk this tree? &lt;strong&gt;Am I a depth-first or breadth-first thinker?&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/thinking_as_a_tree.png&quot; alt=&quot;thinking as a tree&quot; loading=&quot;lazy&quot;&gt;&lt;h2 id=&quot;working-on-a-team&quot;&gt;Working on a team&lt;/h2&gt;
&lt;p&gt;This process of discovery, like finding the exit in a winding forest, takes some time. And frequently, you&#39;re doing it with a slew of other folks in a room somewhere, possibly with a whiteboard.&lt;/p&gt;
&lt;p&gt;When I&#39;m working collaboratively like this, a couple things tend to happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Someone says something that I feel is irrelevant&lt;/li&gt;
&lt;li&gt;Someone moves too quickly in one direction and I can&#39;t follow what they&#39;re saying&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These encounters can be frustrating, but here&#39;s the thing: &lt;em&gt;everyone in the room is traversing the tree.&lt;/em&gt; We&#39;re all just in different places along the BFT—DFT spectrum.&lt;/p&gt;
&lt;p&gt;So the second question to ask yourself is: &lt;strong&gt;Which of my teammates are depth-first thinkers, and which are breadth-first?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you pose this question, you begin to understand why a detail that feels irrelevant to you matters to someone else (&lt;em&gt;what path are they on compared to my own?&lt;/em&gt;). You also come to know why someone&#39;s thinking can move so quickly in one direction (&lt;em&gt;how deep down a path are they compared to me?&lt;/em&gt;)&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/working_with_a_tree_team.png&quot; alt=&quot;working with a tree team&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;Let&#39;s go back to the forest for a second. &lt;/p&gt;
&lt;p&gt;Imagine now that, instead of being lost by yourself, all of your teammates are with you. By now, you&#39;ve figured out whether you&#39;re a depth-first thinker or a breadth-first thinker. You&#39;ll be able to find an exit more quickly by nature of the fact that you have more people to help you.&lt;/p&gt;
&lt;p&gt;But if your teammates all think like you, you&#39;ll go down the same path together and you won&#39;t be any better off than when you were alone.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;People who think differently than you see things that you can&#39;t.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you&#39;re working on a team, first recognize what kind of thinker you are. Then, recognize what kinds of thinkers your teammates are. You&#39;ll be more patient and create better ideas.&lt;/p&gt;
&lt;p&gt;If you&#39;re building a team, seek out different kinds of thinkers. They&#39;ll give you vision that&#39;s impossible without them.&lt;/p&gt;
&lt;p&gt;Otherwise, you&#39;ll miss the tree for the forest.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Thinking as a tree is a simplification. Your thinking is probably more like a &lt;a href=&quot;https://en.wikipedia.org/wiki/Graph_(mathematics)&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;graph&lt;/a&gt; since ideas &lt;em&gt;between&lt;/em&gt; branches are also interrelated. The concept still holds, since one &lt;a href=&quot;https://en.wikipedia.org/wiki/Graph_traversal&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;traverses a graph&lt;/a&gt; in the same manner. &lt;a href=&quot;https://markhudnall.com/2015/12/21/depth-first-and-breadth-first-thinkers/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Try</title>
      <link>https://markhudnall.com/2015/05/19/try/</link>
      <guid isPermaLink="true">https://markhudnall.com/2015/05/19/try/</guid>
      <pubDate>Tue, 19 May 2015 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;I tie my self-worth to what I do and whether I succeed. I call this the &lt;em&gt;“Do or do not, there is no try”&lt;/em&gt; philosophy.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2015/05/19/try/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But I can spend all day &lt;em&gt;doing&lt;/em&gt; without really &lt;em&gt;trying&lt;/em&gt;. When it comes to my self-worth, the more important philosophy is simpler:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Try.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When I follow the &lt;em&gt;“Do or do not”&lt;/em&gt; philosophy, I pin my self-worth to whether I did something or didn’t, however improbable the odds of success. I end up judging myself by things that are out of my control. When the odds aren’t in my favor, I become afraid to even begin.&lt;/p&gt;
&lt;p&gt;When I &lt;em&gt;try&lt;/em&gt;, I’m pushed to my limit, which isn’t always far enough to succeed.&lt;/p&gt;
&lt;p&gt;In other words, I fail.&lt;/p&gt;
&lt;p&gt;At so many things.&lt;/p&gt;
&lt;p&gt;When I try and fail, I learn. When I learn, I get better.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Doing&lt;/em&gt; has never been the destination, but rather, the false means I’ve come up with to achieve a truer goal: to live with myself and love myself.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Trying&lt;/em&gt; never purports to be a destination, but it leads me… &lt;em&gt;somewhere&lt;/em&gt;. It brings into focus my edges, the uncomfortable demarcations of territory where beyond is what I can be, but am not yet.&lt;/p&gt;
&lt;p&gt;As my edges become clear, I can begin to truly know them: winding coastlines formed of insecurity, doubt. I can poke and prod them and find their soft spots. I can accept them and love them as parts of me, as integral as my skills or confidence or &lt;em&gt;doings&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And I can keep pushing.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Only a Sith deals in absolutes. &lt;a href=&quot;https://markhudnall.com/2015/05/19/try/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Friends</title>
      <link>https://markhudnall.com/2014/04/10/friends/</link>
      <guid isPermaLink="true">https://markhudnall.com/2014/04/10/friends/</guid>
      <pubDate>Thu, 10 Apr 2014 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;The other night, I met an Israeli couple who recently moved from Tel Aviv to the Bay Area. They&#39;d lived in Israel their whole lives and it was their first time living abroad in the US. She is getting her master&#39;s degree, and he is working in Silicon Valley.&lt;/p&gt;
&lt;p&gt;During our conversation, I asked what the hardest part of moving to California was.&lt;/p&gt;
&lt;p&gt;&quot;It took some time to adapt to American culture. Being in the Bay Area surrounded by tech helps, because we can still speak that common language.&quot;&lt;/p&gt;
&lt;p&gt;&quot;What &lt;em&gt;is&lt;/em&gt; the difference between US and Israeli culture?&quot; I asked.&lt;/p&gt;
&lt;p&gt;&quot;How often do you see your parents?&quot;&lt;/p&gt;
&lt;p&gt;I told them my parents live in the South Bay, not too far away, but I still see them less than I should, once or twice a month.&lt;/p&gt;
&lt;p&gt;&quot;Back in Israel, I saw my parents almost every day. The friends I grew up with, I saw almost every day. In the US, you make friends, great friends, in college. You become close, but then your friends might move across the country for work or you might move away for work, and you make new friends depending on where you are and who you&#39;re with.&lt;/p&gt;
&lt;p&gt;&quot;Part of that is because Israel is so much smaller. It&#39;s &lt;em&gt;easier&lt;/em&gt; to go out for a drink with your friends every day. And that made it a lot harder to learn how to do &lt;em&gt;this&lt;/em&gt;.&quot;&lt;/p&gt;
&lt;p&gt;He gestured out to the crowd of 15 or so people talking and having dinner in the small house. I was invited over by a friend I met the week before and I knew only 3 of the 15 people there.&lt;/p&gt;
&lt;p&gt;&quot;But walking into this house by myself to talk to people I&#39;ve never met is terrifying for me too,&quot; I said.&lt;/p&gt;
&lt;p&gt;&quot;You have no idea. For you, this is normal. It might feel uncomfortable, but it happens all the time, and you adapt.&quot;&lt;/p&gt;
&lt;p&gt;&quot;Back home, relationships are ...&quot; he paused, searching for the right word, &quot;&lt;em&gt;deeper&lt;/em&gt;. There, you learn how to connect deeply with people, the same people. Here, you learn how to interact with new people.&quot;&lt;/p&gt;
&lt;p&gt;I thought about my roommates from college, who are now spread all over the world. We half-joke over Skype about how we&#39;re going to move into a 5-story house someday with our families and live together again. The reality is that they&#39;re far away, and who knows what forces will bring us back together as our lives take shape.&lt;/p&gt;
&lt;p&gt;&quot;Which kind do you think is better?&quot; I asked, &quot;The depth or the breadth?&quot;&lt;/p&gt;
&lt;p&gt;They both shook their heads without hesitation.&lt;/p&gt;
&lt;p&gt;&quot;The question isn&#39;t &#39;Which is better?&#39; as much as &#39;How do I survive?&#39;&quot;&lt;/p&gt;
</description>
    </item>
<item>
      <title>Kindness is a Skill</title>
      <link>https://markhudnall.com/2013/12/27/kindness-is-a-skill/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/12/27/kindness-is-a-skill/</guid>
      <pubDate>Fri, 27 Dec 2013 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;Imagine that I call you up one day (we&#39;re good friends, on first name basis for years and everything), and I ask you to build me a house. Of course, you&#39;re very willing to build me a house (because we&#39;re such good friends), but you don&#39;t know the first thing about it. But I implore you.&lt;/p&gt;
&lt;p&gt;You are distraught because you don&#39;t know where to begin. It&#39;s not like you were born knowing how to build a house — that would be very improbable. It would be like if you were born knowing how to read or write a novel or public-speak or play the cello professionally. At any rate, you&#39;d really, really like to build this house.&lt;/p&gt;
&lt;p&gt;So you start from the beginning. Maybe you nail together two pieces of wood. But you&#39;ve never wielded a hammer, so it&#39;s hard the first time. You practice nailing two pieces of wood together until it becomes easy, and then you build larger structures using more than two pieces of wood. After you&#39;ve mastered that, maybe you try building a whole wall.&lt;/p&gt;
&lt;p&gt;You keep building and practicing and eventually you build a house. And after you&#39;ve built a couple houses, you become really good at it, and you build me a beautiful house and we become even better friends because you often come over and we drink tea and talk about books or just sit around.&lt;/p&gt;
&lt;p&gt;But learning how to build houses was &lt;em&gt;never&lt;/em&gt; easy. Remember when you thought you couldn&#39;t do it because you had no idea how to begin? When I asked you to build me a house, you could have told me, &quot;I can&#39;t build you a house because I don&#39;t have the mind for it. I&#39;m just not able to. It&#39;s not in my nature.&quot; A lot of people seem to take this attitude toward math and programming, that they&#39;ll never be able to figure it out, even though, like building a house, these things are iterative processes of learning that take time and effort. It&#39;s probably true that it&#39;s naturally easier for some people to build houses. But nobody was born knowing how to build a house. They had to figure it out, and figuring things out is &lt;em&gt;hard&lt;/em&gt;. You can seek more skilled carpenters&#39; and architects&#39; advice, but at the end of the day, you still need to expend effort to develop your own house-building skill.&lt;/p&gt;
&lt;p&gt;Now imagine that you and I are not friends and that we don&#39;t even know each other. Imagine instead that you believe you are selfish and kind of an asshole, but you wish to be kind. Sometimes you say terrible things and your behavior often benefits yourself at the expense of others.&lt;/p&gt;
&lt;p&gt;But you feel guilty about the way you are.&lt;/p&gt;
&lt;p&gt;You want to be kind.&lt;/p&gt;
&lt;p&gt;But you aren&#39;t.&lt;/p&gt;
&lt;p&gt;You look at your friends who are generous and who consistently stick their necks out for you in situations you&#39;d probably just avoid, and you think to yourself, &quot;Dammit, they are so kind.&quot;&lt;/p&gt;
&lt;p&gt;&quot;And I am not.&quot;&lt;/p&gt;
&lt;p&gt;You feel even more guilty because you see other people being kind, and you&#39;re unsure why you aren&#39;t able to act that way. Is there something wrong with you deep down? Maybe you start to believe that it&#39;s not in your nature. And you feel even worse.&lt;/p&gt;
&lt;p&gt;But maybe it occurs to you that feeling guilty about not being kind is like feeling guilty for not knowing how to build a house when your carpenter and architecture friends do it with such ease.&lt;/p&gt;
&lt;p&gt;Maybe it occurs to you that nobody was born knowing how to be kind. (I mean, do you remember being a kid? They aren&#39;t always the &lt;a href=&quot;https://www.youtube.com/watch?v=5HbYScltf1c&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;nicest&lt;/a&gt; &lt;a href=&quot;https://www.youtube.com/watch?v=yWmeB7mY_E0&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;bunch&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Maybe it occurs to you that learning anything when you know nothing is &lt;em&gt;hard&lt;/em&gt;, but the more you build and practice, the better you become. The more you learn about what it means to be kind. The more you learn about what it means to empathize. The more you see the house instead of the two pieces of wood nailed together.&lt;/p&gt;
&lt;p&gt;Maybe you begin to understand how being kind of an asshole is also a skill, and that being kind of an asshole more frequently makes it easier to be kind of an asshole.&lt;/p&gt;
&lt;p&gt;Maybe you realize that kindness must be practiced, that kindness becomes easier, but greater kindness is always challenging.&lt;/p&gt;
&lt;p&gt;Not because you&#39;re fighting against your nature, but because you&#39;re learning.&lt;/p&gt;
</description>
    </item>
<item>
      <title>Handling GCM messages in the foreground and background</title>
      <link>https://markhudnall.com/2013/11/13/gcm-foreground-and-background/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/11/13/gcm-foreground-and-background/</guid>
      <pubDate>Wed, 13 Nov 2013 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;When the &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt; Android app gets notifications, we want it to behave differently when it&#39;s open and in the foreground than when it&#39;s in the background.&lt;/p&gt;
&lt;p&gt;This is a surprisingly difficult thing to do, since it&#39;s actually quite difficult to determine whether an app is in the background. You can use a static boolean or keep track of the shown activity (maybe in a custom &lt;code&gt;Application&lt;/code&gt; class or another singleton), but that isn&#39;t always reliable. You might also have other requirements—what if you want to differentiate behavior based on which activity is in the foreground?&lt;/p&gt;
&lt;p&gt;One &lt;a href=&quot;http://stackoverflow.com/a/15949723/826650&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;solution&lt;/a&gt; is &lt;code&gt;sendOrderedBroadcast&lt;/code&gt;, but I prefer something with a little less boilerplate.&lt;/p&gt;
&lt;h2 id=&quot;all-aboard-the-event-bus&quot;&gt;All aboard the event bus&lt;/h2&gt;
&lt;p&gt;Instead of using &lt;code&gt;BroadcastReceivers&lt;/code&gt;, we can use an event bus like &lt;a href=&quot;http://square.github.io/otto/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Otto&lt;/a&gt; or &lt;a href=&quot;https://github.com/greenrobot/EventBus&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;EventBus&lt;/a&gt; to notify activities about an incoming GCM message. If no activities are registered on the bus, we can correctly place a notification in the notification drawer.&lt;/p&gt;
&lt;p&gt;If you aren&#39;t familiar with event buses, the idea is simple. Any object (usually an &lt;code&gt;Activity&lt;/code&gt;) can register with (or as I like to say, ride) the event bus:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;bus.register(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The object then provides subscriber methods to run when the bus emits an event:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;public &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onEvent&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;NotificationEvent event&lt;/span&gt;) {
    &lt;span class=&quot;hljs-title class_&quot;&gt;Toast&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;makeText&lt;/span&gt;(&lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;, 
        event.&lt;span class=&quot;hljs-property&quot;&gt;payload&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;getString&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;message&quot;&lt;/span&gt;), 
        &lt;span class=&quot;hljs-variable constant_&quot;&gt;LENGTH_SHORT&lt;/span&gt;)
    .&lt;span class=&quot;hljs-title function_&quot;&gt;show&lt;/span&gt;();
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;When an object should stop riding the event bus, you can unregister it:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;bus.unregister(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To actually emit events:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;NotificationEvent&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;event&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;NotificationEvent&lt;/span&gt;(bundle);
bus.post(event);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This triggers a call to the &lt;code&gt;onEvent(NotificationEvent event)&lt;/code&gt; method in every object registered on the bus. You can add different subscriber methods (such as &lt;code&gt;onEvent(LocationEvent event)&lt;/code&gt;) and these method will only be run when an event of the same type is emitted (&lt;code&gt;bus.post(new LocationEvent())&lt;/code&gt;) There is no limit to how many objects can register with the bus.&lt;/p&gt;
&lt;p&gt;If there are no objects registered, a second event is automatically posted. With Otto, this event is called &lt;code&gt;DeadEvent&lt;/code&gt;. With EventBus, it&#39;s called &lt;code&gt;NoSubscriberEvent&lt;/code&gt;. This lets you separately handle cases where there are no subscribers (you might see where this is going).&lt;/p&gt;
&lt;p&gt;You can also post any object. I like to create classes that logically separate events and suffix them with &lt;code&gt;Event&lt;/code&gt;. For instance, the &lt;code&gt;NotificationEvent&lt;/code&gt; class looks like this:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;NotificationEvent&lt;/span&gt; {
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; Bundle payload;

    &lt;span class=&quot;hljs-type&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;NotificationEvent&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Bundle extras)&lt;/span&gt; {
        &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.payload = extras;
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3 id=&quot;otto-and-eventbus&quot;&gt;Otto and EventBus&lt;/h3&gt;
&lt;p&gt;These libraries are very similar. &lt;a href=&quot;http://square.github.io/otto/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Otto&lt;/a&gt; uses annotations (&lt;code&gt;@Subscribe&lt;/code&gt;) to determine which subscriber methods to run, while &lt;a href=&quot;https://github.com/greenrobot/EventBus&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;EventBus&lt;/a&gt; uses a consistent method name (&lt;code&gt;onEvent&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Unfortunately, both libraries have drawbacks. Otto &lt;a href=&quot;https://github.com/square/otto/issues/83&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;doesn&#39;t let you&lt;/a&gt; register with the bus in a superclass and receive events in a subclass. This is annoying if you&#39;d like to register in a &lt;code&gt;BaseActivity&lt;/code&gt; that all of your activities extend from.&lt;/p&gt;
&lt;p&gt;EventBus has a bug when trying to subscribe to the &lt;code&gt;NoSubscriberEvent&lt;/code&gt;. If you register and unregister an object, the event never fires. Bummer. There&#39;s a &lt;a href=&quot;https://github.com/greenrobot/EventBus/pull/32&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;pull request&lt;/a&gt; that adds a &lt;em&gt;one line fix&lt;/em&gt;, but the maintainers haven&#39;t touched it yet.&lt;/p&gt;
&lt;p&gt;I ended up choosing EventBus because I really wanted to be able to register with the bus in a base class&lt;sup&gt;&lt;a id=&quot;footnote-ref-ft1&quot; href=&quot;https://markhudnall.com/2013/11/13/gcm-foreground-and-background/#footnote-ft1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but again, they are basically the same for most purposes. I did have to add the patch in &lt;a href=&quot;https://github.com/greenrobot/EventBus/pull/32&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;this pull request&lt;/a&gt; to make it work.&lt;/p&gt;
&lt;h2 id=&quot;changing-app-behavior-for-gcm-messages&quot;&gt;Changing app behavior for GCM messages&lt;/h2&gt;
&lt;p&gt;Here&#39;s how we use an event bus to do different things when the app is in the foreground or background.&lt;/p&gt;
&lt;p&gt;We use a singleton event bus. The EventBus library provides one for you with &lt;code&gt;EventBus.getInstance()&lt;/code&gt;. It&#39;s also easy to make a singleton bus with Otto—you can even use &lt;a href=&quot;https://github.com/square/dagger/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Dagger&lt;/a&gt; to inject the global bus where needed (to truly Square-ify your code).&lt;/p&gt;
&lt;p&gt;Every activity registers with the bus when it is shown and unregisters when it is hidden. This ensures that only one activity receives events from the bus at a time. We also add a subscriber method to handle the notification. We accomplish this with a base class:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;BaseActivity&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Activity&lt;/span&gt; {
    &lt;span class=&quot;hljs-meta&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onResume&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt; {
        &lt;span class=&quot;hljs-built_in&quot;&gt;super&lt;/span&gt;.onResume();
        EventBus.getInstance().register(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);
    }
    
    &lt;span class=&quot;hljs-meta&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onPause&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt; {
        &lt;span class=&quot;hljs-built_in&quot;&gt;super&lt;/span&gt;.onPause();
        EventBus.getInstance().unregister(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onEvent&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(NotificationEvent notification)&lt;/span&gt; {
        startActivity(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Intent&lt;/span&gt;(BaseActivity.&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;, NotificationActivity.class));
        overridePendingTransition(R.anim.fade_in_animation, R.anim.fade_out_animation);
    }

}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then, in our GCM Receiver, we post to the event bus:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;GcmReceiver&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;BroadcastReceiver&lt;/span&gt; {
    &lt;span class=&quot;hljs-meta&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onReceive&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Context context, Intent intent)&lt;/span&gt; {
        &lt;span class=&quot;hljs-type&quot;&gt;GoogleCloudMessaging&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;gcm&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; GoogleCloudMessaging.getInstance(context);
        &lt;span class=&quot;hljs-type&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;gcmMessageType&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; gcm.getMessageType(intent);
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(gcmMessageType)) {
            &lt;span class=&quot;hljs-type&quot;&gt;Bundle&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;payload&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; intent.getExtras();
            &lt;span class=&quot;hljs-type&quot;&gt;NotificationEvent&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;NotificationEvent&lt;/span&gt;(payload);

            &lt;span class=&quot;hljs-comment&quot;&gt;// Register ourselves so we can receive the NoSubscriberEvent notification if no&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// activities are registered&lt;/span&gt;
            EventBus.getDefault().register(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;, NoSubscriberEvent.class);

            &lt;span class=&quot;hljs-comment&quot;&gt;// Post the notification for an activity&lt;/span&gt;
            EventBus.getDefault().post(notification);
        }
        setResultCode(Activity.RESULT_OK);
    }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The real magic happens in this line:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;    &lt;span class=&quot;hljs-comment&quot;&gt;// Register ourselves so we can receive the NoSubscriberEvent notification if no&lt;/span&gt;
    &lt;span class=&quot;hljs-comment&quot;&gt;// activities are registered&lt;/span&gt;
    EventBus.getDefault().register(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;, NoSubscriberEvent.class);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We register the &lt;code&gt;GcmReceiver&lt;/code&gt; with the event bus to receive only the &lt;code&gt;NoSubscriberEvent&lt;/code&gt;. If no activity is being displayed, then nothing is registered for &lt;code&gt;NotificationEvent&lt;/code&gt;. EventBus will then post the &lt;code&gt;NoSubscriberEvent&lt;/code&gt;, which we can handle in &lt;code&gt;GcmReceiver&lt;/code&gt; like so:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-comment&quot;&gt;/*
 * Called when there are no subscribers to the NotificationEvent.
 *
 * This occurs when the app is in the background and no activities are riding the bus.
 */&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onEvent&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(NoSubscriberEvent deadEvent)&lt;/span&gt; {
    &lt;span class=&quot;hljs-comment&quot;&gt;// We&#39;re all done here&lt;/span&gt;
    EventBus.getDefault().unregister(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);

    &lt;span class=&quot;hljs-type&quot;&gt;NotificationEvent&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; (NotificationEvent) deadEvent.originalEvent;

    &lt;span class=&quot;hljs-comment&quot;&gt;// Save the wee little notification for later consumption by an activity&lt;/span&gt;
    Mailbox.getInstance().put(notification);

    &lt;span class=&quot;hljs-comment&quot;&gt;// Display the notification in the notification drawer&lt;/span&gt;
    buildNotification(notification);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;buildNotification&lt;/code&gt; puts the notification in the notification drawer with a &lt;a href=&quot;https://developer.android.com/reference/android/app/PendingIntent.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;PendingIntent&lt;/a&gt;. We also save the notification in a global queue, so that when application is opened back up &lt;em&gt;not&lt;/em&gt; from the notification drawer, we can process it as normal. There&#39;s probably a better solution than this, since it&#39;s possible for the &lt;code&gt;Mailbox&lt;/code&gt; singleton to be deallocated when the app is in the background.&lt;/p&gt;
&lt;p&gt;Both EventBus and Otto also support &quot;sticky&quot; events. Once you post an event with &lt;code&gt;postSticky&lt;/code&gt;, EventBus caches it, so you can get the last event back with &lt;code&gt;bus.getStickyEvent(TheEventClass.class)&lt;/code&gt;. Otto allows you to use a default value by defining a method with a &lt;code&gt;@Provides&lt;/code&gt; annotation. In this example, sticky events could replace storing the notification away in &lt;code&gt;Mailbox&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;differentiating-notifications&quot;&gt;Differentiating notifications&lt;/h3&gt;
&lt;p&gt;Your app probably sends more than one kind of GCM message. If that&#39;s the case, you can subclass &lt;code&gt;NotificationEvent&lt;/code&gt; to handle different message types. When you register an activity with the event bus, you can write your subscriber functions so they only respond to certain kinds of notifications. This is a nice way to use the built-in type system, instead of doing something like &lt;code&gt;payload.getString(&quot;type&quot;).equals(aMessageType)&lt;/code&gt; everywhere.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Event buses are awesome. There are many more use cases, such as communicating between fragments in a sane way (without using interfaces) or making callbacks from &lt;code&gt;AsyncTasks&lt;/code&gt; (although, I prefer replacing &lt;code&gt;AsyncTask&lt;/code&gt; &lt;a href=&quot;http://markhudnall.com/2013/10/15/rxjava-and-android/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;with RxJava&lt;/a&gt; altogether&lt;sup&gt;&lt;a id=&quot;footnote-ref-ft2&quot; href=&quot;https://markhudnall.com/2013/11/13/gcm-foreground-and-background/#footnote-ft2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-ft1&quot;&gt;
&lt;p&gt;Though there is a &lt;a href=&quot;http://howrobotswork.wordpress.com/2013/07/20/subclassing-with-otto/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;workaround&lt;/a&gt; for Otto. &lt;a href=&quot;https://markhudnall.com/2013/11/13/gcm-foreground-and-background/#footnote-ref-ft1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference ft1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-ft2&quot;&gt;
&lt;p&gt;Event buses could actually replace RxJava for a lot of simpler use cases. Otto and EventBus both provide some control over threading so you can do things in the background, and it might make for less complex looking code. &lt;a href=&quot;https://markhudnall.com/2013/11/13/gcm-foreground-and-background/#footnote-ref-ft2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference ft2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Happiness and Fulfillment</title>
      <link>https://markhudnall.com/2013/11/05/happiness-and-fulfillment/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/11/05/happiness-and-fulfillment/</guid>
      <pubDate>Tue, 05 Nov 2013 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;Two patients lie in their death beds in a hospital. Maybe they&#39;re both surrounded by their family and friends. Maybe they&#39;re in the Northwest as the weather gets colder and they both realize independently, at slightly different times, that they&#39;ll never see the sun again. Maybe they each have regrets.&lt;/p&gt;
&lt;p&gt;One thing I can say for certain, as the author of this little story, is that one patient&#39;s life was spent in the pursuit of happiness, while the other&#39;s was spent in the pursuit of fulfillment. Whether they each found it is unbeknownst to me. I also can only speculate what they thought as they quietly slipped away, as the steady beep of the EKG disappeared into a deafening solid note, which the nurses quickly cut out partly because we got the message and partly because the sound itself is so obnoxious.&lt;/p&gt;
&lt;p&gt;In that instant, I suppose it doesn&#39;t matter whether the one found happiness or the other found fulfillment, or even which they chose to pursue—whether they spent their lives frolicking in the grass or anti-frolicking in a metaphorical windowless office. I suppose it doesn&#39;t matter to the two patients, at that very moment and every one thereafter, whether or not they figured it all out.&lt;/p&gt;
&lt;p&gt;But for the rest of us, it does.&lt;/p&gt;
&lt;p&gt;Every day, I wake up and struggle with whether I want to be myself and happy or whether I want to be better and fulfilled.&lt;/p&gt;
&lt;h2 id=&quot;happiness&quot;&gt;Happiness&lt;/h2&gt;
&lt;p&gt;I&#39;ve spent most of my life in the pursuit of happiness&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, but the times I&#39;ve actually felt happy are when I stopped trying. Happiness is an ephemeral moment when I&#39;m not self-conscious, when I&#39;m not worried about what I&#39;m doing with my body or what I look like, when I&#39;m completely present. It&#39;s taken me a long time to understand what that feels like and I think it&#39;s why people like dancing, and why I like music festivals&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;When I&#39;m present, I forget the past, I forget the future, I forget my aspirations. I forget who I &lt;em&gt;am&lt;/em&gt; in my mind&#39;s eye, because I&#39;m focused on &lt;em&gt;being&lt;/em&gt; who I am&lt;sup&gt;&lt;a id=&quot;footnote-ref-3&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I get lost in the present. In a kind of twisted way, that presence also implies an absence. I&#39;m &lt;em&gt;here&lt;/em&gt; but I&#39;m also not really &lt;em&gt;there&lt;/em&gt;, because there&#39;s no context for me being there.&lt;/p&gt;
&lt;p&gt;In that blissful moment&lt;sup&gt;&lt;a id=&quot;footnote-ref-4&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;, however, I don&#39;t notice an absence. I simply accept the beauty that everything is the way it is.&lt;/p&gt;
&lt;h3 id=&quot;it-isnt-enough&quot;&gt;It isn&#39;t enough&lt;/h3&gt;
&lt;p&gt;And then, something creeps into my psyche. I start to feel complacent. Stagnated.&lt;/p&gt;
&lt;p&gt;How do I reconcile this complacency with my desire to be better, to do better, to not simply accept my experience and my fate as it is?&lt;/p&gt;
&lt;h2 id=&quot;fulfillment&quot;&gt;Fulfillment&lt;/h2&gt;
&lt;p&gt;Some would say you can&#39;t. When you feel that complacency, it&#39;s because life is about fulfillment, not happiness, and you aren&#39;t fulfilling yourself. And that&#39;s true—I feel guilty&lt;sup&gt;&lt;a id=&quot;footnote-ref-5&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-5&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; for not trying to better myself, because I know I can be more. I know I can be part of something greater. When I strive to better myself, when I build something, when I&#39;m uncomfortable, I&#39;m &lt;em&gt;satisfied&lt;/em&gt;. I&#39;m &lt;em&gt;contributing&lt;/em&gt;. I&#39;ve felt this way a lot while working on &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;it-isnt-enough-either&quot;&gt;It isn&#39;t enough either&lt;/h3&gt;
&lt;p&gt;You can say that you&#39;re happy when you&#39;re bettering yourself, but that&#39;s an issue of semantics, and I&#39;d call that fulfillment.&lt;/p&gt;
&lt;p&gt;When I strive to better myself, it&#39;s out of discontent with who I am &lt;em&gt;now&lt;/em&gt;. That&#39;s pretty opposed to my happiness. Sometimes that discontent is totally concrete and I know exactly what I need to do, but most of the time, it&#39;s an itch more than anything else. A reminder in the back of my head that what I&#39;m doing isn&#39;t enough.&lt;/p&gt;
&lt;h2 id=&quot;i-dont-have-a-satisfying-answer&quot;&gt;I don&#39;t have a satisfying answer&lt;/h2&gt;
&lt;p&gt;If you&#39;re looking for someone to tell you that happiness isn&#39;t important, it isn&#39;t me. But I cannot tell you that fulfillment is the answer either, sorry.&lt;/p&gt;
&lt;p&gt;The pursuit of fulfillment is torture. Perhaps if I were driven by some purely-good, higher purpose, it wouldn&#39;t be. But I&#39;m driven by a feeling of incompleteness and my insecurities.&lt;/p&gt;
&lt;h2 id=&quot;trust-yourself&quot;&gt;Trust yourself&lt;/h2&gt;
&lt;p&gt;You can&#39;t have only happiness or only fulfillment. You need to have a balance.&lt;/p&gt;
&lt;p&gt;&quot;Balance&quot; is a pretty wholly disappointing answer, but I&#39;m not surprised it isn&#39;t some grand revelation (seeing how people have been thinking about this sort of thing for a very long time).&lt;/p&gt;
&lt;p&gt;It raises the next question: How do I know what the right balance is?&lt;/p&gt;
&lt;p&gt;That isn&#39;t something I can answer for you. Hell, it&#39;s something I can barely answer for myself. But I try to approach it from a place of self-love, where I can begin to accept who I am, even when I&#39;m not happy with who I am.&lt;/p&gt;
&lt;p&gt;When I feel right with the world, that is how I am. When I feel discontent and I strive to better myself, that is also how I am. I can&#39;t fight against the way I feel in a given moment, because in that moment, I&#39;m being &lt;em&gt;me&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So when I want to float in the waves, I revel in that fact. And when I&#39;m no longer content just floating, when I want to swim my ass off and ride one into the shore, I revel in that fact too.&lt;/p&gt;
&lt;p&gt;Trusting myself to do both is hard. I worry that when I&#39;m floating or when I&#39;m swimming, I&#39;ll never have the desire for the other again. It isn&#39;t that I want to be one way or another—it&#39;s the self-doubt in either state that drives me crazy. So I have to trust that what I&#39;m doing at any given moment is right for me, and listen to myself when it isn&#39;t.&lt;/p&gt;
&lt;p&gt;When I am one of those patients, wondering whether I&#39;ll ever see the sun again, I probably still won&#39;t know whether I&#39;ve &quot;achieved&quot; fulfillment or happiness. And it probably won&#39;t matter&lt;sup&gt;&lt;a id=&quot;footnote-ref-6&quot; href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-6&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;What will be true is when I wanted to be better, I tried, and when I wanted to be me, I was.&lt;/p&gt;
&lt;p&gt;And hopefully, the nurse will turn that obnoxious sound off sooner rather than later.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Well, I spent high school in the wholehearted pursuit of &lt;em&gt;being cool&lt;/em&gt;, which I guess I did because I thought it would make me happy. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;The first music festival I ever attended was Sasquatch 2013. I&#39;ll have to write another post about how to approach festivals, but in a nutshell: festivals can be an incredibly safe space for exploring presence and being yourself. Some call it &lt;em&gt;letting loose&lt;/em&gt;. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-3&quot;&gt;
&lt;p&gt;Maybe a better way to say this is that there is no &lt;em&gt;I&lt;/em&gt; to be focused on &lt;em&gt;anything&lt;/em&gt;. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-4&quot;&gt;
&lt;p&gt;That &quot;moment&quot; could last weeks or months. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-5&quot;&gt;
&lt;p&gt;I wonder how much of that is a result of my own drive and how much is a result of constantly comparing myself to others. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-6&quot;&gt;
&lt;p&gt;Or maybe it will! The living will never know. &lt;a href=&quot;https://markhudnall.com/2013/11/05/happiness-and-fulfillment/#footnote-ref-6&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>RxJava and Android: Just What the Doctor Ordered</title>
      <link>https://markhudnall.com/2013/10/15/rxjava-and-android/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/10/15/rxjava-and-android/</guid>
      <pubDate>Tue, 15 Oct 2013 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;I work on Android at &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt;. Most of the time, though, it feels like I&#39;m working against Android. The Activity and Service model is elegantly designed for interoperability, but when it comes down to implementation, it&#39;s often painful to work with.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/10/15/rxjava-and-android/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But a bigger problem is handling user interaction. There are two very common patterns that I have trouble with:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Doing something in the background and updating the UI. &lt;/li&gt;
&lt;li&gt;Responding to user interaction and updating a different part of the UI.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;rxjava&quot;&gt;RxJava&lt;/h2&gt;
&lt;p&gt;In both cases, &lt;a href=&quot;https://github.com/Netflix/RxJava&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;RxJava&lt;/a&gt; helps me keep my sanity. RxJava is a Java implementation of &lt;a href=&quot;https://en.wikipedia.org/wiki/Functional_reactive_programming&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Functional Reactive Programming&lt;/a&gt;. If you aren&#39;t familiar with FRP, it&#39;s not as complicated as its name makes it sound. FRP is all about streams of data. A &lt;em&gt;stream&lt;/em&gt; produces data at different points in time. An &lt;em&gt;observer&lt;/em&gt; is notified whenever data in that stream and does something with it. It&#39;s a little different than the &lt;a href=&quot;https://en.wikipedia.org/wiki/Observer_pattern&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Observer pattern&lt;/a&gt;, since there&#39;s a focus on composition. You can map, filter, and reduce streams—that&#39;s where the &quot;functional&quot; part comes in.&lt;/p&gt;
&lt;h2 id=&quot;doing-things-in-the-background&quot;&gt;Doing things in the background&lt;/h2&gt;
&lt;h3 id=&quot;the-old-way-asynctask&quot;&gt;The old way: AsyncTask&lt;/h3&gt;
&lt;p&gt;It&#39;s an inconvenient necessity that we have to worry about blocking the UI thread. Android provides &lt;a href=&quot;https://developer.android.com/reference/android/os/AsyncTask.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;AsyncTask&lt;/a&gt; to help ease this pain, letting you do work on the background with &lt;code&gt;doInBackground&lt;/code&gt; and then delivering on the UI thread with &lt;code&gt;onPostExecute&lt;/code&gt;. For the most part, the AsyncTasks I create are requests to Clef&#39;s API server that return JSON. But there are a few pitfalls with AsyncTask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Error handling: as &lt;a href=&quot;https://mttkay.github.io/blog/2013/08/25/functional-reactive-programming-on-android-with-rxjava/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mttkay points out&lt;/a&gt;, there&#39;s no easy way to handle error messages in the UI for errors that happen in the background. I usually return null instead of the actual JSONObject and then later handle the case where the JSONObject is null. Ugh.&lt;/li&gt;
&lt;li&gt;Code reuse/readability: Since API requests are all similar, I use a general APIRequestTask that I give a path and some parameters. I also pass in the caller which implements a &lt;code&gt;APIRequestHandler&lt;/code&gt; interface, so the caller can handle the results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the activity/fragment:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-type&quot;&gt;APIRequestTask&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;APIRequestTask&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;/user/register&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;);
request.putParameter(&lt;span class=&quot;hljs-string&quot;&gt;&quot;email&quot;&lt;/span&gt;, &lt;span class=&quot;hljs-string&quot;&gt;&quot;test@example.com&quot;&lt;/span&gt;);
request.execute();
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In &lt;code&gt;APIRequestTask.java&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;public interface &lt;span class=&quot;hljs-title class_&quot;&gt;NetworkRequestHandler&lt;/span&gt; {
    public &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onRequestCompleted&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt; response, &lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt; url);
}

public &lt;span class=&quot;hljs-title class_&quot;&gt;NetworkRequestTask&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt; url, &lt;span class=&quot;hljs-title class_&quot;&gt;NetworkRequestHandler&lt;/span&gt; handler) {
    &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;url&lt;/span&gt; = url;
    &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;mHandler&lt;/span&gt; = handler;
    &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;requestParameters&lt;/span&gt; = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;ArrayList&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;NameValuePair&lt;/span&gt;&amp;gt;();
}

public &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;putParameter&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;String&lt;/span&gt; key, &lt;span class=&quot;hljs-built_in&quot;&gt;String&lt;/span&gt; value&lt;/span&gt;) {
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (key != &lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt; &amp;amp;&amp;amp; value != &lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt;) {
        requestParameters.&lt;span class=&quot;hljs-title function_&quot;&gt;add&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;BasicNameValuePair&lt;/span&gt;(key, value));
    }
}

@&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
protected &lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;doInBackground&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;Void... voids&lt;/span&gt;) {
    &lt;span class=&quot;hljs-comment&quot;&gt;// Do the request&lt;/span&gt;
}

@&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
protected &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onPostExecute&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;JSONObject response&lt;/span&gt;) {
    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (mHandler != &lt;span class=&quot;hljs-literal&quot;&gt;null&lt;/span&gt;) {
        mHandler.&lt;span class=&quot;hljs-title function_&quot;&gt;onRequestCompleted&lt;/span&gt;(response, &lt;span class=&quot;hljs-variable language_&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;url&lt;/span&gt;);
    }
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Though the generality is nice, with this pattern, if a calling Activity/Fragment makes multiple requests, it needs to handle responses in a single function by switching on the URL. This makes for code that isn&#39;t very readable, especially when you have to jump from where the request is executed to where the response is handled.&lt;/p&gt;
&lt;h3 id=&quot;once-more-with-feeling&quot;&gt;Once more, with feeling&lt;/h3&gt;
&lt;p&gt;Let&#39;s try that again with RxJava:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;create&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;OnSubscribeFunc&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-title class_&quot;&gt;Subscription&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onSubscribe&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;Observer&amp;lt;? &lt;span class=&quot;hljs-variable language_&quot;&gt;super&lt;/span&gt; JSONObject&amp;gt; observer&lt;/span&gt;) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt; {
            &lt;span class=&quot;hljs-comment&quot;&gt;// Do the request&lt;/span&gt;
            &lt;span class=&quot;hljs-comment&quot;&gt;// ...&lt;/span&gt;
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onNext&lt;/span&gt;(jsonResponse);
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onCompleted&lt;/span&gt;();
        }
        &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;hljs-title class_&quot;&gt;Exception&lt;/span&gt; e) {
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onError&lt;/span&gt;(e);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Subscriptions&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;empty&lt;/span&gt;()
    }
}).&lt;span class=&quot;hljs-title function_&quot;&gt;subscribeOn&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;Schedulers&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;newThread&lt;/span&gt;()).&lt;span class=&quot;hljs-title function_&quot;&gt;observeOn&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;AndroidSchedulers&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;mainThread&lt;/span&gt;())
  .&lt;span class=&quot;hljs-title function_&quot;&gt;subscribe&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Action1&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;JSONObject response&lt;/span&gt;) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; (response.&lt;span class=&quot;hljs-title function_&quot;&gt;getBoolean&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;success&quot;&lt;/span&gt;)) {
            &lt;span class=&quot;hljs-title class_&quot;&gt;RegistrationActivity&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;this&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;messageView&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;setText&lt;/span&gt;(&lt;span class=&quot;hljs-string&quot;&gt;&quot;You&#39;re all set.&quot;&lt;/span&gt;);
        }
    }
}, &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Action1&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;Throwable&lt;/span&gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;Throwable error&lt;/span&gt;) {
        &lt;span class=&quot;hljs-title class_&quot;&gt;Toast&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;makeText&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;RegistrationActivity&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;this&lt;/span&gt;,
            error.&lt;span class=&quot;hljs-title function_&quot;&gt;getMessage&lt;/span&gt;(),
            &lt;span class=&quot;hljs-title class_&quot;&gt;Toast&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;LENGTH_SHORT&lt;/span&gt;)
        .&lt;span class=&quot;hljs-title function_&quot;&gt;show&lt;/span&gt;();
    }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;Observable.create&lt;/code&gt; creates a stream that makes a network request. When the request completes, the response is delivered from the stream to an observer. In this case, the observer is the first &lt;code&gt;Action1&lt;/code&gt; in the &lt;code&gt;subscribe&lt;/code&gt; method. If there are any errors, they are delivered to the second &lt;code&gt;Action1&lt;/code&gt; passed into the &lt;code&gt;subscribe&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;subscribeOn(Schedulers.newThread())&lt;/code&gt; runs the &lt;code&gt;OnSubscribeFunc&lt;/code&gt; on a thread, while &lt;code&gt;observeOn(AndroidSchedulers.mainThread())&lt;/code&gt; delivers the results to the main thread.&lt;/p&gt;
&lt;p&gt;It&#39;s still a little verbose, but it&#39;s more similar to writing synchronous code. I use the response in code that directly follows the request. I also get error handling on the UI thread for free.&lt;/p&gt;
&lt;h3 id=&quot;composition&quot;&gt;Composition&lt;/h3&gt;
&lt;p&gt;RxJava also lets you compose logically separated tasks. &lt;a href=&quot;https://mttkay.github.io/blog/2013/08/25/functional-reactive-programming-on-android-with-rxjava/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;mttkay gives&lt;/a&gt; this example:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt;&amp;gt; filePathObservable = &lt;span class=&quot;hljs-title function_&quot;&gt;downloadFileObservable&lt;/span&gt;().&lt;span class=&quot;hljs-title function_&quot;&gt;map&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Func1&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;File&lt;/span&gt;, &lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;File file&lt;/span&gt;) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; file.&lt;span class=&quot;hljs-title function_&quot;&gt;getAbsolutePath&lt;/span&gt;();
    }
});

&lt;span class=&quot;hljs-comment&quot;&gt;// now emits file paths, not `File`s&lt;/span&gt;
subscription = filePathObservable.&lt;span class=&quot;hljs-title function_&quot;&gt;subscribe&lt;/span&gt;(&lt;span class=&quot;hljs-comment&quot;&gt;/* Observer&amp;lt;String&amp;gt; */&lt;/span&gt;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, if &lt;code&gt;return file.getAbsolutePath();&lt;/code&gt; throws an error, we run into the same problem that we had with AsyncTasks: how do we handle the error when we don&#39;t have a reference to an observer? Instead of &lt;code&gt;map&lt;/code&gt;, we must use &lt;code&gt;flatMap&lt;/code&gt;. Here&#39;s an example where we tokenize a credit card into a &lt;a href=&quot;http://balancedpayments.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Balanced&lt;/a&gt; uri and then use the uri in a request to our server:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;final &lt;span class=&quot;hljs-title class_&quot;&gt;Card&lt;/span&gt; card = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Card&lt;/span&gt;(cardNumber, expiryMonth, expiryYear, cvc);

&lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;create&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;OnSubscribeFunc&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-title class_&quot;&gt;Subscription&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onSubscribe&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;Observer&amp;lt;? &lt;span class=&quot;hljs-variable language_&quot;&gt;super&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;String&lt;/span&gt;&amp;gt; observer&lt;/span&gt;) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt; {
            &lt;span class=&quot;hljs-title class_&quot;&gt;Balanced&lt;/span&gt; balanced = &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Balanced&lt;/span&gt;(&lt;span class=&quot;hljs-title class_&quot;&gt;Constants&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;BALANCED_MARKETPLACE_URI&lt;/span&gt;, &lt;span class=&quot;hljs-title class_&quot;&gt;NewCreditCardActivity&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;this&lt;/span&gt;);
            &lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt; cardURI = balanced.&lt;span class=&quot;hljs-title function_&quot;&gt;tokenizeCard&lt;/span&gt;(card);
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onNext&lt;/span&gt;(cardURI);
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onCompleted&lt;/span&gt;();
        } &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;hljs-title class_&quot;&gt;Exception&lt;/span&gt; e) {
            observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onError&lt;/span&gt;(e);
        }

        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Subscriptions&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;empty&lt;/span&gt;();
    }
})
.&lt;span class=&quot;hljs-title function_&quot;&gt;flatMap&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Func1&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;String&lt;/span&gt;, &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt;&amp;gt;&amp;gt;() {
    @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
    public &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt;&amp;gt; &lt;span class=&quot;hljs-title function_&quot;&gt;call&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;String&lt;/span&gt; cardURI&lt;/span&gt;) {
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;create&lt;/span&gt;(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Observable&lt;/span&gt;.&lt;span class=&quot;hljs-property&quot;&gt;OnSubscribeFunc&lt;/span&gt;&amp;lt;&lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt;&amp;gt;() {
            @&lt;span class=&quot;hljs-title class_&quot;&gt;Override&lt;/span&gt;
            public &lt;span class=&quot;hljs-title class_&quot;&gt;Subscription&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;onSubscribe&lt;/span&gt;(&lt;span class=&quot;hljs-params&quot;&gt;Observer&amp;lt;? &lt;span class=&quot;hljs-variable language_&quot;&gt;super&lt;/span&gt; JSONObject&amp;gt; observer&lt;/span&gt;) {
                &lt;span class=&quot;hljs-keyword&quot;&gt;try&lt;/span&gt; {
                    &lt;span class=&quot;hljs-title class_&quot;&gt;JSON&lt;/span&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;Object&lt;/span&gt; response = &lt;span class=&quot;hljs-title function_&quot;&gt;doTheRequest&lt;/span&gt;(cardURI); 
                    observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onNext&lt;/span&gt;(response);
                    observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onCompleted&lt;/span&gt;();
                }
                &lt;span class=&quot;hljs-keyword&quot;&gt;catch&lt;/span&gt; (&lt;span class=&quot;hljs-title class_&quot;&gt;Exception&lt;/span&gt; e) {
                    observer.&lt;span class=&quot;hljs-title function_&quot;&gt;onError&lt;/span&gt;(e);
                }
                &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Subscriptions&lt;/span&gt;.&lt;span class=&quot;hljs-title function_&quot;&gt;empty&lt;/span&gt;();
            }
        });
    }
})
.&lt;span class=&quot;hljs-title function_&quot;&gt;subscribe&lt;/span&gt;(&lt;span class=&quot;hljs-comment&quot;&gt;/* ... */&lt;/span&gt;);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Unfortunately, maintaining the error handling abilities makes the code much more verbose. However, it also makes it &lt;em&gt;a lot&lt;/em&gt; easier to display errors to the UI and chain separate, dependent asynchronous operations in a readable way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; lungos in the comments pointed out that the using &lt;code&gt;flatMap&lt;/code&gt; may be overkill. Any exceptions in the &lt;code&gt;flatMap&lt;/code&gt;&#39;s &lt;code&gt;OnSubscribeFunc&lt;/code&gt; will be caught by the try/catch block in the original &lt;code&gt;Observable&lt;/code&gt;&#39;s &lt;code&gt;OnSubscribeFunc&lt;/code&gt;. In fact, &lt;code&gt;flatMap&lt;/code&gt; is not necessary here at all, because &lt;code&gt;map&lt;/code&gt; in RxJava will wrap return values in an &lt;code&gt;Observable&lt;/code&gt; automatically, making &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;flatMap&lt;/code&gt; essentially the same. They aren&#39;t &lt;em&gt;actually&lt;/em&gt; the same, but the nuance requires an explanation of monads and other theory that I don&#39;t want to get into.&lt;/p&gt;
&lt;h2 id=&quot;responding-to-user-interaction&quot;&gt;Responding to user interaction&lt;/h2&gt;
&lt;p&gt;RxJava isn&#39;t just for background operations. While RxJava isn&#39;t yet as fully featured as its Objective-C counterpart, &lt;a href=&quot;https://github.com/ReactiveCocoa/ReactiveCocoa&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ReactiveCocoa&lt;/a&gt;, it&#39;s still really useful for making a responsive UI. It also enforces a separation of view and model logic.&lt;/p&gt;
&lt;p&gt;For instance, say I&#39;m displaying information about a pending payment. Instead of storing payment information as member variables of an activity, I store them in a separate &lt;code&gt;Payment&lt;/code&gt; model object (which is good practice anyway). A user can select a shipping option that increases a total amount of the payment. I could create an &lt;code&gt;onItemClickListener&lt;/code&gt; which updates the amount &lt;code&gt;TextView&lt;/code&gt;, but this entangles the view logic with the underlying model logic.&lt;/p&gt;
&lt;p&gt;With RxJava, I can use a &lt;a href=&quot;http://netflix.github.io/RxJava/javadoc/rx/subjects/ReplaySubject.html&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Subject&lt;/a&gt; to automatically update the UI. Subjects are observables that you can both subscribe to and trigger updates on. With Subjects, you don&#39;t need a reference to an observer—you just emit the data on the subject itself.&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Payment&lt;/span&gt; {
    &lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; Integer totalAmount; 
    &lt;span class=&quot;hljs-keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;ReplaySubject&lt;/span&gt;&amp;lt;Integer&amp;gt; &lt;span class=&quot;hljs-variable&quot;&gt;totalAmountSubject&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;ReplaySubject&lt;/span&gt;.create();

    &lt;span class=&quot;hljs-comment&quot;&gt;/* ... */&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;setTotalAmount&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Integer newAmount)&lt;/span&gt; {
        &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.totalAmount = newAmount; 
        &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.totalAmountSubject.onNext(&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.totalAmount);
    }

    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;Observable&lt;/span&gt;&amp;lt;Integer&amp;gt; &lt;span class=&quot;hljs-title function_&quot;&gt;totalAmountObservable&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;()&lt;/span&gt; {
        &lt;span class=&quot;hljs-keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.totalAmountSubject.observeOn(AndroidSchedulers.mainThread());
    }

    &lt;span class=&quot;hljs-comment&quot;&gt;/* ... */&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, I can subscribe to &lt;code&gt;totalAmountObservable&lt;/code&gt; in the Activity like so:&lt;/p&gt;
&lt;div class=&quot;content-scroll&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Scrollable code sample&quot;&gt;&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.totalAmountSubscription = &lt;span class=&quot;hljs-built_in&quot;&gt;this&lt;/span&gt;.payment.totalAmountInCentsObservable().subscribe(&lt;span class=&quot;hljs-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hljs-title class_&quot;&gt;Action1&lt;/span&gt;&amp;lt;Integer&amp;gt;() {
    &lt;span class=&quot;hljs-meta&quot;&gt;@Override&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;hljs-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;hljs-title function_&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;hljs-params&quot;&gt;(Integer newAmount)&lt;/span&gt; {
        Log.d(TAG, &lt;span class=&quot;hljs-string&quot;&gt;&quot;Total amount in cents has been updated!&quot;&lt;/span&gt;);
        &lt;span class=&quot;hljs-type&quot;&gt;TextView&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;amountView&lt;/span&gt; &lt;span class=&quot;hljs-operator&quot;&gt;=&lt;/span&gt; (TextView) findViewById(R.id.price);
        amountView.setText(Format.formatCents(newAmount));
    }
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;code&gt;onItemClickListener&lt;/code&gt; (and anywhere else), I can now just use &lt;code&gt;setTotalAmount&lt;/code&gt;, and the view is automatically updated to reflect the new amount.&lt;/p&gt;
&lt;p&gt;If you&#39;ve used &lt;a href=&quot;http://backbonejs.org/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Backbone.js&lt;/a&gt; or another JavaScript framework, this one-way binding might not be new to you, but it&#39;s nice to have it available on Android. There&#39;s also an &lt;a href=&quot;http://github.com/Netflix/RxJava/issues/335&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;open issue on GitHub&lt;/a&gt; for adding more UI hooks and make it even more usable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Edit:&lt;/strong&gt; &lt;a href=&quot;http://twitter.com/bryanstern&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;@bryanstern&lt;/a&gt; pointed out that you can use a &lt;code&gt;BehaviorSubject&lt;/code&gt; instead of a &lt;code&gt;ReplaySubject&lt;/code&gt; to the same effect. The difference between them is that &lt;code&gt;BehaviorSubject&lt;/code&gt; emits only the last item when it is subscribed to, while &lt;code&gt;ReplaySubject&lt;/code&gt; emits all previous items. That means if you do &lt;code&gt;subject.onNext(foo1); subject.onNext(foo2);&lt;/code&gt; you&#39;ll get &lt;code&gt;foo1&lt;/code&gt; and then &lt;code&gt;foo2&lt;/code&gt; with a &lt;code&gt;ReplaySubject&lt;/code&gt;, but just &lt;code&gt;foo2&lt;/code&gt; with a &lt;code&gt;BehaviorSubject&lt;/code&gt;. Check out the different kinds of subjects in the &lt;a href=&quot;http://github.com/Netflix/RxJava/wiki/Subject&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;RxJava has been a pleasure to use so far. There are a few snags, like remembering to include &lt;code&gt;subscribeOn(Schedulers.newThread())&lt;/code&gt; and &lt;code&gt;observeOn(AndroidSchedulers.mainThread())&lt;/code&gt;, but being able to properly handle errors and write more readable code is essential.&lt;/p&gt;
&lt;p&gt;What are your experiences using RxJava with Android? Am I doing anything egregiously wrong? Let me know in the comments.&lt;/p&gt;
&lt;p&gt;You should follow me on &lt;a href=&quot;http://twitter.com/landakram&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;For instance, detecting when an app is being backgrounded is simple on iOS because a delegate function is provided for you. On Android, however, we are forced to manually keep track of whether activities are open (or other similar hackery). Again, the Activity model is beautiful, but sometimes, we need to keep global state, dammit. &lt;a href=&quot;https://markhudnall.com/2013/10/15/rxjava-and-android/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Docking at Port</title>
      <link>https://markhudnall.com/2013/09/24/docking-at-port/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/09/24/docking-at-port/</guid>
      <pubDate>Tue, 24 Sep 2013 00:00:03 GMT</pubDate>
      <description>&lt;p&gt;&lt;strong&gt;This post is part of a larger media experiment describing why, and how, &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt; moved to Oakland. If you want to read more, visit the timeline at &lt;a href=&quot;http://oakland.is/home&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;oakland.is/home&lt;/a&gt; or tell us your thoughts with &lt;a href=&quot;https://twitter.com/search?q=%23oaklandis&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#oaklandis on Twitter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;We woke up the next morning sore and tired from sleeping on the floor. We drove in to our new office and didn&#39;t know what to expect. What we found was a burgeoning community that welcomed us openly.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;http://www.portworkspaces.com/spaces/101-broadway/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Port at 101 Broadway&lt;/a&gt; sits on the edge of Jack London Square. A 30-second walk takes you to the water, where small boats wait at the docks and a hulking freighter unloads container after container with a tall crane off in the distance.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/oakland1-1024x768.jpg&quot; alt=&quot;oakland1&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;While the breeze out there is refreshing, all the good stuff is happening within the red brick walls of the Port.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; We&#39;ve been here three weeks, and it felt like home two weeks ago.&lt;/p&gt;
&lt;p&gt;The first (real) day we arrived, we were exhausted from the move. Aaron and Ryan from &lt;a href=&quot;http://www.theshoppad.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;ShopPad&lt;/a&gt; immediately invited us to play ping pong. Though we didn&#39;t actually play (we had to run into a meeting), I was caught off guard in the best way. At our old office, we were friendly with the other companies nearby, but never really interacted with them socially.&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;And Michael, Zoey and Stephen helped with everything else.&lt;/p&gt;
&lt;p&gt;Michael is one of the founders of the Port and an architect who helped design Pier 39. When we jokingly asked him for a bike room, he told us he was already making one.&lt;sup&gt;&lt;a id=&quot;footnote-ref-3&quot; href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Stephen runs IT and general operations. When our router stopped working, Stephen got mobile hotspots for the whole co-working space.&lt;/p&gt;
&lt;p&gt;And Zoey does community management. She designed a drip coffee bar that cuts down on plastic waste for everyone to use. We even had the crew over at &lt;a href=&quot;http://www.mrespresso.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Mr. Espresso&lt;/a&gt; give us a lesson on how to make perfect drip coffee.&lt;sup&gt;&lt;a id=&quot;footnote-ref-4&quot; href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-4&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/port2-1024x768.jpg&quot; alt=&quot;port2&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;The staff (Michael, Zoey, Stephen and many others) and the co-workers at Port are passionate about what they do. And what they do isn’t the same buzzword-heavy software play that overcrowds co-working spaces across the Bay. They’re in music, law, art, home decor, cannabis, architecture, video production and even Antarctica.&lt;sup&gt;&lt;a id=&quot;footnote-ref-5&quot; href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-5&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; There is software too, of course, but it’s more focused. When we get together for weekly dinners or for a game of pool, we don’t just rehash the tech news of the day. We talk about a co-worker’s homemade wine, the best places to catch the sun setting over the Bay, experiences at Burning Man or what’s showing at next month’s Art Murmur.&lt;/p&gt;
&lt;p&gt;Port isn’t more cultured or anything like that–it’s just more diverse. It isn’t contaminated by technical elitism or overt, cold competition. It’s filled with interested, disparate people who are making something real.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/IMG_0545-1024x768.jpg&quot; alt=&quot;IMG 0545&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;And they’re friendly as hell, too.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The last piece in this series is &lt;a href=&quot;http://brennenbyrne.com/2013/09/oakland/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Oakland&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Well, that’s kind of an exaggeration. There are some great restaurants and &lt;a href=&quot;http://www.jacklondonsquare.com/events.php#ongoing_events/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;events&lt;/a&gt; happening in Jack London. Check out Jack&#39;s Night Market for an awesome mix of food and art. &lt;a href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;Of course, that&#39;s our fault too. It&#39;s easy to get into the rhythm of just working at work and neglecting the other aspects of the people around you – and that goes for a company of any size. The difference at Port was that people made it &lt;em&gt;easy&lt;/em&gt; to engage them. &lt;a href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-3&quot;&gt;
&lt;p&gt;And it got built in the next 2 days. &lt;a href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-ref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-4&quot;&gt;
&lt;p&gt;A drip coffee setup seems pretentious, but it’s a beautiful alternative to the water cooler. We’ve had drip coffee for about a week, and the number of interactions I’ve had with fellow co-workers has risen dramatically. It’s also about 10 bucks to make. &lt;a href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-ref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-5&quot;&gt;
&lt;p&gt;One startup at Port is building a way for researchers in Antarctica to share their experiences with each other and the outside world. They previously worked at &lt;a href=&quot;http://en.wikipedia.org/wiki/Palmer_Station&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Palmer Station&lt;/a&gt;. &lt;a href=&quot;https://markhudnall.com/2013/09/24/docking-at-port/#footnote-ref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>The 10-Hour Move</title>
      <link>https://markhudnall.com/2013/09/24/the-10-hour-move/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/09/24/the-10-hour-move/</guid>
      <pubDate>Tue, 24 Sep 2013 00:00:02 GMT</pubDate>
      <description>&lt;p&gt;&lt;strong&gt;This post is part of a larger media experiment describing why, and how, &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt; moved to Oakland. If you want to read more, visit the timeline at &lt;a href=&quot;http://oakland.is/home&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;oakland.is/home&lt;/a&gt; or tell us your thoughts with &lt;a href=&quot;https://twitter.com/search?q=%23oaklandis&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#oaklandis on Twitter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Brennen promised it would be like ripping off a bandaid. In the end, he was right. But at the time, it felt like ripping off the bandaid, putting it back on, and then ripping it off a few more times for good measure.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We moved to Oakland in less than 10 hours.&lt;/p&gt;
&lt;p&gt;That morning, we were working at our windowless, basement office in San Francisco. By the evening, we were sleeping on the floor of our new home in Oakland, ready to wake up the next day and drive to our new office at Jack London Square.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3pm&quot;&gt;3pm&lt;/h2&gt;
&lt;p&gt;The Bay Bridge closes tomorrow. We have to drive over the bridge to meet the owner of a house anyway, so Brennen emails Port Workspaces and asks if we can sign a year-long lease–and move in all our stuff–today. They respond almost immediately and give us the go ahead.&lt;/p&gt;
&lt;p&gt;We start disassembling the office.&lt;/p&gt;
&lt;h2 id=&quot;5pm&quot;&gt;5pm&lt;/h2&gt;
&lt;p&gt;Traffic is heavy on the bridge. We finally arrive at the house, just northwest of Lake Merritt. As we walked in, we spot the owner and the realtor in the backyard, watering some plants and laughing. We&#39;d actually scheduled another house to visit at 5:30pm, but in 20 minutes of meeting the owner and seeing the place again, we are talking about signing the lease. It&#39;s the place we want to call home.&lt;/p&gt;
&lt;h2 id=&quot;530pm&quot;&gt;5:30pm&lt;/h2&gt;
&lt;p&gt;Since it&#39;s such short notice, Port isn&#39;t quite ready with keys for us. But we don&#39;t mind. We&#39;d just driven south along Lake Merritt and realized we would be biking that route every day to work. Though the necklace of lights isn&#39;t yet lit, our faces are glowing.&lt;/p&gt;
&lt;h2 id=&quot;6-7pm&quot;&gt;6-7pm&lt;/h2&gt;
&lt;p&gt;One thing we notice immediately about Port is the sheer amount of glass (we actually have windows!). We get the idea to make our own whiteboards with panes of glass, hung on the wall with industrial velcro.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;We take a trip to Home Depot to get panes of glass and some other materials, including velcro and a drill. Glass is much, much heavier and sharper than I would have expected, but I&#39;ve also never handled glass before. As we drive away from Home Depot, I keep peering into the backseat, wondering whether the glass will shatter. When we get back to Port, we assemble the whiteboards and leave them on the floor to let the velcro adhesive dry.&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/whiteboards-1024x768.jpg&quot; alt=&quot;whiteboards&quot; loading=&quot;lazy&quot;&gt;&lt;h2 id=&quot;835pm-back-to-san-francisco&quot;&gt;8:35pm (Back to San Francisco)&lt;/h2&gt;
&lt;p&gt;We still need to get the rest of our stuff from the old office, so we head back over the bridge. This is the part that feels like slapping back on the bandaid. When we come back to our old office to get the tables, it no longer feels &lt;em&gt;ours&lt;/em&gt;. The shadows cast from street lights up above warp the walls. The office is done with us. We shove all the little items into boxes and load the car with tables.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/old_office-1024x768.jpg&quot; alt=&quot;old office&quot; loading=&quot;lazy&quot;&gt;&lt;h2 id=&quot;930pm&quot;&gt;9:30pm&lt;/h2&gt;
&lt;p&gt;It is time for a celebratory meal at Chipotle.&lt;sup&gt;&lt;a id=&quot;footnote-ref-3&quot; href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; As we eat our last burritos as founders in San Francisco, we overhear a group of guys at another table talking about a senior executive at a large company. &quot;I heard she slept with X (another executive) when she was working there.&quot; There&#39;s that bandaid again.&lt;/p&gt;
&lt;h2 id=&quot;1130pm&quot;&gt;11:30pm&lt;/h2&gt;
&lt;p&gt;We head back over the bridge, all very happy that we won&#39;t have to make another trip tonight. As we shuttle between the car and our office on the third floor, we hear the last train roll by and the bustle of people returning home from a late dinner.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/new_office-1024x768.jpg&quot; alt=&quot;new office&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;Our old office is empty and a memory to us. It&#39;s weird to leave a place completely, to be in a routine in one moment and then gone the next, without the natural distancing of sleep.&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/moved_in-1024x768.jpg&quot; alt=&quot;moved in&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;But here we are, nearing midnight, standing in our new office with our whiteboards drying and our standing desks ready.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;We still didn&#39;t have mattresses for our home (or even a fridge for that matter). But moving was like ripping off a bandaid, and underneath, I found the wound was fully healed. We had arrived.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The next piece in this series is &lt;a href=&quot;http://jessepolak.me/one-weird-trick-to-furnish-your-new-home-for-free/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;One weird trick to furnish your new home for free&lt;/a&gt;. (Don&#39;t worry, it isn&#39;t spam.)&lt;/em&gt;&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;We later found out that Port would have painted our walls with dry-erase paint if we had asked. &lt;a href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;We&#39;ll cover how we did this in another post.   &lt;a href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-3&quot;&gt;
&lt;p&gt;It wasn&#39;t all that celebratory. I was really hungry and really, really irritable at the time. &lt;a href=&quot;https://markhudnall.com/2013/09/24/the-10-hour-move/#footnote-ref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Moving Alone</title>
      <link>https://markhudnall.com/2013/09/24/moving-alone/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/09/24/moving-alone/</guid>
      <pubDate>Tue, 24 Sep 2013 00:00:01 GMT</pubDate>
      <description>&lt;p&gt;&lt;strong&gt;This post is part of a larger media experiment describing why, and how, &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt; moved to Oakland. If you want to read more, visit the timeline at &lt;a href=&quot;http://oakland.is/home&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;oakland.is/home&lt;/a&gt; or tell us your thoughts with &lt;a href=&quot;https://twitter.com/search?q=%23oaklandis&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#oaklandis on Twitter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I think box manufacturers purposefully make boxes just a little too awkwardly shaped and large. So when you try to move them by yourself, it’s nearly impossible, but when you have another person, it’s way too easy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We&#39;d missed it.&lt;/p&gt;
&lt;p&gt;The lease was up and we hadn&#39;t found another place, so we had to move into my parents’ house in Los Altos.&lt;/p&gt;
&lt;p&gt;Actually, &lt;em&gt;I&lt;/em&gt; had to move into my parent’s house in Los Altos. Jesse was backpacking for the weekend with his father and brother in the Sierras, something he’d planned months ago, and Brennen was speaking at WordCamp in Providence. I wasn’t angry they were gone and I didn’t feel like they deserted me. I just had to move everything by myself, and that sucks. A certain surreal feeling arises when profound emotion is mixed with administrative tasks. I’ve felt it when family dies and everyone is crying and sad, but the mortician still needs papers signed for the cremation and arrangements need to be made for the funeral, and we have to decide what type of flowers to have and what box to pick to hold the ashes.&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/09/24/moving-alone/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I’m still not sure whether the mundane tasks help ease emotional pain or make it worse.&lt;/p&gt;
&lt;p&gt;I can’t in good faith compare moving to a family death–that would be ridiculous. But I was still terrified. I had no sense of permanence. How long would we be staying with my dad? Two weeks? A month? A year? How long would it be until the commute made me lose it?&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2013/09/24/moving-alone/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Working at a startup means experiencing uncertainty every day. That takes a toll, but we bear it willingly because we love what we’re doing. But I don’t want everything in my life to be like that. I want to come back to a home, not just a place to sleep. I need a little certainty too. I don’t want to think about where I’ll be living in two weeks when I’m trying to help build a company that will last years.&lt;/p&gt;
&lt;p&gt;I was terrified, and I was alone. And there the boxes and suitcases were by the door, mocking me. So I did what people do when they’re scared and unsure about the monolithic future. I started the administrative tasks. I sat for a moment, grabbed two smaller bags and began loading up the car.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The next piece in this series is &lt;a href=&quot;https://brennenbyrne.com/2013/09/wandering-eyes/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Wandering Eyes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;It’s weird because you’re not sure whether the mortician is trying to upsell you. I wonder if the mortician feels guilty about how easy that is. &lt;a href=&quot;https://markhudnall.com/2013/09/24/moving-alone/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;Turns out, it was about two weeks. See the 10 hour move. &lt;a href=&quot;https://markhudnall.com/2013/09/24/moving-alone/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
<item>
      <title>Slim Pickings in San Francisco</title>
      <link>https://markhudnall.com/2013/09/24/slim-pickings/</link>
      <guid isPermaLink="true">https://markhudnall.com/2013/09/24/slim-pickings/</guid>
      <pubDate>Tue, 24 Sep 2013 00:00:00 GMT</pubDate>
      <description>&lt;p&gt;&lt;strong&gt;This post is part of a larger media experiment describing why, and how, &lt;a href=&quot;https://getclef.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Clef&lt;/a&gt; moved to Oakland. If you want to read more, visit the timeline at &lt;a href=&quot;https://oakland.is/home&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;oakland.is/home&lt;/a&gt; or tell us your thoughts with &lt;a href=&quot;https://twitter.com/search?q=%23oaklandis&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;#oaklandis on Twitter&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;img src=&quot;https://markhudnall.com/writing-assets/sunset-1024x664.jpg&quot; alt=&quot;sunset&quot; loading=&quot;lazy&quot;&gt;&lt;p&gt;It gets foggy in Excelsior. Not as much as the Sunset, but the &quot;wall of fog&quot; up there kind of leaks down and casts a gray in the mornings and evenings.&lt;/p&gt;
&lt;p&gt;On July 27th, though, the sun was rearing its head, and we were feeling good.&lt;/p&gt;
&lt;p&gt;We were about to visit an apartment only a few blocks away with high hopes. Rent was only $3000 a month, the bedrooms looked sunny and big in the Craigslist post, and it was almost next door. It wouldn&#39;t be the palace we&#39;d found for the summer, but it was close enough (and cheap enough) that we&#39;d be happy.&lt;/p&gt;
&lt;p&gt;I never considered $3000 a month cheap until I started looking for apartments in San Francisco. Every day we looked, we lowered our expectations a little more. It&#39;s amazing what relative pricing does to the brain - we started out with an absolute maximum of $3300, and by the end of the week, we were all looking at the $4000 apartments and wondering, &quot;Why couldn&#39;t we?&quot;&lt;/p&gt;
&lt;p&gt;That day I was bluntly educated by the city of San Francisco about what $3000 a month gets you. The bedrooms were tiny and narrow, like slightly widened hallways and nothing like the pictures. The bathroom (only one) was very small, and even though I&#39;m short,&lt;sup&gt;&lt;a id=&quot;footnote-ref-1&quot; href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-1&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt; I could barely fit my head underneath the shower head. But these were just gripes, things we could live with and get accustomed to over time.&lt;/p&gt;
&lt;p&gt;We continued doing our walkthrough-I like to circle around a few times pretending like I know all the things a responsible renter is supposed to look for-until we realized: there&#39;s no kitchen. The three of us were piled into the bathroom, quietly wondering aloud where the kitchen was, when we heard another prospective tenant ask the owner the same question.&lt;/p&gt;
&lt;p&gt;&quot;Sorry, there&#39;s no kitchen. But you can put a hotplate right here.&quot;&lt;sup&gt;&lt;a id=&quot;footnote-ref-2&quot; href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-2&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Brennen turned to me, flattened his mouth and said, &quot;Nope,&quot; and we booked it out of there.&lt;/p&gt;
&lt;p&gt;The place was not physically unlivable.&lt;sup&gt;&lt;a id=&quot;footnote-ref-3&quot; href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-3&quot; data-footnote-ref=&quot;&quot; aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; It had bedrooms, it had bathrooms, it had a place where food could be cooked. But it was $3000 a month. I didn&#39;t want to live there for a year and pay that price. What pained me is that most people &lt;em&gt;couldn&#39;t&lt;/em&gt; pay that price. I&#39;ve read the articles about tech people invading the city, paying 10 bucks for a slice of bread and a cup of coffee. And there I was, a tech person who comes from privilege in San Francisco looking for an apartment, increasing the cost of living for everyone. I wasn&#39;t an artist or an SF local or a student. I was the reason why, for $3000 a month, you got an apartment with a little counter where you could put a hotplate.&lt;/p&gt;
&lt;p&gt;I didn&#39;t want to be a part of that problem.&lt;/p&gt;
&lt;p&gt;I didn&#39;t want to make people pay exorbitantly to just get by.&lt;/p&gt;
&lt;p&gt;But dammit, I needed a place to live.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The next piece in this series is &lt;a href=&quot;https://markhudnall.com/2013/09/24/moving-alone/&quot;&gt;Moving Alone&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes=&quot;&quot;&gt;
&lt;h2 id=&quot;footnote-label&quot; class=&quot;sr-only&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;footnote-1&quot;&gt;
&lt;p&gt;Hey, still above average. &lt;a href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-ref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-2&quot;&gt;
&lt;p&gt;Something I (a naive college graduate) learned from apartment hunting: people lie on Craigslist. Well, they don&#39;t lie outright, they just don&#39;t tell you the whole truth until you actually get over there. Apartment hunting, like most human interaction, unfortunately, is all about due diligence. &lt;a href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-ref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;footnote-3&quot;&gt;
&lt;p&gt;Though legally, it wasn&#39;t an in-law apartment as advertised, since an in-law apartment must have a kitchen. &lt;a href=&quot;https://markhudnall.com/2013/09/24/slim-pickings/#footnote-ref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</description>
    </item>
</channel></rss>
