<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Implements Programmer - Posts</title>
    <link rel="self" type="application/atom+xml" href="https://www.thelonelyghost.com/posts/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-05-10T01:13:31-04:00</updated>
    <id>https://www.thelonelyghost.com/posts/atom.xml</id>
    <entry xml:lang="en">
        <title>Lessons in EKS</title>
        <published>2024-05-10T01:13:31-04:00</published>
        <updated>2024-05-10T01:13:31-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/lessons-in-eks/"/>
        <id>https://www.thelonelyghost.com/posts/lessons-in-eks/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/lessons-in-eks/">&lt;p&gt;I took for granted that managed Kubernetes offerings, like Amazon&#x27;s Elastic Kubernetes Service (EKS), would continue to Just Work (tm). What happens when they break down? Here are some lessons I&#x27;ve learned:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Sometimes, with high-churn workloads (e.g., knative), the de facto CNI of &lt;code&gt;vpc-cni&lt;&#x2F;code&gt; breaks down and refuses to assign more IP addresses. When that happens, you&#x27;re SOL.&lt;&#x2F;li&gt;
&lt;li&gt;Despite some clever Terraform code lining up the EKS cluster version and grabbing the latest version of the &lt;code&gt;vpc-cni&lt;&#x2F;code&gt; EKS add-on, do not trust that it is stable enough for production use until verified directly.&lt;&#x2F;li&gt;
&lt;li&gt;The EKS control plane (kube api) not permitting custom CNI plugins use definitely limits its utility and makes the entire deployment and verification more complex.&lt;&#x2F;li&gt;
&lt;li&gt;Setting &lt;code&gt;hostNetwork: true&lt;&#x2F;code&gt; or otherwise setting &lt;code&gt;hostPort:&lt;&#x2F;code&gt; on all of the operators&#x27; pod templates is both less secure and very difficult for some helm-based workloads that are missing a parameter that would control that feature.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;hostNetwork: true&lt;&#x2F;code&gt; workaround will almost certainly violate any decent &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;security&#x2F;pod-security-admission&#x2F;&quot;&gt;Pod Security Standards&lt;&#x2F;a&gt; for the given namespace.&lt;&#x2F;li&gt;
&lt;li&gt;The Kubernetes scheduler will still try to put a pod, which needs to expose a specific port on the host network, on a node where that same port is already in use.&lt;&#x2F;li&gt;
&lt;li&gt;Karpenter or Cluster Autoscaler is a &lt;em&gt;must&lt;&#x2F;em&gt; when using a custom CNI for IPAM. So many more nodes...&lt;&#x2F;li&gt;
&lt;li&gt;Calico talks about eBPF and other higher-order features, but none of them seem to work reliably in EKS. Overlay VXLAN networking barely works.&lt;&#x2F;li&gt;
&lt;li&gt;Just because a &lt;code&gt;Deployment&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;Daemonset&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;Statefulset&lt;&#x2F;code&gt; says its pods are healthy does not mean they are. Check the logs. All of the logs.&lt;&#x2F;li&gt;
&lt;li&gt;Just because you&#x27;ve run &lt;code&gt;helm upgrade&lt;&#x2F;code&gt; without &lt;code&gt;--reuse-values&lt;&#x2F;code&gt; does not mean any out-of-band, manual changes to the resources that helm chart manages have been reverted&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Intro to Blogging: Engineer Edition</title>
        <published>2023-07-10T04:41:00+00:00</published>
        <updated>2023-07-10T04:41:00+00:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/intro-to-blogging-engineer-edition/"/>
        <id>https://www.thelonelyghost.com/posts/intro-to-blogging-engineer-edition/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/intro-to-blogging-engineer-edition/">&lt;p&gt;Blogging is supposed to be super easy, low effort, and high reward. compare the effort it takes to produce a video for YouTube. Maybe how long it takes to code an application. Or publish an academic article. All of those make information you have to say publicly available, but how much work is put into it to get the content processed and over the finish line?&lt;&#x2F;p&gt;
&lt;p&gt;For this reason, among many others, blogging is a form of self-promotion, of self-discovery, and of public navel-gazing (you didn&#x27;t think it&#x27;s all sunshine and roses, did you?) where thoughts and ideas can be freely shared or ignored. The web is the public forum.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h1 id=&quot;the-classic-advice&quot;&gt;The classic advice&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-classic-advice&quot; aria-label=&quot;Anchor link for: the-classic-advice&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;start-writing&quot;&gt;Start writing&lt;a class=&quot;zola-anchor&quot; href=&quot;#start-writing&quot; aria-label=&quot;Anchor link for: start-writing&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;First and foremost, collect your thoughts in written form. Bullet lists, long form prose, mind maps that evolve over time. All of these are valid ways of iterating into a document one might publish online. Because this is a document.&lt;&#x2F;p&gt;
&lt;p&gt;Even if you don&#x27;t have a blog on a fancy website, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;http:&#x2F;&#x2F;wordpress.com&#x2F;&quot;&gt;Wordpress.com&lt;&#x2F;a&gt; is free. Many blog sites are free. Maybe follow in line with &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;sive.rs&#x2F;plaintext&quot;&gt;Derek Sivers’ advice&lt;&#x2F;a&gt; and write plain text notes for now. Just get started.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;it-will-never-be-perfect&quot;&gt;It will never be perfect&lt;a class=&quot;zola-anchor&quot; href=&quot;#it-will-never-be-perfect&quot; aria-label=&quot;Anchor link for: it-will-never-be-perfect&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Given a “blog” (originally from “web log”) is, by definition, an online medium, one can easily update a post after it has been made publicly available. This doesn&#x27;t mean put a stream of consciousness set of ramblings out there (or do, it might work well for you), but being okay with bullet points for key topics might be sufficient to put out there. It all depends on how formal you aim to be.&lt;&#x2F;p&gt;
&lt;p&gt;I, for one, tried to be too formal out of the gate and it led to sporadic bursts of posts here and there, only some of which survived into the blog seen today. I&#x27;m not a professional writer, nor do I am to have that level of polish. I do aim to be understood. If it is understandable, it is good enough. Publish. That&#x27;s my defining line.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-will-be-terrible-at-it-and-that-is-okay&quot;&gt;You will be terrible at it, and that is okay&lt;a class=&quot;zola-anchor&quot; href=&quot;#you-will-be-terrible-at-it-and-that-is-okay&quot; aria-label=&quot;Anchor link for: you-will-be-terrible-at-it-and-that-is-okay&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Every writer I have heard of, when asked, indicates how terrible of quality their written works were on the first draft, in their early career, or when they&#x27;re first organizing thoughts. That&#x27;s normal. Writing is a skill and, just like a muscle, it gets easier to do impressive things the more you practice in earnest.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;imposter-syndrome-is-real&quot;&gt;Imposter syndrome is real&lt;a class=&quot;zola-anchor&quot; href=&quot;#imposter-syndrome-is-real&quot; aria-label=&quot;Anchor link for: imposter-syndrome-is-real&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;There is always a non-zero chance that you have something of value to contribute. Even if it&#x27;s regurgitated facts learned elsewhere, maybe it&#x27;s phrased in a way that makes sense to others struggling with the topic. Maybe it helps solidify the topic in your mind. Maybe it indicates where your mind was at a certain point in history.&lt;&#x2F;p&gt;
&lt;p&gt;Clients and employers looking at your background may see this and get a better idea about how you think, what you&#x27;re interested in, and assess talent. With the amount of bluster out there it is refreshing, speaking as someone who has conducted countless interviews over the years, to have a candidate that is demonstrably transparent. Even if what you say is factually incorrect, a blog is your own platform and you can update it. Or even erase it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;contemporary-advice&quot;&gt;Contemporary advice&lt;a class=&quot;zola-anchor&quot; href=&quot;#contemporary-advice&quot; aria-label=&quot;Anchor link for: contemporary-advice&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;h2 id=&quot;my-audience-is-not-your-audience&quot;&gt;My audience is not your audience&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-audience-is-not-your-audience&quot; aria-label=&quot;Anchor link for: my-audience-is-not-your-audience&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;How do I know that? Because my audience is most often… me.&lt;&#x2F;p&gt;
&lt;p&gt;I write because I have a terrible memory. But because I write it down I get practice. And with practice I get better at writing. For me. Others may not see improvement, but that is because I&#x27;m not optimizing for them. They are not me.&lt;&#x2F;p&gt;
&lt;p&gt;Don&#x27;t mistake me for not caring. If others find benefit in my writing, I get a special kind of giddy in my soul. The off-chance of that happening is one reason I make my ramblings available. I also don&#x27;t have comments for a very particular reason: my comments to myself are in the form of edits to the original post or follow up posts.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t take myself seriously (anymore) and it is incredibly freeing.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-it-accessible&quot;&gt;Make it accessible&lt;a class=&quot;zola-anchor&quot; href=&quot;#make-it-accessible&quot; aria-label=&quot;Anchor link for: make-it-accessible&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;While web accessibility is a core value of mine, I mean to focus on accessibility to write. Don&#x27;t limit yourself to [only writing when sitting at a desk]({{&amp;lt;relref &quot;.&#x2F;blogging-made-easy.md&quot; &amp;gt;}}), pondering the wonders of life in your amazingly abundant free time. I never have that kind of free time. If I do, I&#x27;d rather write code or read technical documents, or maybe even immerse myself in a good fantasy novel or video game. But that rarely happens without me falling asleep midway through.&lt;&#x2F;p&gt;
&lt;p&gt;My writing process looks like composition in Notion, from my phone, writing and revising the meat of the article when and where I have time. 20 minutes while rocking my eldest back to sleep, 5 minutes while waiting for my order to be finished at the coffee shop, whenever I&#x27;m available. I don&#x27;t leave it as the source of truth because I have strong feelings now, in my old age (heh) about having control over my own data. When my revisions get to a certain point I move it to a “queued” state where some automation periodically checks for any posts in that state, publishes the extracted markdown from them to my static site generator’s git repository, and updates the post in Notion to move the post to the next state.&lt;&#x2F;p&gt;
&lt;p&gt;I write anywhere and can push to public when I feel like it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;you-have-more-to-say-than-you-realize&quot;&gt;You have more to say than you realize&lt;a class=&quot;zola-anchor&quot; href=&quot;#you-have-more-to-say-than-you-realize&quot; aria-label=&quot;Anchor link for: you-have-more-to-say-than-you-realize&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When I start enumerating my thoughts into words, I keep tripping over myself with “but first” and “prerequisite knowledge to understand that is…”&lt;&#x2F;p&gt;
&lt;p&gt;To avoid getting caught up in that loop, leave a loose end here and there. One need not explain the world when it is a note to oneself. It is helpful to lean into the online medium by linking to other content. Maybe someone else wrote something already. Maybe it&#x27;s more eloquent than you could write. Maybe it&#x27;s more polished or speaks to a different crowd than you intend. Or maybe you wrote it already. Link to it. If your audience already knows about consensus algorithms and how Raft differs from Zookeeper’s approach, they&#x27;ll skip the link as a given. If the audience is confused, and has the wherewithal to dig deeper, they&#x27;ll click through to the linked page. Lean on the world wide web being… a web.&lt;&#x2F;p&gt;
&lt;p&gt;Also, keep in mind that, when writing this article, it only started as about 5 bullet points in a list. Now I can&#x27;t shut up!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-never-fully-finish-my-thought&quot;&gt;I never fully finish my thought&lt;a class=&quot;zola-anchor&quot; href=&quot;#i-never-fully-finish-my-thought&quot; aria-label=&quot;Anchor link for: i-never-fully-finish-my-thought&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Again, it doesn&#x27;t need to be perfect. It also doesn&#x27;t need to be any certain length. Plenty of folks blog within a 500 character limit. It&#x27;s called micro-blogging, done by applications like Mastodon, Twitter, and the like.&lt;&#x2F;p&gt;
&lt;p&gt;A partial thought is still a thought worth having. Post it. Edit it later. Or don&#x27;t.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;own-your-content&quot;&gt;Own your content&lt;a class=&quot;zola-anchor&quot; href=&quot;#own-your-content&quot; aria-label=&quot;Anchor link for: own-your-content&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Ever think that social media sites own way too much of what you present as your public image? That they can dictate if you&#x27;re allowed to speak in what is thought of as a public forum? Host it yourself.&lt;&#x2F;p&gt;
&lt;p&gt;Many free services are available to host a static site or small-time blog. I like to host mine on Netlify, though I have tinkered with GitHub pages, Vercel, and Amazon S3 before. Keep a copy of your posts locally and build it as HTML ahead of time. It is much harder to hack a system that doesn&#x27;t do anything dynamically.&lt;&#x2F;p&gt;
&lt;p&gt;Buy a domain for $10 a year. Point to a set of links under that domain name. Even if you change where it is hosted, others’ links will remain active and your blog will survive. Your information will be more difficult to censor.&lt;&#x2F;p&gt;
&lt;p&gt;Twitter, Facebook, etc. are allowed to monetize you (mostly) or delete everything about you without your consent. Why not take ownership? Check out the &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;indieweb.org&#x2F;&quot;&gt;Indie Web movement&lt;&#x2F;a&gt; for more on this topic.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Dopamine to Create</title>
        <published>2023-01-01T12:38:17-05:00</published>
        <updated>2023-01-01T12:38:17-05:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/dopamine-to-create/"/>
        <id>https://www.thelonelyghost.com/posts/dopamine-to-create/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/dopamine-to-create/">&lt;p&gt;Looking at the Nivenly Discord, I saw Hachyderm.io was having some HTTP 504 issues indicating some backend service was malfunctioning. This lead to a &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.hachyderm.io&#x2F;blog&#x2F;2022&#x2F;12&#x2F;13&#x2F;global-outage-504-timeouts&#x2F;&quot;&gt;link of a post-mortem&lt;&#x2F;a&gt; last time this happened.&lt;&#x2F;p&gt;
&lt;figure class=&quot;quote&quot;&gt;
    &lt;blockquote url=&quot;https:&#x2F;&#x2F;community.hachyderm.io&#x2F;blog&#x2F;2022&#x2F;12&#x2F;13&#x2F;global-outage-504-timeouts&#x2F;&quot;&gt;
        &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;19:06&lt;&#x2F;strong&gt; &lt;code&gt;@dma&lt;&#x2F;code&gt; status.hachyderm.io updated acknowledging the root cause&lt;&#x2F;li&gt;
        &lt;&#x2F;ul&gt;
    &lt;&#x2F;blockquote&gt;
    &lt;figcaption&gt;
        From &lt;cite&gt;&lt;a href=&quot;https:&#x2F;&#x2F;community.hachyderm.io&#x2F;blog&#x2F;2022&#x2F;12&#x2F;13&#x2F;global-outage-504-timeouts&#x2F;&quot;&gt;blog.hachyderm.io&lt;&#x2F;a&gt;&lt;&#x2F;cite&gt;
    &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;This small snippet from the timeline sparked an urge. It was an urge to do web development for a running service, not just a static site or two—of which this very blog is one.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve felt this urge before: create something new, get that dopamine hit, and ride high on that feeling of having accomplished something productive; even if that thing is MySpace for dogs and literally receives zero traffic, even from bots.&lt;&#x2F;p&gt;
&lt;p&gt;Why would it satisfy that need to create something? Would it actually provide some level of utility, or is it polishing the tile in a bathroom that&#x27;s about to be renovated?&lt;&#x2F;p&gt;
&lt;p&gt;It makes me wonder how much of my need to do &quot;cool tech things&quot; is based almost entirely on getting that next hit of dopamine, imagining I&#x27;m creating something or pushing society forward with the progress I make. Is it sweeter if I can observe as it happens and bask in the &lt;em&gt;potential&lt;&#x2F;em&gt; utility? For some hypothetical person? Or is the delayed gratification of seeing a fully working, but far more complex mechanism after a large binge of effort? Also for some hypothetical person?&lt;&#x2F;p&gt;
&lt;p&gt;The urge to create passed and I realized I should channel my inspiration into journaling. At least then I&#x27;ll have something useful to show for it, years down the road.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Neurodivergence and Remote Work</title>
        <published>2022-06-05T16:10:40-04:00</published>
        <updated>2022-06-05T16:10:40-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/neurodivergence-and-remote-work/"/>
        <id>https://www.thelonelyghost.com/posts/neurodivergence-and-remote-work/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/neurodivergence-and-remote-work/">&lt;p&gt;I&#x27;m seeing an influx of people talking about &quot;everyone back to the office!&quot; with responses like &quot;lol, I moved. When should I expect the travel itinerary with accommodations you&#x27;ve booked?&quot; I too have moved. I too would respond this way. I too believe if I am able to do my job well remotely, it &lt;em&gt;shouldn&#x27;t friggin&#x27; matter&lt;&#x2F;em&gt; if you&#x27;ve spent millions of dollars on a state of the art office. Your poor investment is not my problem.&lt;&#x2F;p&gt;
&lt;p&gt;This is the prevailing thought for many and, again, I share it, but what isn&#x27;t discussed too often is the massive benefit neurodivergent people get out of a fully remote work situation. It is much easier to be more productive when we don&#x27;t have to mask for long periods of time. It drastically improves quality of life when we actually have energy at the end of the day which normally would have been expended white-knuckling through our natural responses to, for example, Terry coming over for some water cooler chit chat while we&#x27;re focused on something or the air conditioner blowing frigid air on just one part of one shoulder. Or how we can&#x27;t make any clicks or noises or other more obvious tics that help soothe the growing stress of the environment because... it would fundamentally change others&#x27; views of what we are capable of handling.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s unpack that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-neurodivergence&quot;&gt;What is Neurodivergence?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-neurodivergence&quot; aria-label=&quot;Anchor link for: what-is-neurodivergence&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Generally, when people talk about neurodivergence (ND), they contrast it with &quot;neurotypical&quot; behavior. There is no agreed-upon, defining line between what is neurotypical and what is neurodivergent, but what is often epitomized as neurodivergent linked to stereotypical autistic quirks. Things like&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Aversion to certain sensory stimuli: sights, sounds, smells, textures, flavors, etc.
&lt;ul&gt;
&lt;li&gt;Dislike of t-shirts with a seam at the end of the shoulder or only wearing ones without tags&lt;&#x2F;li&gt;
&lt;li&gt;Noise level that neurotypical people find acceptable or don&#x27;t notice being disruptive (too loud or not loud enough) for neurodivergent people&lt;&#x2F;li&gt;
&lt;li&gt;Gagging at the smell of a bonfire&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Readily apparent behavior (ie. &quot;tics&quot;) that is done periodically to soothe one&#x27;s growing mental discomfort.
&lt;ul&gt;
&lt;li&gt;Twitch of the arm, fingers, legs, neck, facial muscles, etc.&lt;&#x2F;li&gt;
&lt;li&gt;Making of nonsensical sounds or audibly reciting a mantra whose meaning matters less than the repetition itself&lt;&#x2F;li&gt;
&lt;li&gt;Toussling hair or scratching some spot, regarless of if it itches&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Fundamentally different perspectives, values, and methods when seeing the same objective circumstances&lt;&#x2F;li&gt;
&lt;li&gt;Hyper-fixation or extreme interest in one specific topic for a given period of time (maybe life-long, maybe for a few hours)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;These are some of the common patterns neurodivergent people seem to follow, many having their own reasons for each behavior. It means those with Tourette&#x27;s Syndrome, Autism Spectrum Disorder (ASD), Attention Deficit Disorder (ADD or ADHD), and more are commonly considered neurodivergent. Why does this matter? What does this mean for neurotypical people?&lt;&#x2F;p&gt;
&lt;p&gt;We just think differently. Our brains are built differently. There are cases where this is a &lt;em&gt;massive&lt;&#x2F;em&gt; benefit compared to neurotypical people. Some of us see patterns more easily when others only see chaos. Hyper-fixation especially pays off in knowledge-based work like programming, information security, and similarly technical career paths. You even see some hiring managers (perhaps unknowingly) paying homage to this with the expressed desire of a &quot;T-shaped&quot; engineer. They&#x27;re referring to someone who is a good generalist, but also knows a metric crap-ton about at least one specific area of expertise.&lt;&#x2F;p&gt;
&lt;p&gt;I am neurodivergent. I was diagnosed with ADHD from an early age. Even now, I&#x27;m the textbook definition of a person with ADHD.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-is-masking&quot;&gt;What is Masking?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-is-masking&quot; aria-label=&quot;Anchor link for: what-is-masking&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Neurodivergent people may go undiagnosed with ASD, ADHD, or other labels for years. Maybe they never get diagnosed. Until they get that diagnosis &lt;em&gt;and&lt;&#x2F;em&gt; become comfortable publicly having one of those labels, it is often reinforced at an early age to &quot;act normal.&quot; For those of us with ADHD, it was to &quot;stop fidgeting in [our] seat.&quot; It was to otherwise hide our behaviors that weren&#x27;t neurotypical, almost like putting on a mask over our true selves so we could go about life uninhibited by public perception. But it&#x27;s exhausting.&lt;&#x2F;p&gt;
&lt;p&gt;Masking takes energy to suppress what behaviors come naturally. Anecdotally, I see a lot of neurodivergent people also identifying as introverted.&lt;&#x2F;p&gt;
&lt;section class=&quot;tip message is-info&quot;&gt;
	&lt;header class=&quot;message-header&quot;&gt;
		&lt;p&gt;Introversion vs. Extraversion&lt;&#x2F;p&gt;
	&lt;&#x2F;header&gt;
	&lt;div class=&quot;message-body&quot;&gt;
		&lt;p&gt;Introversion can be defined as requiring use of energy to engage is social behavior. Some behaviors take more energy than others. Recharging energy is often done through solitary activities such as reading, sleeping, knitting, or playing video games.&lt;&#x2F;p&gt;
&lt;p&gt;Extraversion is just the opposite: social behavior results in recharging energy.&lt;&#x2F;p&gt;
&lt;p&gt;Introversion and extraversion can be considered a spectrum. Rarely is a person purely extraverted or purely introverted.&lt;&#x2F;p&gt;

	&lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
&lt;p&gt;I, for example, mask to put on a kind face, actively listening, and expressions on my face that empathize with the person speaking. Though I genuinely feel that empathy, kindness, and desire to actively listen, it doesn&#x27;t come as naturally to me as it might for others. I consciously do it. And it takes energy.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-does-this-have-to-do-with-remote-work&quot;&gt;What Does This Have To Do with Remote Work?&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-does-this-have-to-do-with-remote-work&quot; aria-label=&quot;Anchor link for: what-does-this-have-to-do-with-remote-work&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;When someone like me is masking, it takes effort to maintain that mask just like it does to carry in the 743 bags of groceries mom came home with all in one trip. It&#x27;s difficult at first, but pretty manageable if you&#x27;re quick to finish unloading the groceries (or whatever task is required), but as time goes on, you increasingly need a break. It starts to wear on you, marking your arms with the indentations that are painful, and making you irritable if people distract you from just finishing that task. But it&#x27;s a muscle. The more often you do it, the easier it becomes to maintain it until the task is complete. The less often you need to take a break, get a better grip, and continue on. But it&#x27;s still friggin&#x27; exhausting.&lt;&#x2F;p&gt;
&lt;p&gt;Face-to-face meetings require masking. Masking is like carrying in those groceries. I&#x27;ve gotten fairly good at masking, but it still takes energy that would be better used on absolutely anything else. When working in an office, I need to keep my mask on all day. Basically every single moment unless I find a private spot (e.g., bathroom stall) to take a break and get a grip again. With remote work, it&#x27;s anytime that I&#x27;m not actively on camera in a meeting.&lt;&#x2F;p&gt;
&lt;p&gt;This means that energy throughout the day that would have been spent making myself behaviorally presentable to society could be repurposed. Maybe I have energy at the end of the day to play with my kids, or to put up those curtains I&#x27;ve had on my to-do list for months, or cook dinner instead of get takeout for the 4th time this week. This means I have energy for a deep-dive on a given topic and can lean into my hyper-fixation for the benefit of my team.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-you-should-care&quot;&gt;Why You Should Care&lt;a class=&quot;zola-anchor&quot; href=&quot;#why-you-should-care&quot; aria-label=&quot;Anchor link for: why-you-should-care&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Companies would not get this much productivity or benefit out of me if it were in-person, regardless of how awesome of a company it is to work for or how acceptable it is for me to go about my day without masking (I&#x27;d still wear a mask, regardless). I wouldn&#x27;t be nearly as mentally and emotionally present for my wife and kids if I had to use that energy to not get on someone&#x27;s to-fire list.&lt;&#x2F;p&gt;
&lt;p&gt;Remote work has made me that much more successful. So why go into the office if you can do your job better remotely?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Facets of a Good README</title>
        <published>2022-01-25T22:11:01-04:00</published>
        <updated>2022-01-25T22:11:01-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/a-good-readme/"/>
        <id>https://www.thelonelyghost.com/posts/a-good-readme/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/a-good-readme/">&lt;p&gt;I see a good project README answering the following 3 questions:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;What does this do?&lt;&#x2F;li&gt;
&lt;li&gt;How do I install&#x2F;use it?&lt;&#x2F;li&gt;
&lt;li&gt;What do I need to do&#x2F;know to make changes?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I tend to answer this with a few key sections of a README with some self-evident headings. Doing so, in my case, generally looks something like this:&lt;&#x2F;p&gt;
&lt;pre arial-label=&quot;Markdown file contents&quot;&gt;

# My Project

This is my project. It does cool things and talks to neat people.

## Requirements

- Python (&gt;= 3.7.1)
- [Poetry](https:&#x2F;&#x2F;python-poetry.org&#x2F;)
- Sanity

## Usage

```
user@localhost $ my-project --do-things fun --often
This is the output. Take actual output and put
it in here, then replace personal details like
usernames and IP addresses with generic placeholders
```

## Build

Run the following:

```
user@localhost $ poetry install
user@localhost $ poetry build
```

### Testing

```
user@localhost $ poetry run pytest
```

&lt;&#x2F;pre&gt;
&lt;p&gt;Some key concepts to keep it brief:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prefer linking to other docs whenever possible. If you don&#x27;t understand a thing that is a hyperlink, you&#x27;re going to click through to find out more info about it. Make your life easy and rely on prior art&lt;&#x2F;li&gt;
&lt;li&gt;Enumerate organization- or project-specific caveats or changes to what is posted in linked content in the README&lt;&#x2F;li&gt;
&lt;li&gt;Show, don&#x27;t describe. Give example and, where possible, include example output.&lt;&#x2F;li&gt;
&lt;li&gt;Plaintext is better, where possible. For terminal output, paste the output itself instead of embedding a screenshot&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Nix Is Worth the Complexity</title>
        <published>2021-07-04T01:11:27-04:00</published>
        <updated>2021-07-04T01:11:27-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/nix-is-worth-the-complexity/"/>
        <id>https://www.thelonelyghost.com/posts/nix-is-worth-the-complexity/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/nix-is-worth-the-complexity/">&lt;p&gt;Recently I&#x27;ve gotten fed up with the breaking changes in &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;brew.sh&#x2F;&quot;&gt;Homebrew package manager&lt;&#x2F;a&gt;. After some research, using &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;&quot;&gt;Nixpkgs&lt;&#x2F;a&gt; seemed like a far more stable option for GNU&#x2F;Linux tooling on macOS, albeit with a decent learning curve for configuration.&lt;&#x2F;p&gt;
&lt;p&gt;Without going too much further into it &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;nixos.org&#x2F;guides&#x2F;how-nix-works.html&quot;&gt;Nix is pretty cool&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Over the following months, I&#x27;d been spending what free time I had tinkering with Nix on macOS, specifically with &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nix-community&#x2F;home-manager&quot;&gt;Home Manager&lt;&#x2F;a&gt; and &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;daiderd.com&#x2F;nix-darwin&#x2F;&quot;&gt;nix-darwin&lt;&#x2F;a&gt;. Nix is cross-platform between Linux and macOS, and, frankly, I found myself maintaining an increasing number of shell scripts for installing important tools I use. I got &lt;em&gt;really&lt;&#x2F;em&gt; good at writing bash scripts. 🤣&lt;&#x2F;p&gt;
&lt;p&gt;Bash scripts are really handy, but there are limits. There is no clean state with them, there&#x27;s only whatever you&#x27;re working with right then. You try your best to make them idempotent, but there&#x27;s no reasonable way to test that they meet that expectation. It can only be reliably tested from a clean state once. Conversely, Nix builds in a clean room every time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-pollute-the-global-state&quot;&gt;Don&#x27;t pollute the global state&lt;a class=&quot;zola-anchor&quot; href=&quot;#don-t-pollute-the-global-state&quot; aria-label=&quot;Anchor link for: don-t-pollute-the-global-state&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Nix is a clean state, it&#x27;s purpose-built for isolation between each program, allowing me to better follow the adage &quot;don&#x27;t mutate global state&quot; and sandbox each tool I needed. Then I could selectively upgrade and, if the upgrade broke something, roll back to previous state easily.&lt;&#x2F;p&gt;
&lt;p&gt;It has happened more times than I can count, I help my coworkers through a borked python setup when the underlying python version gets upgraded in-place. Thanks &lt;code&gt;brew upgrade&lt;&#x2F;code&gt;... 😑&lt;&#x2F;p&gt;
&lt;p&gt;Instead of digging through all the virtualenvs out there, and rummaging through whether pyenv was set up right for that shell, or any number of other issues, why not decouple it?&lt;&#x2F;p&gt;
&lt;p&gt;Nix offers the best of both &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.geeksforgeeks.org&#x2F;static-and-dynamic-linking-in-operating-systems&#x2F;&quot;&gt;dynamic and static linking&lt;&#x2F;a&gt; when building an application. It allows for multiple versions of python 3 at the same time. Or Java. Or Haskell. Or Go. Or glibc. Upgrade one library, then it doesn&#x27;t need to update them all. Similarly if 5 applications all use the same library, there&#x27;s no reason to duplicate it 5 times on the disk.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keep-project-specific-tools-with-the-project&quot;&gt;Keep project-specific tools with the project&lt;a class=&quot;zola-anchor&quot; href=&quot;#keep-project-specific-tools-with-the-project&quot; aria-label=&quot;Anchor link for: keep-project-specific-tools-with-the-project&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I tend to use &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;direnv.net&#x2F;&quot;&gt;direnv&lt;&#x2F;a&gt; in my workflow for exactly this purpose: I can keep project-specific settings (e.g., tool selection) specific to that base directory. Unfortunately this is typically limited to &lt;em&gt;versions&lt;&#x2F;em&gt; of known programs (e.g., python 3.7.3 instead of python 3.9.1) and workstation-specific environment variables (e.g., path to secret files).&lt;&#x2F;p&gt;
&lt;p&gt;Introducing Nix, this changes my workflow. By using &lt;code&gt;nixify&lt;&#x2F;code&gt; (roughly inspired by &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ejpcmac&#x2F;config&#x2F;blob&#x2F;bc9ee4e7363e4e0ca97f4addbdd9370b83048d3c&#x2F;zsh&#x2F;direnv.zsh#L33-L138&quot;&gt;this bash function&lt;&#x2F;a&gt;) I am able to install and use postgres, limiting it only to being used in this one project directory. Maybe I&#x27;ll (re)use postgres in another project. Do I need it installed globally? Absolutely not. This is a development machine, not an application server.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;current-state-of-integration&quot;&gt;Current state of integration&lt;a class=&quot;zola-anchor&quot; href=&quot;#current-state-of-integration&quot; aria-label=&quot;Anchor link for: current-state-of-integration&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve been working with &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nix-community&#x2F;home-manager&quot;&gt;Home Manager&lt;&#x2F;a&gt; for managing my dotfiles and (user-scoped) system configuration. So far it has been difficult translating certain parts of &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;thoughtbot&#x2F;rcm&quot;&gt;RCM&lt;&#x2F;a&gt;&#x27;s framework, such as its overlay approach (having both &lt;code&gt;~&#x2F;.dotfiles&lt;&#x2F;code&gt; and &lt;code&gt;~&#x2F;.dotfiles-local&lt;&#x2F;code&gt; repos cloned with the latter containing higher priority config files).&lt;&#x2F;p&gt;
&lt;p&gt;Instead of symlinking files into place, thereby ensuring any changes to them in-place are reflected back in the git repo, they&#x27;re made immutable. The &lt;em&gt;only&lt;&#x2F;em&gt; way to change them is from the git repo.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve begun ripping out the version managers like pyenv, asdf, chruby, and others to completely replace it with project-specific Nix expressions.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>7 principles of a good sysadmin</title>
        <published>2021-04-12T12:35:00-04:00</published>
        <updated>2021-04-12T12:35:00-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/sysadmin-principles/"/>
        <id>https://www.thelonelyghost.com/posts/sysadmin-principles/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/sysadmin-principles/">&lt;p&gt;This seems to be a common topic of conversation, so I figure I should put it on paper (so to speak) what I value as a systems administrator, or &quot;sysadmin.&quot;&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Keep it simple&lt;&#x2F;li&gt;
&lt;li&gt;Ensure it can be reproduced&lt;&#x2F;li&gt;
&lt;li&gt;Keep it close to stock&lt;&#x2F;li&gt;
&lt;li&gt;Magic is bad&lt;&#x2F;li&gt;
&lt;li&gt;No development tools on the server&lt;&#x2F;li&gt;
&lt;li&gt;Prefer complexity at compile time over runtime&lt;&#x2F;li&gt;
&lt;li&gt;Consume artifacts&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;What does this mean?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-fewer-moving-parts-the-easier-to-diagnose&quot;&gt;The fewer moving parts, the easier to diagnose&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-fewer-moving-parts-the-easier-to-diagnose&quot; aria-label=&quot;Anchor link for: the-fewer-moving-parts-the-easier-to-diagnose&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;By keeping things simple, reproduceable, and close to their defaults, this sets a sysadmin up for success &lt;em&gt;when&lt;&#x2F;em&gt; things go wrong. More so, by keeping things close to the default settings you maximize the chance that your setup overlaps someone else&#x27;s. Bonus to finding info on Stack Overflow or &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;xkcd.com&#x2F;979&#x2F;&quot;&gt;that one forum post&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;section class=&quot;tip message is-info&quot;&gt;
	&lt;header class=&quot;message-header&quot;&gt;
		&lt;p&gt;&lt;a name=&quot;tip-script-it&quot;&gt;&lt;&#x2F;a&gt;TIP: Script it out&lt;&#x2F;p&gt;
	&lt;&#x2F;header&gt;
	&lt;div class=&quot;message-body&quot;&gt;
		&lt;p&gt;By scripting out everything you do, no matter how small, this ensures you can walk away mid-thought and pick up where you left off later.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Keep scripts in some central location to share with your team of sysadmins&lt;&#x2F;li&gt;
&lt;li&gt;Version control systems (VCS) are best for iterating on these scripts&lt;&#x2F;li&gt;
&lt;li&gt;Make the VCS repo private, lest credentials are mistakenly hardcoded&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Make your shell script &lt;em&gt;executable documentation&lt;&#x2F;em&gt;. Write it by defining your own shell functions describing each step you&#x27;re taking.&lt;&#x2F;p&gt;

	&lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
&lt;h2 id=&quot;know-your-tools&quot;&gt;Know your tools&lt;a class=&quot;zola-anchor&quot; href=&quot;#know-your-tools&quot; aria-label=&quot;Anchor link for: know-your-tools&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;If you don&#x27;t understand how a thing works, fix that. Learn about it. Pull back that abstraction layer and look under the hood.&lt;&#x2F;p&gt;
&lt;p&gt;Why is magic bad? When you&#x27;re troubleshooting some error, how can you logically rule out the tool as a contributing factor?&lt;&#x2F;p&gt;
&lt;p&gt;This principle does not preclude you from using said magical tool, but it does mandate you dispel that magic by working to understand how it is implemented.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;compile-time-vs-runtime&quot;&gt;Compile time vs. runtime&lt;a class=&quot;zola-anchor&quot; href=&quot;#compile-time-vs-runtime&quot; aria-label=&quot;Anchor link for: compile-time-vs-runtime&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In a sysadmin context, compile time can mean &quot;the stuff done to configure and setup a service, system, or application before it is immediately needed.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;In contrast, runtime means &quot;stuff being done as the application is being put into its &#x27;running&#x27; state.&quot;&lt;&#x2F;p&gt;
&lt;section class=&quot;example message is-dark&quot;&gt;
	&lt;header class=&quot;message-header&quot;&gt;
		&lt;p&gt;Example: Docker&lt;&#x2F;p&gt;
	&lt;&#x2F;header&gt;
	&lt;div class=&quot;message-body&quot;&gt;
		&lt;p&gt;Some container images contain a shell script as an entrypoint (e.g., &lt;code&gt;entrypoint.sh&lt;&#x2F;code&gt;). These shift some compile time tasks to execute at runtime, then defer to running the underlying application.&lt;&#x2F;p&gt;
&lt;p&gt;Reasons for this design might be:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;More in-depth configuration changes required to pivot between environments&lt;&#x2F;li&gt;
&lt;li&gt;Rapid action may be required to change credentials, so they&#x27;re only passed at runtime&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Actions taken in a &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; when &lt;code&gt;docker build&lt;&#x2F;code&gt; is run are considered &quot;compile time.&quot;&lt;&#x2F;p&gt;
&lt;p&gt;Actions taken when running &lt;code&gt;docker exec&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;docker run&lt;&#x2F;code&gt; are considered &quot;runtime.&quot;&lt;&#x2F;p&gt;

	&lt;&#x2F;div&gt;
&lt;&#x2F;section&gt;
&lt;p&gt;Complexity costs at compile time need only be paid down once: when things are being setup.&lt;&#x2F;p&gt;
&lt;p&gt;If at runtime, cost of complexity is paid down every time the application is started.&lt;&#x2F;p&gt;
&lt;p&gt;Keep it simple. Pay down the cost as soon as possible.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;artifacts-are-like-gold&quot;&gt;Artifacts are like gold&lt;a class=&quot;zola-anchor&quot; href=&quot;#artifacts-are-like-gold&quot; aria-label=&quot;Anchor link for: artifacts-are-like-gold&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Whether the application uses an interpreted language (e.g., python) or is statically compiled (e.g., golang), an artifact can be built to make rolling forward and reverting simple.&lt;&#x2F;p&gt;
&lt;p&gt;In the case of python, a wheel (&lt;code&gt;my_pkg-0.1.0-py3-any.whl&lt;&#x2F;code&gt;) is a well-formed package holding the python source code. In a &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;tutorial&#x2F;venv.html&quot;&gt;venv&lt;&#x2F;a&gt;, install it with &lt;code&gt;pip install .&#x2F;my_pkg-0.1.1-py3-any.whl&lt;&#x2F;code&gt; to upgrade and &lt;code&gt;pip install .&#x2F;my_pkg-0.1.0-py3-any.whl&lt;&#x2F;code&gt; to roll back.&lt;&#x2F;p&gt;
&lt;p&gt;With golang it&#x27;s even easier. Just drop the new binary in place and, if it doesn&#x27;t work, drop the old binary in that spot instead.&lt;&#x2F;p&gt;
&lt;p&gt;Some VCS providers, like GitHub.com, allow uploading binaries and other assets related to a release to a location that can be accessed later, perhaps even by shell scripts.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Chef goes RedHat</title>
        <published>2019-04-04T23:19:01-04:00</published>
        <updated>2019-04-04T23:19:01-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/chef-goes-redhat/"/>
        <id>https://www.thelonelyghost.com/posts/chef-goes-redhat/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/chef-goes-redhat/">&lt;p&gt;Given the &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;blog.chef.io&#x2F;chef-software-announces-the-enterprise-automation-stack&quot;&gt;recent news&lt;&#x2F;a&gt; that broke about Chef&#x27;s changes to their licensing model (and the associated &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.chef.io&#x2F;pricing&#x2F;subscription-model-faq&quot;&gt;FAQ&lt;&#x2F;a&gt;), a lot of chaos has ensued and I feel the need to straighten out a few things since I&#x27;ve been present for most every public development so far.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a quick overview:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Rebranding of products&lt;&#x2F;li&gt;
&lt;li&gt;Open source release of Chef Automate&lt;&#x2F;li&gt;
&lt;li&gt;Change in licensing, affecting monetization&lt;&#x2F;li&gt;
&lt;li&gt;Price increase for commercial licenses&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let&#x27;s break these down into more consumable pieces.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rebranding-of-products&quot;&gt;Rebranding of products&lt;a class=&quot;zola-anchor&quot; href=&quot;#rebranding-of-products&quot; aria-label=&quot;Anchor link for: rebranding-of-products&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This part is a relatively minor, semantics change for clarity and consistent brand. Here&#x27;s a quick guide:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Chef&lt;&#x2F;code&gt; (config management tool) → &lt;code&gt;Chef Infra&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Habitat&lt;&#x2F;code&gt; → &lt;code&gt;Chef Habitat&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;InSpec&lt;&#x2F;code&gt; → &lt;code&gt;Chef InSpec&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Automate&lt;&#x2F;code&gt; → &lt;code&gt;Chef Automate&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As part of this rebranding, Chef (hereafter a term used solely to refer to the company) will be bundling their individual products as part of an &quot;Effortless Infrastructure&quot; and &quot;Enterprise Automation&quot; stack. This becomes important later.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;emphasis-on-open-source&quot;&gt;Emphasis on open source&lt;a class=&quot;zola-anchor&quot; href=&quot;#emphasis-on-open-source&quot; aria-label=&quot;Anchor link for: emphasis-on-open-source&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Per their announcement, Chef will be licensing the source code for Chef Automate under the same Apache 2.0 license as the rest of Chef&#x27;s intellectual property. The release of this source code is to take place on April 16, 2019.&lt;&#x2F;p&gt;
&lt;p&gt;Shocking, right? My immediate question was, &quot;How will they make money? All support contracts and professional services?&quot; That&#x27;s where the licensing change comes in.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;license-change&quot;&gt;License change&lt;a class=&quot;zola-anchor&quot; href=&quot;#license-change&quot; aria-label=&quot;Anchor link for: license-change&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Here&#x27;s where things get a little dicey. All of the official statements from Chef have indicated the following:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The source code for all Chef products (listed above) are to have their source code released under the Apache 2.0 license.&lt;&#x2F;li&gt;
&lt;li&gt;Binary distributions of the Chef products are to be provided under &lt;a href=&quot;#&quot;&gt;a different license&lt;&#x2F;a&gt; than the source, specifically targetting commercial use.&lt;&#x2F;li&gt;
&lt;li&gt;Commercial license fees for the previously free products have been updated on their &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.chef.io&#x2F;pricing&quot;&gt;pricing page&lt;&#x2F;a&gt; (more on pricing change below)&lt;&#x2F;li&gt;
&lt;li&gt;Change in licensing is inspired by RedHat&#x27;s business model&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;A quick list of reported exceptions to the license requirements for binaries, the details of which are to be reported by Chef at some undisclosed, later date:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Personal use&lt;&#x2F;li&gt;
&lt;li&gt;Experimentation for proving viability in a commercial setting (a.k.a., proof-of-concept work)&lt;&#x2F;li&gt;
&lt;li&gt;Non-profit organizations&lt;&#x2F;li&gt;
&lt;li&gt;Open source contributors to the products that meet certain criteria&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Starting with the following versions, the new EULA for this commercial license will be included:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Chef Habitat — (version not yet disclosed)&lt;&#x2F;li&gt;
&lt;li&gt;Chef Infra — v15.0&lt;&#x2F;li&gt;
&lt;li&gt;Chef InSpec — v4.0&lt;&#x2F;li&gt;
&lt;li&gt;Chef Automate — (first publicly available version)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Development of the products is still to take place in the open. In fact, Adam Jacob made a call to the open source community to &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@adamhjk&#x2F;goodbye-open-core-good-riddance-to-bad-rubbish-ae3355316494&quot;&gt;continue to collaborate&lt;&#x2F;a&gt; on the products and take advantage of the fact that it is still open source. He also made generalizations that this is entirely intentional so Chef can rely less on the &quot;heroes&quot; and &quot;martyrs&quot; who work on the previously closed source applications, inevitably burning themselves out. Instead, that burden can shift to anyone from the community who wishes to undertake contributing to the previously proprietary software in as big or small of a way as they wish.&lt;&#x2F;p&gt;
&lt;p&gt;It is important to emphasize that the &lt;em&gt;source code&lt;&#x2F;em&gt; will be under Apache 2.0 license and the &quot;&lt;em&gt;binaries&lt;&#x2F;em&gt;&quot; (a term Chef has chosen to indicate the result of packaging with Omnibus and Habitat) will have the new commercial EULA.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, it&#x27;s as if Chef is saying &quot;You can either pay us for this PPA, Yum repo, etc. that makes it super easy to install and update our software, or you can take this tar.gz file and compile the same software yourself with quite a bit of trouble for free.&quot;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;price-increase&quot;&gt;Price increase&lt;a class=&quot;zola-anchor&quot; href=&quot;#price-increase&quot; aria-label=&quot;Anchor link for: price-increase&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Per the previous licensing change, several products have since changed from their $0 price tag to... well, other non-zero prices. While it&#x27;s interesting how they&#x27;ve decided to measure units for increasing the price tag (e.g., InSpec is based on number of scan targets), the most interesting part here is for existing customers.&lt;&#x2F;p&gt;
&lt;p&gt;The price tag for Chef Automate seems to have increased from $75&#x2F;node previously to $150&#x2F;node currently, according to some community members. That increase is difficult to verify since the pricing is no longer just for Chef Automate, but rather the entire Enterprise Automation stack. (Did I tell you their organizing their products into bundles?)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;a class=&quot;zola-anchor&quot; href=&quot;#conclusion&quot; aria-label=&quot;Anchor link for: conclusion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;What does this mean? I&#x27;ll tell you later, in a separate post. Right now I&#x27;m going to keep this page updated with as many facts as I can find as the story unfolds.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>InSpec: You&#x27;re Doing It Wrong</title>
        <published>2019-03-05T23:10:31-05:00</published>
        <updated>2019-03-05T23:10:31-05:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/inspec-doing-it-wrong/"/>
        <id>https://www.thelonelyghost.com/posts/inspec-doing-it-wrong/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/inspec-doing-it-wrong/">&lt;p&gt;I&#x27;ve been using &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;community.chef.io&#x2F;tools&#x2F;chef-inspec&quot;&gt;InSpec&lt;&#x2F;a&gt; for over 2 years now and have been writing automated tests for longer than that. What&#x27;s the one thing that I&#x27;ve seen trip up the most people? Useless tests.&lt;&#x2F;p&gt;
&lt;p&gt;InSpec as a project is based off of RSpec, a behavior driven testing framework. That isn&#x27;t to say Gherkin like &quot;Given &lt;em&gt;X&lt;&#x2F;em&gt;, When &lt;em&gt;Y&lt;&#x2F;em&gt;, I should see &lt;em&gt;Z&lt;&#x2F;em&gt;&quot;, but rather a more generic definition. RSpec promotes writing tests in a feature-oriented manner:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;MyClass&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;can instantiate with no arguments&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    expect {&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8BE9FD;font-style: italic;&quot;&gt; MyClass&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;new&lt;&#x2F;span&gt;&lt;span&gt; }.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;not_to&lt;&#x2F;span&gt;&lt;span&gt; raise_error&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;  end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;has an attribute for its name&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    obj&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8BE9FD;font-style: italic;&quot;&gt; MyClass&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;new&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;    expect&lt;&#x2F;span&gt;&lt;span&gt;(obj.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;to&lt;&#x2F;span&gt;&lt;span&gt; eq &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;MyClass&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;  end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The net benefit with RSpec comes from the reporting format:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ✅ MyClass can instantiate with no arguments&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; ⛔ MyClass has an attribute for its name&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       expected: &amp;#39;MyClass&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       actual: &amp;#39;Lol, just kidding&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Instead of testing each unit like &lt;code&gt;1 + 2 = 3&lt;&#x2F;code&gt;, we can have a very tightly scoped integration test that gives us enough info to figure out &lt;em&gt;that&lt;&#x2F;em&gt; something is wrong. The frist battle is figuring out &lt;em&gt;that&lt;&#x2F;em&gt; something is wrong, then you need to know &lt;em&gt;how&lt;&#x2F;em&gt; it&#x27;s wrong.&lt;&#x2F;p&gt;
&lt;p&gt;Back to the core of the matter, what do I mean by useless tests? As stated earlier, &lt;code&gt;1 + 2 = 3&lt;&#x2F;code&gt; sorts of tests are useless. We don&#x27;t care about the unit as much as we care about the gestalt. It doesn&#x27;t matter if the calculator widget works flawlessly if the entire page doesn&#x27;t render!&lt;&#x2F;p&gt;
&lt;p&gt;What if, instead of &lt;code&gt;1 + 2 = 3&lt;&#x2F;code&gt;, we tested some small specific things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Does the login page present a button to login?&lt;&#x2F;li&gt;
&lt;li&gt;When I send invalid credentials, does the backend refuse to authorize me to act as the given user?&lt;&#x2F;li&gt;
&lt;li&gt;When I send correct credentials, does the backend allow me to act as the given user?&lt;&#x2F;li&gt;
&lt;li&gt;Are there any links on any of the pages that result in a 404 or 500 HTTP status code?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Any of these tests would be relatively low effort to implement, but provide much better feedback as to the overall state of the web application. Just like these real life examples from my career as a web developer writing automated tests, the same principals apply to testing infrastructure: we should test the gestalt. Let&#x27;s consider something basic.&lt;&#x2F;p&gt;
&lt;p&gt;I want to confirm that my web service is operating correctly on my EC2 instance in AWS. How might I be doing that currently?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;service&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;httpd&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should be_running }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;service&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;mariadb&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should be_running }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;80&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should be_listening }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;443&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should be_listening }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;3306&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should be_listening }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# My application&amp;#39;s deployment artifacts exist in their proper locations&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&#x2F;var&#x2F;www&#x2F;public_html&#x2F;index.php&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# I have an Apache configuration entry for my service&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&#x2F;etc&#x2F;httpd&#x2F;sites-available&#x2F;my-site.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# The prior Apache configuration is currently active&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&#x2F;etc&#x2F;httpd&#x2F;sites-enabled&#x2F;my-site.conf&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# I have the correct database credentials written to the necessary configuration files&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;file&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&#x2F;var&#x2F;www&#x2F;config.php&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;  its&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;db_username=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8BE9FD;font-style: italic;&quot;&gt;Regexp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;escape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF5555;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;1337h4x0r&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF5555;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;  its&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;content&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;db_password=&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;#{&lt;&#x2F;span&gt;&lt;span style=&quot;color: #8BE9FD;font-style: italic;&quot;&gt;Regexp&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;escape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF5555;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;hunter2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF5555;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;&amp;quot;$&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This is the thought process one might go through. I&#x27;m not willing to go so far as to say these tests are useless, per se, but they certainly miss the forest for the trees if you still get an angry tweet from an end user including a picture of a severely broken page.&lt;&#x2F;p&gt;
&lt;p&gt;Because we&#x27;re Agile, Lean, or whatever other trendy buzz-words that can be thrown at the problem, we&#x27;re going to make small, incremental changes. To figure out what changes to make to fix a bug, I ask the following questions in the given order:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;How is it broken?&lt;&#x2F;li&gt;
&lt;li&gt;What indicates it is broken?&lt;&#x2F;li&gt;
&lt;li&gt;How can I automate checking if it is broken?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;The answers may surprise you. From that hypothetical tweet we just got, we might decide that a working definition for &quot;broken&quot; in this context means anything from &quot;I see a blank white screen when the page loads&quot; to &quot;Firefox warns me that something related to the SSL certificate is weird&quot;, or even &quot;I found the rainbow unicorn when I tried to load GitHub.com.&quot; How can we test these indicators, then?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;http&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;https:&#x2F;&#x2F;svc.example.com&#x2F;my-page&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;  its&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;body&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;You&amp;#39;re visitor number &lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;\d&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;+ today&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;ssl&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;port&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; 443&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;protocols&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;ssl3&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should_not be_enabled }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Okay, pause. What functionality does that first test actually check?&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The httpd service is running (or else would have I&#x2F;O failure establishing connection)&lt;&#x2F;li&gt;
&lt;li&gt;The machine is serving HTTP requests over port 443 (or else would have I&#x2F;O failure establishing connection)&lt;&#x2F;li&gt;
&lt;li&gt;We have TLS certs installed to the proper spot (or else would have a TLS failure)&lt;&#x2F;li&gt;
&lt;li&gt;There aren&#x27;t any syntax or compile errors in the server-side code (or else would be HTTP status code 500)&lt;&#x2F;li&gt;
&lt;li&gt;The httpd service is configured to serve requests according to the server-side code you deployed (instead of the default Hello World site or a completely different project)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Assuming the content you&#x27;re checking on the page is derived from data gathered from the database (e.g., &quot;You&#x27;re the 394th visitor today!&quot;), this might also check the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Our connection to the database is allowed through the firewall&lt;&#x2F;li&gt;
&lt;li&gt;Our database credentials are up to date&lt;&#x2F;li&gt;
&lt;li&gt;The database schema is setup in the expected format (at least in part)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Is this an all-encompassing test? No, but it does test a helluva lot of moving parts and, if any one of them is broken, we&#x27;ll know because the test will fail. That&#x27;s one useful canary in this here coal mine!&lt;&#x2F;p&gt;
&lt;p&gt;Now, we find that the test is failing. What now? Here&#x27;s where the initial set of tests might help. They check the known hiding spots for some of these types of errors. Maybe the source of the problem is in one of these spots, maybe not. At least we can go through by process of elimination. Even better if the tests were already written too!&lt;&#x2F;p&gt;
&lt;p&gt;The moral of this story is that the initial tests on the application server aren&#x27;t wrong, but they don&#x27;t have a broad enough scope to determine &lt;em&gt;if&lt;&#x2F;em&gt; there&#x27;s a problem. You want to be hyper sensitive to &lt;em&gt;if&lt;&#x2F;em&gt; there&#x27;s a problem, then hone in &lt;em&gt;when&lt;&#x2F;em&gt; there&#x27;s a problem using investigative tests, helping us diagnose the problem if the canary test is failing.&lt;&#x2F;p&gt;
&lt;p&gt;There are any number of known or unknown reasons why a problem may exist. The most effective way to be successful is to know as early as possible &lt;em&gt;that&lt;&#x2F;em&gt; a problem exists so you can start looking for the source of it.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;Let&#x27;s examine one more concept, best embodied by InSpec&#x27;s cloud provider resources.&lt;&#x2F;p&gt;
&lt;p&gt;Why would you need to test that a cloud provider&#x27;s setting has a certain value? Are you testing that Terraform did its job? Are you testing that a bit of automation for building up and tearing down cloud resources is working? Great, but something tells me you&#x27;re not telling the whole story.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the following InSpec tests:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ec2_sg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;sg-asdlkjasdflkj321iu21340&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt; # some security group id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rds_sg&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;sg-23oiwqoj23lqwedasdlkj2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt; # some other security group id&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;aws_ec2_instance&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;name&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;web-server&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;  its&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;security_group_ids&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;include&lt;&#x2F;span&gt;&lt;span&gt; ec2_sg }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;aws_security_group&lt;&#x2F;span&gt;&lt;span&gt;(rds_sg)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should exist }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;allow_in&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;to_port&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; 3306&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; security_group&lt;&#x2F;span&gt;&lt;span&gt;: ec2_sg,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; protocol&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;tcp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;allow_in&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt;to_port&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; 3306&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; security_group&lt;&#x2F;span&gt;&lt;span&gt;: ec2_sg,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; protocol&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;udp&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In case you aren&#x27;t fluent in AWS jargon, this means my virtual machine running my web service is whitelisted in the firewall to allow communication with my database over TCP or UDP on the database&#x27;s port 3306.&lt;&#x2F;p&gt;
&lt;p&gt;Instead of testing that the firewall rule, what if we just tested that we&#x27;re functionally able to connect from one host to the other?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;db_username&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;root&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;db_password&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;hunter2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;db_host&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;mariadb.example.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;db_port&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; 3306&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sql&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt; mysql_session&lt;&#x2F;span&gt;&lt;span&gt;(db_username, db_password, db_host, db_port)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;describe sql.&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;query&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;SELECT 1;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should_not &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;Can&amp;#39;t connect to .* MySQL server&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;i) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  it { should_not &lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;match&lt;&#x2F;span&gt;&lt;span&gt;(&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;^error &lt;&#x2F;span&gt;&lt;span&gt;&#x2F;i) }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;What does this change really accomplish?&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s say some poor engineer working for Amazon pushes some bad code. This bad code disregards your settings and changes the actual port for a few RDS instances to be listening from 9999 instead. What happens with our tests from earlier? All green! Why? Because we&#x27;re testing the settings, not the functionality.&lt;&#x2F;p&gt;
&lt;p&gt;The same goes for if AWS has an unexpected outage. Why is my web service suddenly failing, despite all of my infrastructure being configured correctly? Oh because I&#x27;m only checking that I&#x27;ve told it the right thing, not that it&#x27;s doing the right thing.&lt;&#x2F;p&gt;
&lt;p&gt;Not to rip on cloud providers, but the same concept is found in testing, say, an IOS router by Cisco. I might write a test that confirms its config commands output something in particular. Is checking the output helpful? Maybe, but we&#x27;re not actually testing that packets get routed to their proper destination.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s focus on the end goal instead of being so myopic to how we arrive at that goal.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Blogging made easy</title>
        <published>2018-01-28T23:10:31-05:00</published>
        <updated>2018-01-28T23:10:31-05:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/blogging-made-easy/"/>
        <id>https://www.thelonelyghost.com/posts/blogging-made-easy/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/blogging-made-easy/">&lt;p&gt;Since starting this blog, I&#x27;ve encountered any number of topics where I&#x27;ve literally said, &quot;Maybe I&#x27;ll write a blog post about that...&quot; The biggest hinderance has always been thus:&lt;&#x2F;p&gt;
&lt;p&gt;When I&#x27;m at my computer, I want to code. When I&#x27;m away from my computer, I&#x27;m typically on my phone scrolling through twitter or reddit, wishing I would blog more like the posts I end up reading.&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;netlify&quot;&gt;Netlify&lt;a class=&quot;zola-anchor&quot; href=&quot;#netlify&quot; aria-label=&quot;Anchor link for: netlify&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I typically use Netlify for deploying any static sites these days, if only because I can easily hook it up to github and auto-deploy using more than just Jekyll, even going so far as to have a custom domain with LetsEncrypt TLS certs. &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.grafeas.org&#x2F;&quot;&gt;And I have done that before&lt;&#x2F;a&gt;. It only makes sense that I would investigate what else they have to offer.&lt;&#x2F;p&gt;
&lt;p&gt;Netlify offers a &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;decapcms.org&#x2F;&quot;&gt;full-on CMS&lt;&#x2F;a&gt; in the form of for managing your static site. It claims to work with any static site generator with a little configuration and requires the use of Github for hosting the source code of the site.&lt;&#x2F;p&gt;
&lt;p&gt;Behind the scenes, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;decapcms.org&#x2F;&quot;&gt;Netlify CMS&lt;&#x2F;a&gt; creates a new branch off of &lt;code&gt;master&lt;&#x2F;code&gt; or &lt;code&gt;gh-pages&lt;&#x2F;code&gt; (whichever you configure is the main site) and creates a new commit on that branch every time you save with its built-in editor. It opens a pull request so it might be picked up later by the CMS. It does this all through the Github API.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-issues-with-netify-cms&quot;&gt;My issues with Netify CMS&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-issues-with-netify-cms&quot; aria-label=&quot;Anchor link for: my-issues-with-netify-cms&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE:&lt;&#x2F;strong&gt; Netlify CMS was renamed to Decap CMS in February 2023. (&lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.netlify.com&#x2F;blog&#x2F;netlify-cms-to-become-decap-cms&#x2F;&quot;&gt;announcement&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;First off, the drafts of your blog posts will always be publicly available. Something so public so early on is not conducive to stress-free brainstorming or just getting started with writing. I get it though, it&#x27;s super intelligent to store the data on Github&#x27;s side instead of committing to any data storage by Netlify. It also makes it so you have one less dependency for storing drafts. This is a trade-off they have clearly considered and I just have to deal with the consequences.&lt;&#x2F;p&gt;
&lt;p&gt;My second issue is that, when I last tinkered with it, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;decapcms.org&#x2F;&quot;&gt;Netlify CMS&lt;&#x2F;a&gt; had some show-stopping issues in integrating it with a Middleman site. The biggest one had to do with file extensions not being honored in the expected manner. It seems Jekyll was the favored child.&lt;&#x2F;p&gt;
&lt;p&gt;Thirdly, if you prefer Gitlab for source code storage, you&#x27;re SOL--and reasonably so, given Gitlab was never intended as a drop-in replacement for Github. Again, this is an obvious trade-off that has been considered already and means I fall outside of the target audience.&lt;&#x2F;p&gt;
&lt;p&gt;There is nothing inherently wrong with &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;decapcms.org&#x2F;&quot;&gt;Netlify CMS&lt;&#x2F;a&gt;, in fact I applaud its existence and would love to see it grow. It just isn&#x27;t the right tool for my situation.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;evernote&quot;&gt;Evernote&lt;a class=&quot;zola-anchor&quot; href=&quot;#evernote&quot; aria-label=&quot;Anchor link for: evernote&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;After Netlify&#x27;s solution not working out, I considered using a proprietary option that I already use to store a metric crap-ton of my data: &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt;. I have a mobile app for it, it has an unofficial Linux client called &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;web.archive.org&#x2F;web&#x2F;20230412061849&#x2F;http:&#x2F;&#x2F;www.nixnote.org&#x2F;&quot;&gt;Nixnote&lt;&#x2F;a&gt; (formerly Nevernote), and I&#x27;m already paying for a premium account. Why not?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-issues-with-evernote&quot;&gt;My issues with Evernote&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-issues-with-evernote&quot; aria-label=&quot;Anchor link for: my-issues-with-evernote&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Okay so &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt; has a much nicer solution than Netlify&#x27;s web interface that may or may not work based on my internet connectivity. I can just pop open my Blog Posts notebook and go to town, right?&lt;&#x2F;p&gt;
&lt;p&gt;That works up until it comes to transposing them into Jekyll. &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt; does the &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;WYSIWYG&quot;&gt;WYSIWYG&lt;&#x2F;a&gt; approach, which is great when composing but not too great when copying it into a markup language like Markdown or even HTML. Underneath the covers, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt; notes use a form of XML with a proprietary schema for certain things like TODOs and inter-note linking. This means building a tool to work with &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt;’s APIs is that much more difficult and I would have to build Markdown pages from XML. No way.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s the other option? Well, I would have to rewrite every single blog post by hand, reading from &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;www.evernote.com&#x2F;&quot;&gt;Evernote&lt;&#x2F;a&gt; and writing into vim in Markdown. The only way I&#x27;d be able to preview it with any certainty is to run the Jekyll server locally.&lt;&#x2F;p&gt;
&lt;p&gt;Writing using a &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;WYSIWYG&quot;&gt;WYSIWYG&lt;&#x2F;a&gt; editor is enough of a hinderance, but having to transpose manually afterwards? That&#x27;s a hard no. Too much overhead to write a simple blog post.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simplenote&quot;&gt;SimpleNote&lt;a class=&quot;zola-anchor&quot; href=&quot;#simplenote&quot; aria-label=&quot;Anchor link for: simplenote&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This brings me to a solution made by WordPress&#x27; parent company, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;automattic.com&#x2F;&quot;&gt;Automattic&lt;&#x2F;a&gt;. Yes, the company behind the blogging engine I set out to avoid at all costs could actually help me with blogging. Automattic &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;techcrunch.com&#x2F;2013&#x2F;01&#x2F;24&#x2F;wordpress-simperium-simplenote&#x2F;&quot;&gt;bought the company&lt;&#x2F;a&gt; behind a note syncing application called &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;&quot;&gt;SimpleNote&lt;&#x2F;a&gt;, with renowned client applications such as &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Notational_Velocity&quot;&gt;Notational Velocity&lt;&#x2F;a&gt; and the fork, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;brettterpstra.com&#x2F;projects&#x2F;nvalt&#x2F;&quot;&gt;nvAlt&lt;&#x2F;a&gt;. Before acquisition, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;&quot;&gt;SimpleNote&lt;&#x2F;a&gt; had been touted as a no-frills note-taking application with &lt;em&gt;incredibly fast&lt;&#x2F;em&gt;, near-instant syncing of plaintext notes to Dropbox and other select cloud storage providers. Various clients, such as &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;brettterpstra.com&#x2F;projects&#x2F;nvalt&#x2F;&quot;&gt;nvAlt&lt;&#x2F;a&gt;, supported previewing of markdown in its richer formatting.&lt;&#x2F;p&gt;
&lt;p&gt;By the time I got to &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;&quot;&gt;SimpleNote&lt;&#x2F;a&gt;, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;automattic.com&#x2F;&quot;&gt;Automattic&lt;&#x2F;a&gt; had long-since bought it and made its own improvements. It removed the ability to send to Dropbox (et. al) and, for a time, broke compatibility with some of the third party clients. What it brought to the table were:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;2014&#x2F;02&#x2F;24&#x2F;syncing-improvements-features&#x2F;&quot;&gt;&lt;em&gt;even&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;2013&#x2F;08&#x2F;22&#x2F;simplenote-relaunch&#x2F;&quot;&gt;&lt;em&gt;faster&lt;&#x2F;em&gt;&lt;&#x2F;a&gt; syncing&lt;&#x2F;li&gt;
&lt;li&gt;creating native clients for Linux, Windows, and Android&lt;&#x2F;li&gt;
&lt;li&gt;adding a web app option&lt;&#x2F;li&gt;
&lt;li&gt;making all of the official clients open source&lt;&#x2F;li&gt;
&lt;li&gt;nixing all paid plans&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;my-issues-with-simplenote&quot;&gt;My issues with SimpleNote&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-issues-with-simplenote&quot; aria-label=&quot;Anchor link for: my-issues-with-simplenote&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;As stated previously, it almost feels like I&#x27;m working with WordPress again. it doesn&#x27;t (yet) allow me to link to other notes and transpose them to another platform. It doesn&#x27;t allow me to organize my notes into ones that are on a particular topic or ready to publish. It also doesn&#x27;t let me see how long-winded I am in rendered markdown while also editing it.&lt;&#x2F;p&gt;
&lt;p&gt;Honestly, that’s about it. &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;simplenote.com&#x2F;&quot;&gt;SimpleNote&lt;&#x2F;a&gt; allows me to seamlessly write from my desktop, my phone, or some random computer with an internet connection and a web browser.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;notion&quot;&gt;Notion&lt;a class=&quot;zola-anchor&quot; href=&quot;#notion&quot; aria-label=&quot;Anchor link for: notion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A proprietary platform, &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;notion.so&quot;&gt;Notion&lt;&#x2F;a&gt; allows me to compose my thoughts in a knowledgbase that already is built for organization, displaying data in a way that works with your brain, and optimized for entirely keyboard-driven operation. Until 2022 it did not have a public API upon which to build stable integrations. Now it does. Now others have built them to extract the WYSIWYG elements they call &quot;blocks&quot; and transform them into markdown with good accuracy. With a bit more time and effort, the translation of Notion blocks to markdown could improve to handle Hugo&#x27;s shortcodes, which help with custom layouts of content.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-issues-with-notion&quot;&gt;My issues with Notion&lt;a class=&quot;zola-anchor&quot; href=&quot;#my-issues-with-notion&quot; aria-label=&quot;Anchor link for: my-issues-with-notion&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;It is a blackbox solution which seems to structure the stored data based on position in the Notion application. Any dynamic field is evaluated by the client instead of the server, which means they&#x27;ve written a language which is used to evaluate those dynamic fields into static values. Any third party converter would need to reimplement the underlying language, without a spec, on a different platform.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>What is a Symbol?</title>
        <published>2017-10-09T23:02:00-04:00</published>
        <updated>2017-10-09T23:02:00-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/what-is-a-symbol/"/>
        <id>https://www.thelonelyghost.com/posts/what-is-a-symbol/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/what-is-a-symbol/">&lt;p&gt;As I was browsing Twitter this evening, I came across a peculiar tweet:&lt;&#x2F;p&gt;
&lt;figure class=&quot;quote&quot;&gt;
    &lt;blockquote url=&quot;https:&#x2F;&#x2F;x.com&#x2F;searls&#x2F;status&#x2F;917392217140072449&quot;&gt;
        &lt;p&gt;Ruby friends who&#x27;ve been writing Ruby for ~5 years or less: what sort of things did you find surprising and need someone to explain to you?&lt;&#x2F;p&gt;
    &lt;&#x2F;blockquote&gt;
    &lt;figcaption&gt;
        &lt;cite&gt;Tweeted &lt;a rel=&quot;nofollow noopener&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;searls&#x2F;status&#x2F;917392217140072449&quot;&gt;&lt;time class=&quot;relative&quot; datetime=&quot;2017-10-09T10:12:00-04:00&quot;&gt;10:12am - 9 Oct 2017&lt;&#x2F;time&gt;&lt;&#x2F;a&gt; by Justin Searls (&lt;a rel=&quot;nofollow noopener&quot; href=&quot;https:&#x2F;&#x2F;x.com&#x2F;searls&quot;&gt;@searls&lt;&#x2F;a&gt;)&lt;&#x2F;cite&gt;
    &lt;&#x2F;figcaption&gt;
&lt;&#x2F;figure&gt;
&lt;p&gt;The most common concept that remained confusing was symbols, so here’s my explanation of it. To explain symbols, I must first explain datatypes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;datatypes&quot;&gt;Datatypes&lt;a class=&quot;zola-anchor&quot; href=&quot;#datatypes&quot; aria-label=&quot;Anchor link for: datatypes&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Do you know the difference between datatypes?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;primitives&quot;&gt;Primitives&lt;a class=&quot;zola-anchor&quot; href=&quot;#primitives&quot; aria-label=&quot;Anchor link for: primitives&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;There’s a &lt;code&gt;string&lt;&#x2F;code&gt; (e.g., &lt;code&gt;&quot;Lorem ipsum dolor&quot;&lt;&#x2F;code&gt;), which is a set of characters (&lt;code&gt;char&lt;&#x2F;code&gt;s). There are various numbers ranging from an integer (e.g., &lt;code&gt;1&lt;&#x2F;code&gt; but not &lt;code&gt;1.4&lt;&#x2F;code&gt;), a double (a really large integer), a float (e.g., &lt;code&gt;1.4&lt;&#x2F;code&gt; or &lt;code&gt;1.0&lt;&#x2F;code&gt;), and any of those can be signed (including negative numbers) or unsigned (absolute values, positive numbers only). There are super simple concepts like booleans, which can be exactly “yes” or “no”.&lt;&#x2F;p&gt;
&lt;p&gt;Why are there all these variations? Isn’t it all just text? Think about this:&lt;&#x2F;p&gt;
&lt;h3 id=&quot;binary&quot;&gt;Binary&lt;a class=&quot;zola-anchor&quot; href=&quot;#binary&quot; aria-label=&quot;Anchor link for: binary&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A computer thinks in binary, ones and zeros. Binary can represent any number, such as &lt;code&gt;000000&lt;&#x2F;code&gt; represents zero and &lt;code&gt;000001&lt;&#x2F;code&gt; represents one, as one might imagine. The tricky part is &lt;code&gt;000010&lt;&#x2F;code&gt; represents two. How’s that? Let’s continue.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;000011&lt;&#x2F;code&gt; → three&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;000100&lt;&#x2F;code&gt; → four&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;000101&lt;&#x2F;code&gt; → five&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;000110&lt;&#x2F;code&gt; → six&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;000111&lt;&#x2F;code&gt; → seven&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;001000&lt;&#x2F;code&gt; → eight&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Get the idea? If we continue with this pattern, we’ll see that we eventually hit &lt;code&gt;111111&lt;&#x2F;code&gt; (sixty-three), but that’s not the last number there exists, is it? We need another digit to hold a zero or one, and it represents all the way up to one-hundred twenty-seven. Wow, that’s quite a jump, right? How does all of this have anything to do with data types?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;datatypes-in-binary&quot;&gt;Datatypes in binary&lt;a class=&quot;zola-anchor&quot; href=&quot;#datatypes-in-binary&quot; aria-label=&quot;Anchor link for: datatypes-in-binary&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;A computer has to store a representation of data in memory. But wait, didn’t we just cover that a computer thinks in binary? Well that’s still true. We have to come up with a shorthand for storing things like &lt;code&gt;1.3&lt;&#x2F;code&gt; in binary.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s tear this apart. This is a float datatype. We have to come up with some shorthand for noting what is before the decimal point and what is after. How about we say the first four digits are the number before the decimal point, and the last four are the number after. We can extend this beyond eight digits later.&lt;&#x2F;p&gt;
&lt;p&gt;In this case the binary representation of &lt;code&gt;1.3&lt;&#x2F;code&gt; might be &lt;code&gt;00010011&lt;&#x2F;code&gt;. What’s the difference between this and the integer &lt;code&gt;19&lt;&#x2F;code&gt;? Both are represented by the same binary, right? We’ll need to come up with some universal shorthand to note the difference.&lt;&#x2F;p&gt;
&lt;p&gt;In an integer, we know the same value &lt;code&gt;010011&lt;&#x2F;code&gt; (19) might also be positive or negative. This is called a “signed” integer. Other numeric datatypes can be signed as well, but we’ll keep it simple with integers for now. We’ll keep our shorthand of splitting up the digits, but whether it’s positive or negative is just 2 choices. We can probably keep to the first digit where &lt;code&gt;1&lt;&#x2F;code&gt; means negative and &lt;code&gt;0&lt;&#x2F;code&gt; means positive. Therefore &lt;code&gt;19&lt;&#x2F;code&gt; means &lt;code&gt;0010011&lt;&#x2F;code&gt; and &lt;code&gt;-19&lt;&#x2F;code&gt; means &lt;code&gt;1010011&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;data-headers&quot;&gt;Data headers&lt;a class=&quot;zola-anchor&quot; href=&quot;#data-headers&quot; aria-label=&quot;Anchor link for: data-headers&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;We have all these shorthand tricks we have to remember, but at the end of the day it’s just 1’s and 0’s. How will we differentiate between an unsigned &lt;code&gt;19&lt;&#x2F;code&gt; and &lt;code&gt;1.3&lt;&#x2F;code&gt; from before? Let’s come up with one, universal shorthand to determine which data type we will be storing for the next few digits. Three digits should cover it for now. We’ll remember some datatypes by this mapping for now:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;001&lt;&#x2F;code&gt; → char&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;010&lt;&#x2F;code&gt; → unsigned integer&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;011&lt;&#x2F;code&gt; → signed integer&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;100&lt;&#x2F;code&gt; → float&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;110&lt;&#x2F;code&gt; → double&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;111&lt;&#x2F;code&gt; → boolean&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;To integrate this we’ll tack this onto the starting of each value and just remember to interpret the first 3 digits as the datatype indicators.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;data-maximums-minimums&quot;&gt;Data maximums&#x2F;minimums&lt;a class=&quot;zola-anchor&quot; href=&quot;#data-maximums-minimums&quot; aria-label=&quot;Anchor link for: data-maximums-minimums&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;To have all of these shorthands, we have to agree on the next &lt;em&gt;X&lt;&#x2F;em&gt; number of digits, which will signify the value of the datatype. A boolean, true and false, only needs one digit (plus the header) while an unsigned integer can only count between 0 and 127 with 3 digits, which might not be enough for fun things like counting the number of seats in a stadium.&lt;&#x2F;p&gt;
&lt;p&gt;We’ll come up with some arbitrary lengths of digits for these datatypes. Here are some examples:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0000000000&lt;&#x2F;code&gt; → char (10 digits)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;0000000000000&lt;&#x2F;code&gt; → float (13 digits)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;000000000&lt;&#x2F;code&gt; → unsigned integer (9 digits)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;0000000000&lt;&#x2F;code&gt; → signed integer (10 digits)&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;0&lt;&#x2F;code&gt; → boolean (1 digit)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;All together, a float will take up 13 digits for the value + 3 digits for the header indicating that it’s a float. 16 digits. If we take a multiple instances of data (datatype header + value in binary), we can string them together.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;1000000000010011&lt;&#x2F;code&gt; → float datatype of value &lt;code&gt;1.3&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;1110&lt;&#x2F;code&gt; → boolean datatype of value &lt;code&gt;false&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Let’s take these two example data instances and put them together like a computer might keep it in memory: &lt;code&gt;11101000000000010011&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;What?&lt;&#x2F;p&gt;
&lt;p&gt;Okay, let&#x27;s tear it apart again.&lt;&#x2F;p&gt;
&lt;p&gt;If we saw just this binary without any other context, we can remember our shorthand for the headers by reading left to right. The first 3 digits signify it’s a boolean, which we know has a value length of 1 digit. We read it as boolean &lt;code&gt;false&lt;&#x2F;code&gt;, and we’ve parsed the first 4 digits in the example data.&lt;&#x2F;p&gt;
&lt;p&gt;Since we&#x27;re done with the first 4, we&#x27;ll start at digit 5, follow along the same pattern with interpreting the first 3 digits as the datatype (&lt;code&gt;100&lt;&#x2F;code&gt; = float), and parse the next &lt;em&gt;X&lt;&#x2F;em&gt; digits (float value → 13 digits) as the value. The next 13 digits amount to &lt;code&gt;0000000010011&lt;&#x2F;code&gt;, which we’ll pretend like we already established a new shorthand where floats have the last 3 digits reserved for the decimal number. This makes it easier since we remember this was &lt;code&gt;1.3&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Doing this again with another example value: &lt;code&gt;01100000111010010001011100&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Reading the first 3 digits (&lt;code&gt;011&lt;&#x2F;code&gt;) we see it’s a signed integer. This means we read the next 10 digits (&lt;code&gt;0000011101&lt;&#x2F;code&gt;) as the value. Knowing our shorthand for signed integers, we recall the first digit of the value (&lt;code&gt;0&lt;&#x2F;code&gt;) tells us whether it’s positive or negative, and the remaining digits (&lt;code&gt;000011101&lt;&#x2F;code&gt;) are the number itself. We can then tell it’s &lt;code&gt;+29&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The next three digits after that’s done is &lt;code&gt;001&lt;&#x2F;code&gt;, which means a character. You know from before that a character datatype reserves the next 10 digits for its value, but we don’t know how to decode it. We haven’t established that shorthand. The important part here, though, is that we can differentiate between the different datatypes when they’re thrown together in one, long, unbreaking string of 1’s and 0’s.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;characters-versus-strings&quot;&gt;Characters versus Strings&lt;a class=&quot;zola-anchor&quot; href=&quot;#characters-versus-strings&quot; aria-label=&quot;Anchor link for: characters-versus-strings&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;In case you weren’t already aware, a character might represent any one key on your keyboard, including letters, numbers, punctuation (like &lt;code&gt;?&lt;&#x2F;code&gt; and &lt;code&gt;!&lt;&#x2F;code&gt;), or other bits of written language (like &lt;code&gt;{&lt;&#x2F;code&gt; or &lt;code&gt;)&lt;&#x2F;code&gt;). A character can make up individual letters you might not see on your keyboard, like “é” or “→”. The point is, a character can be numerous possible values taking up the same physical space on your screen as a 1 or 0.&lt;&#x2F;p&gt;
&lt;p&gt;Going back to what was said before, computers think in binary. We could come up with a way to store every character, mapping to a numerical value. These are called character encodings. Like other datatypes this tells us how many digits each character will take up. A character might be ASCII and be only what you might see on a QWERTY keyboard, or it might be UTF-8 and include emoji and other richer styles of a character. For the sake of simplicity, we’ll stick with ASCII.&lt;&#x2F;p&gt;
&lt;p&gt;There are 26 letters in the english alphabet, plus 11 special symbol keys (upper- and lowercase for each, so 22 characters), plus the number key line of 10 keys (so +20 characters), plus the spacebar, which brings us to 69 possible characters. We need enough binary digits, or bits, to handle a maximum number of 69. By my calculations, that’s 7 bits. Not too far off from our 10 bits we reserved earlier for holding character data, right?&lt;&#x2F;p&gt;
&lt;p&gt;To make a string like &lt;code&gt;&quot;Hello world!&quot;&lt;&#x2F;code&gt;, we need to disect it. It’s the character &lt;code&gt;H&lt;&#x2F;code&gt;, then the character &lt;code&gt;e&lt;&#x2F;code&gt;, then the character &lt;code&gt;l&lt;&#x2F;code&gt;, and so on for the word “Hello”.&lt;&#x2F;p&gt;
&lt;p&gt;Wait. We have capital letters here too?! Shoot. Let’s amend our count of 69 characters to add in 26 more, 1 more for each letter of the alphabet in its capital form. That’s a total of 95 possible characters. Good thing our 7 bits can store a representation of any number between 0 and 127.&lt;&#x2F;p&gt;
&lt;p&gt;So we have “Hello” and “world!” separated by a space character, which amounts to 12 characters. Since each character takes up 10 bits (3 header + 7 value), we’re looking at 120 bits to render &lt;code&gt;&quot;Hello world!&quot;&lt;&#x2F;code&gt; as binary data. Here’s where I’m going to stop while you to ponder that for a minute.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;symbol-datatype&quot;&gt;Symbol datatype&lt;a class=&quot;zola-anchor&quot; href=&quot;#symbol-datatype&quot; aria-label=&quot;Anchor link for: symbol-datatype&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Let’s assume you have a mapping of characters to bit values, and the maximum bits mapped out perfectly. You have that string that takes up 12 characters for 120 bits just to say hello to the world. What if there’s something you only want to reference internally for your own purposes? We don’t care about the actual value, we just care that the value happens to be unique from any other value. It has semantic meaning only. You know how we have those mappings of letters, base10 numbers, etc. to binary? Those are the same concept of a symbol. That’s part of the &lt;code&gt;char&lt;&#x2F;code&gt; shorthand. What if we took it a step further?&lt;&#x2F;p&gt;
&lt;p&gt;First, after all that binary-talk, I need to take my head out of the theoretical for a moment. I need to go back to the programming I know: Ruby, Python, etc.&lt;&#x2F;p&gt;
&lt;p&gt;Ruby and Python are dynamic languages where you rarely have to know how many bits in memory a variable, holding a particular datatype, will take up. The way I operate, I care about how easily I can keep the inner workings of a program in my head at any one time. I need cues that won’t matter to the computer, but do only to me as the programmer. Take a logging class (in most languages), for example.&lt;&#x2F;p&gt;
&lt;p&gt;We have the concept of a logger, which takes an enum of various error levels: &lt;code&gt;ERROR&lt;&#x2F;code&gt;, &lt;code&gt;WARN&lt;&#x2F;code&gt;, &lt;code&gt;INFO&lt;&#x2F;code&gt;, &lt;code&gt;DEBUG&lt;&#x2F;code&gt;, and possibly more. Do we care about storing the string of characters to represent it? No. We just need some internal representation to reference. Let’s choose a datatype that has a really small memory footprint here.&lt;&#x2F;p&gt;
&lt;p&gt;Representing &lt;code&gt;&quot;ERROR&quot;&lt;&#x2F;code&gt; (as a string) would be 5 characters × 10 bits per character = 50 bits. Other logging levels might be greater or fewer characters in number, so we just need to have a datatype less than 50 bits, or binary digits long to stand in for the one other, more memory intensive value. Let’s choose the unsigned integer &lt;code&gt;4&lt;&#x2F;code&gt;, and the other logging levels as unsigned integers as well. We don’t care about the value &lt;code&gt;4&lt;&#x2F;code&gt;, only what it represents, so it could just as easily be &lt;code&gt;986&lt;&#x2F;code&gt; or a poop emoji. It is meant to differentiate &lt;code&gt;ERROR&lt;&#x2F;code&gt; from &lt;code&gt;WARN&lt;&#x2F;code&gt; and the others.&lt;&#x2F;p&gt;
&lt;p&gt;So the unsigned integer &lt;code&gt;4&lt;&#x2F;code&gt; is represented with &lt;code&gt;010000000100&lt;&#x2F;code&gt; in binary. That’s 12 bits long when the string version could be 50 bits. That’s quite the savings! Only 25% of the original memory footprint!&lt;&#x2F;p&gt;
&lt;p&gt;In a language like ruby, a small savings like that might not make too big of a difference, but say it could encode it in binary instead of unsigned integer, with all the wasted space up front reclaimed. We only need the first 3 bits for the header and the 3 bits following for the value of &lt;code&gt;4&lt;&#x2F;code&gt;. What if our compiler was smart enough to see we only needed a maximum value of &lt;code&gt;4&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;In that case, we could remember a new datatype that says the following &lt;em&gt;X&lt;&#x2F;em&gt; digits represent a symbol, where &lt;em&gt;X&lt;&#x2F;em&gt; is determined once the entire program is analyzed to figure out the max value one might ever see. In our case it’s 4, so we downsize the number of bits used from 12 to 6. What once was &lt;code&gt;010000000100&lt;&#x2F;code&gt; is now &lt;code&gt;010100&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;But wait, we still have the datatype header for an unsigned integer! That’ll screw everything up! That’s very true. We can&#x27;t keep breaking our own shorthand conventions with encoding&#x2F;decode binary, so we’ll have to come up with a new, on-the-fly datatype, which we’ll refer to as a symbol. It’ll go by the header &lt;code&gt;101&lt;&#x2F;code&gt;, since it hasn’t been used yet.&lt;&#x2F;p&gt;
&lt;p&gt;From the original 50 bits, to &lt;code&gt;010000000100&lt;&#x2F;code&gt; (12 bits), to &lt;code&gt;101100&lt;&#x2F;code&gt; (6 bits), that’s quite a bit of downsizing for the same functionality; the same functionality from the programmer&#x27;s and the end user&#x27;s perspective.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;recap&quot;&gt;Recap&lt;a class=&quot;zola-anchor&quot; href=&quot;#recap&quot; aria-label=&quot;Anchor link for: recap&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Symbols are just what they sound like: an in-memory stand-in value for something else. Their function is to save on memory usage when you don’t need to allocate a ton of bits to have a label that only needs to represent that it&#x27;s different from other labels. The values don&#x27;t actually matter. They are a handy representation, shifting work onto the compiler initially, but nets less memory (and equal computation power) at runtime.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;moving-forward&quot;&gt;Moving forward&lt;a class=&quot;zola-anchor&quot; href=&quot;#moving-forward&quot; aria-label=&quot;Anchor link for: moving-forward&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;These optimizations occur not only for storing data, but referencing binary chunk of data representing logic the computer is supposed to follow. Do you really think your computer remembers to look in that file for the string &lt;code&gt;my_main_function()&lt;&#x2F;code&gt; in order call the logic defined therein? Do you feel the computer cares how you name things? No! It reads the logic into binary and determines a symbol only it remembers in order for it to easily call the functionality. These are more compilation optimizations that happen automatically in the programming languages you use.&lt;&#x2F;p&gt;
&lt;p&gt;Languages like C and Ruby allow you direct access to symbols as a datatype, but languages like PHP and earlier versions of Java require you to declare your preferred datatype and value, leaving the memory optimization to the programmer when defining that a symbol exists.&lt;&#x2F;p&gt;
&lt;p&gt;Are symbols helpful? Sure. How often? That depends on what and how you’re coding the task at hand. Hopefully this will serve as an introduction for what circumstances would be best to use symbols versus other datatypes.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Recruiter quality score</title>
        <published>2017-06-20T14:35:00-04:00</published>
        <updated>2017-06-20T14:35:00-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/recruiter-quality-score/"/>
        <id>https://www.thelonelyghost.com/posts/recruiter-quality-score/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/recruiter-quality-score/">&lt;p&gt;Although I know I&#x27;m incredibly fickle when it comes to who I&#x27;ll work with when it comes to recruiting, the fact of the matter is that I value honest effort over the type to throw bodies at the wall. The shotgun approach is just lazy and rarely pans out. Here&#x27;s my attempt at creating a rubric for my minimum barrier to responding.&lt;&#x2F;p&gt;
&lt;p&gt;Please understand that I am a privileged person. I know that. My reactions based on this rubric will be skewed toward my particular context.&lt;&#x2F;p&gt;
&lt;p&gt;This is coming from someone who:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;lives in a well-off area&lt;&#x2F;li&gt;
&lt;li&gt;can decline offers due to savings to fall back on&lt;&#x2F;li&gt;
&lt;li&gt;works in the software engineering industry with an in-demand language&lt;&#x2F;li&gt;
&lt;li&gt;does not have outside obligations, requiring a rigid work-life balance&lt;&#x2F;li&gt;
&lt;li&gt;has a reasonably up-to-date portfolio of work publicly available&lt;&#x2F;li&gt;
&lt;li&gt;speaks english as a first language&lt;&#x2F;li&gt;
&lt;li&gt;is a white male living in the United States&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;scoring-rubric&quot;&gt;Scoring rubric&lt;a class=&quot;zola-anchor&quot; href=&quot;#scoring-rubric&quot; aria-label=&quot;Anchor link for: scoring-rubric&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: center&quot;&gt;Points&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Description&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 7&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Emailed personal address&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 4&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Use exceedingly old resume (&amp;gt;1 year old, if newer one is published)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 6&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Content is overly wordy (not easy to skim)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Content is overly generalized to be part of a mass email&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Salutations are generalized for a mass email, e.g., &quot;applicant&quot; or &quot;developer&quot; (exception: blank &quot;Hey,\n&quot; or jumping straight to the body of the email is perfectly acceptable)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 50&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Mass email is sent using the TO or CC field&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 10&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Mass email is sent using the BCC field&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 6&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Name of the hiring company is excluded&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Location of the position is excluded&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 5&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No mention of if contract-to-hire, contract, or direct-hire&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 50&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Require me to fill out onboarding paperwork before position is filled by me&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;+ [10 to 30]&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Personalization of the email content to my personal situation (based on degree of personalization)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 30&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Unsubscribe link was followed and does not seem to do anything&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 30&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;No unsubscribe link and signs of a mass email&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;- 15&lt;sup&gt;&lt;em&gt;n&lt;&#x2F;em&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Disregard for direct instructions, e.g., &quot;Not interested&quot; or &quot;Do not contact me again&quot; (where &lt;em&gt;n&lt;&#x2F;em&gt; is the number of times it occurs)&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;+ [0 to 99]&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Exactness of fit of the job description to my skills&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;+ [0 to 30]&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Signs of remediation for prior score subtractions&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;+ 40&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Worked with this recruiter on prior occasion, resulting in at least 1 interview&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center&quot;&gt;+ 10&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Worked with this recruiting firm on prior occasion, with at least 1 submission&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;reaction-if-available-for-hire&quot;&gt;Reaction (if available for hire)&lt;a class=&quot;zola-anchor&quot; href=&quot;#reaction-if-available-for-hire&quot; aria-label=&quot;Anchor link for: reaction-if-available-for-hire&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Total score&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Response&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&amp;gt;80&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Tailor resume for position, immediately send a response that I&#x27;m interested and actively updating my resume for this position&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;40 to 79&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 2 hours with more specifically pertinent resume from archive&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;20 to 39&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 24 hours with more specifically pertinent resume from archive&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;10 to 19&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 36 hours with general resume&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0 to 9&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 3 days, asking questions about the position&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;-19 to 0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Ignore email&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;-80 to -20&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;(Probation) Monitor future email from that recruiting firm for future quality problems&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Below -80&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Filter future email from the recruiting firm to black hole&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h2 id=&quot;reaction-if-unavailable&quot;&gt;Reaction (if unavailable)&lt;a class=&quot;zola-anchor&quot; href=&quot;#reaction-if-unavailable&quot; aria-label=&quot;Anchor link for: reaction-if-unavailable&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;Total score&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: left&quot;&gt;Response&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;&amp;gt;80&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond within 24 hours with info that it is a good match but bad timing. Add to contacts as preferred recruiter. Refer out to colleague&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;40 to 79&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 36 hours with most up-to-date resume for their records, add to contacts as preferred recruiter&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;20 to 39&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Respond to email within 1 week with canned &quot;I&#x27;m not looking right now...&quot; response&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;-19 to 19&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Ignore email&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;-80 to -20&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;(Probation) Monitor future email from that recruiting firm for future quality problems&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;Below -80&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: left&quot;&gt;Filter future email from the recruiting firm to black hole&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Usernames, not Email Addresses</title>
        <published>2017-06-20T11:05:00-04:00</published>
        <updated>2017-06-20T11:05:00-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/usernames-not-email-addresses/"/>
        <id>https://www.thelonelyghost.com/posts/usernames-not-email-addresses/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/usernames-not-email-addresses/">&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;www.thelonelyghost.com&#x2F;posts&#x2F;usernames-not-email-addresses&#x2F;.&#x2F;recruiter-email.png&quot; alt=&quot;Email to old address from recruiter&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Yet another email from yet another recruiter, today. This is one more email sent to an address I haven&amp;#39;t used since 2014. If their sources of information really 3 years old, why are they pinging me about my work with Ruby on Rails? 3 years ago I wasn&amp;#39;t publicly posting any such interest online.&lt;&#x2F;p&gt;
&lt;p&gt;How are they getting this information? I&amp;#39;m not certain, but I suspect it&amp;#39;s scraping Github&amp;#39;s API. Either way it&amp;#39;s getting really fricking annoying. Let me tell you why:&lt;&#x2F;p&gt;
&lt;h2 id=&quot;&quot;&gt;Security&lt;a class=&quot;zola-anchor&quot; href=&quot;#&quot; aria-label=&quot;Anchor link for: &quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Too many websites out there, these days, accept email addresses as a person&amp;#39;s username. While this particular issue isn&amp;#39;t the fault of the recruiter sending this email, despite them more than likely pulling my email from GitHub &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;en&#x2F;github&#x2F;site-policy&#x2F;github-acceptable-use-policies#6-information-usage-restrictions&quot;&gt;against their terms of use&lt;&#x2F;a&gt;, it is the fault of many login systems.&lt;&#x2F;p&gt;
&lt;p&gt;Without multi-factor authentication enabled on all accounts with an email that &lt;em&gt;can&lt;&#x2F;em&gt; act as the username, a potential attacker already has one piece of the keys to your account. In most cases which involve a cooling-off period between failed login attempts, it&amp;#39;s just a waiting game for an attacker to brute-force your account password. If you work on an application with a login system like this, use whatever sway you can to enable a lockout system requiring password reset, not a cooling off period.&lt;&#x2F;p&gt;
&lt;p&gt;For an email account, requiring a password reset wouldn&amp;#39;t make sense. Where would you send the reset request, via email? No, in this case a cooling off period makes sense and a user should always, &lt;em&gt;always&lt;&#x2F;em&gt;, &lt;em&gt;ALWAYS&lt;&#x2F;em&gt; have some form of multifactor authentication enabled. For any other service, it doesn&amp;#39;t make sense.&lt;&#x2F;p&gt;
&lt;p&gt;As a programmer, I get it. Finding a unique, memorable key for someone to use as one half of their authentication mechanism makes sense. The username&#x2F;password approach is ingrained in the user experience for most internet goers. An efficiency gain is to use their email address as a string acting as their username, right? Then you can guarantee it&amp;#39;s unique (pending validation by clicking a confirmation link in a welcome email) and won&amp;#39;t have to validate uniqueness as a database constraint, right? Wrong.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, security aside. Let&amp;#39;s assume I have multi-factor authentication on everything that uses the concerned email address. What else might be a problem?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;-1&quot;&gt;Reply-all&lt;a class=&quot;zola-anchor&quot; href=&quot;#-1&quot; aria-label=&quot;Anchor link for: -1&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This email keeps coming. Notice the list of recipients? The fact that I can see them means the sender didn&amp;#39;t use the BCC field, as is convention. If you haven&amp;#39;t been part of a reply-all rage-fest, I do not recommend it. One recipient hits reply-all because maybe the opportunity interests them, maybe they&amp;#39;re being a good netizen and replying to every email inquiry they receive, or maybe they have a vacation responder set for &lt;em&gt;every&lt;&#x2F;em&gt; incoming email. If multiple vacation responders do what they&amp;#39;re intended? You have an exponentially growing chain of responses. Hopefully it&amp;#39;s just 2 auto-responders, in that event.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, no one has vacation responders setup to reply to anyone in this case. What&amp;#39;s wrong with the TO or CC field? Do you remember the Github API abuse mentioned earlier? Anyone else who received that email is able to pull in every other address on the list into their own, spammy sales pipeline. And the cycle continues...&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>On the Importance of Packaging</title>
        <published>2017-06-06T10:18:53-04:00</published>
        <updated>2017-06-06T10:18:53-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/importance-of-packaging/"/>
        <id>https://www.thelonelyghost.com/posts/importance-of-packaging/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/importance-of-packaging/">&lt;p&gt;You have an idea and want to turn it into a bit of code to carry it out. What do you do? You open up your IDE&#x2F;Editor, perhaps you structure it inside of a folder with some default tooling for linting and (if necessary) compiling, and you get the code to work. It&#x27;s hacky, it&#x27;s ugly, but it works.&lt;&#x2F;p&gt;
&lt;p&gt;Now that you have something, you want to add one more feature to it or work out a bug. What do you do? You follow the same process. If you&#x27;re feeling zesty, maybe you init a new git repository at the base of the project, but there&#x27;s no real point to branching and merging back into master. Backing up the code? Might be an account on Bitbucket, or maybe something with GitLab. They both have free usage tiers with private repositories, and this definitely isn&#x27;t good enough for the public to see.&lt;&#x2F;p&gt;
&lt;p&gt;This continues with more features hacked on, with no tests and zero documentation (except a comment here and there), all kept 100% private. Maybe you try to make it work on a Digital Ocean server you have stood up as a floating workspace with a persistent internet connection. It was difficult, but it works now. Kinda. Well, some of the time, anyway.&lt;&#x2F;p&gt;
&lt;p&gt;Your code portfolio is still missing a lot. Not a whole lot is visible because not a whole lot is in good enough condition to show anybody. You have a lot of these tiny projects that are riddled with what you know are bad practices, but were so much easier that figuring out the right way to do it. You treat your open source code contributions almost like a stage performance where you have everything perfectly thought out. First you have to write documentation, which means you have to clearly define that snowflake server you have setup to run your script. It also means you have to write usage documentation. Automated tests too, with varying importance depending on the community surrounding the language in which your project is implemented.&lt;&#x2F;p&gt;
&lt;p&gt;This has been my process for years with any number of once-off scripts. Projects that were intended for a recurring, minisculely scoped purpose I would have privately and, when I didn&#x27;t have need for them anymore or they broke due to changes elsewhere (e.g., web scrapers breaking because website updates), I would just delete the project and call it done. Of course I would only be able to verbally mention &quot;Yeah, I built something like that in my free time&quot; during job interviews, but wouldn&#x27;t be able to prove it because it was longer than a week ago and I didn&#x27;t save the work.&lt;&#x2F;p&gt;
&lt;p&gt;What I&#x27;ve found, recently, is that there is a way to ease these growing pains.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;packaging&quot;&gt;Packaging&lt;a class=&quot;zola-anchor&quot; href=&quot;#packaging&quot; aria-label=&quot;Anchor link for: packaging&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Every (legitimate) language these days has a package management strategy. Not familiar with the concept of package management? Let&#x27;s talk about that for a sec.&lt;&#x2F;p&gt;
&lt;p&gt;You have a new project, let&#x27;s say it&#x27;s written in Python for now. The standard package format is well defined for installing using &lt;code&gt;pip&lt;&#x2F;code&gt;. It includes dependencies, name of the package, author name and contact info, version constraints for dependencies... a lot of information that is--and should be--standardized. In python, that&#x27;s consistently defined in &lt;code&gt;setup.py&lt;&#x2F;code&gt; at the base of the repository.&lt;&#x2F;p&gt;
&lt;p&gt;Consider the following repository structure:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── LICENSE.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── README.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── setup.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── my_project&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── __init__.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── core&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── admin_commands.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── inbox.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── initialize.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── mentions.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── posts.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── user_interaction.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   └── validation.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── helpers&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   ├── misc.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    │   └── wiki.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ├── main.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    └── strings&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── debug.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── posts.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ├── responses.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        └── urls.py&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We have a project named &lt;code&gt;my_project&lt;&#x2F;code&gt;, which is should be the name of the package in &lt;code&gt;setup.py&lt;&#x2F;code&gt;. Python has the Java-esque convention of &lt;code&gt;import package.name.here&lt;&#x2F;code&gt; to map to &lt;code&gt;package&#x2F;name&#x2F;here.py&lt;&#x2F;code&gt; filesystem structure.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;readme&quot;&gt;Readme&lt;a class=&quot;zola-anchor&quot; href=&quot;#readme&quot; aria-label=&quot;Anchor link for: readme&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Packages always have a README. If you use a package generator (like &lt;code&gt;bundle gem&lt;&#x2F;code&gt;) and it generates a README, &lt;em&gt;always&lt;&#x2F;em&gt; remove the default generated description (and other TODO-lines) and insert your own. Not sure what your project is about? Note what it currently covers. You can update it later. That&#x27;s the point of version control.&lt;&#x2F;p&gt;
&lt;p&gt;In the README, include installation instructions that are realistic to your situation. Are there native OS dependencies that you need installed too? Those might not fit well in the &lt;code&gt;setup.py&lt;&#x2F;code&gt; or &lt;code&gt;*.gemspec&lt;&#x2F;code&gt; files. In that case, put it in the README.&lt;&#x2F;p&gt;
&lt;p&gt;A lot of times the boilerplate includes a lot of noise that might not be necessary for publicly publishing a package. Here are the bare minimum requirements for any package I make, public or private. I&#x27;m a forgetful person so coming back to a project 2 months later will require some getting up-to-speed again anyway. I like to make it easier for myself with this list of requirements for a README:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Usage&lt;&#x2F;li&gt;
&lt;li&gt;Installation procedure (assume a freshly installed OS)&lt;&#x2F;li&gt;
&lt;li&gt;Description&lt;&#x2F;li&gt;
&lt;li&gt;(if applicable) Assumptions of native platform dependencies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;dependencies&quot;&gt;Dependencies&lt;a class=&quot;zola-anchor&quot; href=&quot;#dependencies&quot; aria-label=&quot;Anchor link for: dependencies&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Packages provide a very clear definition of what it takes to run a piece of software that was written. I&#x27;ve been hearing for years that successful projects include a very narrowly defined scope and that the best way to do that is through narrowly defined interfaces.&lt;&#x2F;p&gt;
&lt;p&gt;In programming, you might see a junior dev hack away at a magic method that takes inputs in any variety of forms and is able to normalize it. Unless that is the primary function of the method&#x2F;command&#x2F;atomic unit they&#x27;re creating, more experienced developers realize this very easily snowballs into a maintenance nightmare. The same might be said for dependency tracking. Having a clearly defined set of required dependencies (instead of the end user doing trial and error to install everything) will always be better.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;overkill&quot;&gt;Overkill&lt;a class=&quot;zola-anchor&quot; href=&quot;#overkill&quot; aria-label=&quot;Anchor link for: overkill&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Python, for example, involves some bootstrapping to setup a package. For me, it has involved looking up prior projects&#x27; &lt;code&gt;setup.py&lt;&#x2F;code&gt; file, modifying it to fit the new project, and creating some default structures like &lt;code&gt;my_project&#x2F;__init__.py&lt;&#x2F;code&gt; containing variables for version (&lt;code&gt;__version__&lt;&#x2F;code&gt;) and author (&lt;code&gt;__author__&lt;&#x2F;code&gt;). It also involves setting up automated testing with unittest and shell scripts to make automated testing easy to execute.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my heuristic when it comes to creating a package:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Are there &lt;em&gt;any&lt;&#x2F;em&gt; dependencies that require a package manager (e.g., &lt;code&gt;pip&lt;&#x2F;code&gt;, &lt;code&gt;npm&lt;&#x2F;code&gt;, &lt;code&gt;gem&lt;&#x2F;code&gt;)?&lt;&#x2F;li&gt;
&lt;li&gt;Does the source code need to be split up into multiple files?&lt;&#x2F;li&gt;
&lt;li&gt;Am I testing my work with any more granularity than full, end-to-end tests?&lt;&#x2F;li&gt;
&lt;li&gt;Do I need to deploy this easily as, e.g., a command line tool?&lt;&#x2F;li&gt;
&lt;li&gt;Do I need to version the releases and have stable, beta, and dev copies of the source code?&lt;&#x2F;li&gt;
&lt;li&gt;Am I tracking code changes with git?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;If I&#x27;ve answered yes to any &lt;em&gt;one&lt;&#x2F;em&gt; of these questions, I know to create turn the project into a package.&lt;&#x2F;p&gt;
&lt;p&gt;This means I...&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;create a new git repository (and private remote, for backup purposes)&lt;&#x2F;li&gt;
&lt;li&gt;create a README (in markdown, preferably)&lt;&#x2F;li&gt;
&lt;li&gt;track dependencies I install or remove, as I install or remove them&lt;&#x2F;li&gt;
&lt;li&gt;formulate a testing strategy to make sure everything works as expected (either automated or manual)&lt;&#x2F;li&gt;
&lt;li&gt;create a set of convenience scripts for running repeatable tasks scoped only to the current project (e.g., Makefile, Rakefile, bundler binstubs, shell script)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Direnv: for secure coding and kind documentation</title>
        <published>2017-06-01T14:43:22-04:00</published>
        <updated>2017-06-01T14:43:22-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/direnv-for-secure-coding/"/>
        <id>https://www.thelonelyghost.com/posts/direnv-for-secure-coding/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/direnv-for-secure-coding/">&lt;h2 id=&quot;what-environment-variables-are&quot;&gt;What environment variables are&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-environment-variables-are&quot; aria-label=&quot;Anchor link for: what-environment-variables-are&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;introduction-to-usr-bin-env&quot;&gt;Introduction to &lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;env&lt;&#x2F;code&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#introduction-to-usr-bin-env&quot; aria-label=&quot;Anchor link for: introduction-to-usr-bin-env&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&#x2F;usr&#x2F;bin&#x2F;env&lt;&#x2F;code&gt; (or just &lt;code&gt;env&lt;&#x2F;code&gt;) echoes out all of the currently set environment variables.&lt;&#x2F;p&gt;
&lt;p&gt;Shebangs at the top of files in Unix-based OS&#x27;s determine how to execute instead of by file extension.&lt;&#x2F;p&gt;
&lt;p&gt;Might see &lt;code&gt;#!&#x2F;usr&#x2F;bin&#x2F;env python&lt;&#x2F;code&gt; or &lt;code&gt;#!&#x2F;usr&#x2F;bin&#x2F;env ruby&lt;&#x2F;code&gt; as a nicer way of not hardcoding the path to &lt;code&gt;ruby&lt;&#x2F;code&gt; or &lt;code&gt;python&lt;&#x2F;code&gt; binary.&lt;&#x2F;p&gt;
&lt;p&gt;Why not &lt;code&gt;#!ruby&lt;&#x2F;code&gt; or &lt;code&gt;#!python&lt;&#x2F;code&gt;? Environment variables such as &lt;code&gt;GEM_HOME&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s take a look into what is set in your terminal environment: (&lt;em&gt;Incoming!&lt;&#x2F;em&gt;)&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# echoes out all current environment variables&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&#x2F;usr&#x2F;bin&#x2F;env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;accessing-from-the-language&quot;&gt;Accessing from the language&lt;a class=&quot;zola-anchor&quot; href=&quot;#accessing-from-the-language&quot; aria-label=&quot;Anchor link for: accessing-from-the-language&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Ruby:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8BE9FD;&quot;&gt;puts&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; ENV&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# =&amp;gt; &amp;#39;&#x2F;home&#x2F;myuser&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Python:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #8BE9FD;&quot;&gt;print&lt;&#x2F;span&gt;&lt;span&gt;(os.environ.get(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;Home&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;))&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #6272A4;&quot;&gt;# =&amp;gt; &amp;#39;&#x2F;home&#x2F;myuser&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;PowerShell:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Write-Host $env:HOME&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# =&amp;gt; &amp;#39;C:\Users\MyUser&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;following-good-practices&quot;&gt;Following good practices&lt;a class=&quot;zola-anchor&quot; href=&quot;#following-good-practices&quot; aria-label=&quot;Anchor link for: following-good-practices&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;3 basic ways, depending on how framework-y you want to get:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Just-in-time environment variables&lt;&#x2F;li&gt;
&lt;li&gt;Shell script setting environment variables&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;dotenv&lt;&#x2F;code&gt; &#x2F; &lt;code&gt;direnv&lt;&#x2F;code&gt; frameworks&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;just-in-time&quot;&gt;Just-in-time&lt;a class=&quot;zola-anchor&quot; href=&quot;#just-in-time&quot; aria-label=&quot;Anchor link for: just-in-time&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Setup: &lt;em&gt;N&#x2F;a&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Invoke:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&#x2F;some_command.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# vs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SOME_VAR=&amp;quot;derp&amp;quot; .&#x2F;some_command.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;shell-script&quot;&gt;Shell script&lt;a class=&quot;zola-anchor&quot; href=&quot;#shell-script&quot; aria-label=&quot;Anchor link for: shell-script&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Setup:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# FILE: environment.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export SOME_VAR=&amp;quot;derp&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Invoke:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;source .&#x2F;environment.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&#x2F;some_command.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;frameworks&quot;&gt;Frameworks&lt;a class=&quot;zola-anchor&quot; href=&quot;#frameworks&quot; aria-label=&quot;Anchor link for: frameworks&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Depending on the framework, it auto-loads (a la &lt;code&gt;source&lt;&#x2F;code&gt;) the file when entering the directory. It is hidden by default, hence filename starting with &lt;code&gt;.&lt;&#x2F;code&gt;, so it stays out of your way most times. The file evaluated depends on the framework (&lt;code&gt;.env&lt;&#x2F;code&gt; for &lt;code&gt;dotenv&lt;&#x2F;code&gt;, &lt;code&gt;.envrc&lt;&#x2F;code&gt; for &lt;code&gt;direnv&lt;&#x2F;code&gt;), and evaluates all other files by that name up the directory tree. Might have seen this approach already with other tools (rspec, git, etc.)&lt;&#x2F;p&gt;
&lt;p&gt;Setup:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# FILE: .env&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;export SOME_VAR=&amp;quot;derp&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Invoke:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;cd &amp;lt;project-directory&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;# if dotenv and no language specific dotenv implementation is loaded program, uncomment the following line:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;#dotenv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.&#x2F;some_command.sh&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;documentation&quot;&gt;Documentation&lt;a class=&quot;zola-anchor&quot; href=&quot;#documentation&quot; aria-label=&quot;Anchor link for: documentation&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Document it.&lt;&#x2F;p&gt;
&lt;p&gt;No. Seriously.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;when&quot;&gt;When&lt;a class=&quot;zola-anchor&quot; href=&quot;#when&quot; aria-label=&quot;Anchor link for: when&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Always. As soon as it is introduced, even in a branch.&lt;&#x2F;p&gt;
&lt;p&gt;Make the code fail spectacularly (throw unhandled exception with explanation in message) if the environment varaible is not set.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;where&quot;&gt;Where&lt;a class=&quot;zola-anchor&quot; href=&quot;#where&quot; aria-label=&quot;Anchor link for: where&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Possible locations:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.env.example&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;.envrc.example&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;within &lt;code&gt;README.md&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;within other file that is &lt;em&gt;versioned with the project&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;how-development-practices&quot;&gt;How (Development Practices)&lt;a class=&quot;zola-anchor&quot; href=&quot;#how-development-practices&quot; aria-label=&quot;Anchor link for: how-development-practices&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Documenting environment variables can be done in a &lt;code&gt;README.md&lt;&#x2F;code&gt; (best), but the quickest notes for later reference can be as comments in a &lt;code&gt;.env.example&lt;&#x2F;code&gt; file itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jit-environment-variable-setting&quot;&gt;JIT environment variable setting&lt;a class=&quot;zola-anchor&quot; href=&quot;#jit-environment-variable-setting&quot; aria-label=&quot;Anchor link for: jit-environment-variable-setting&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Document example usage in &lt;code&gt;README.md&lt;&#x2F;code&gt;, or other docs&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Benefits&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Consistent -- Same approach as with &lt;code&gt;&#x2F;etc&#x2F;init.d&#x2F;*&lt;&#x2F;code&gt; scripts&lt;&#x2F;li&gt;
&lt;li&gt;Dependencies -- No additional tooling&lt;&#x2F;li&gt;
&lt;li&gt;Power -- Access to everything the shell has to offer&lt;&#x2F;li&gt;
&lt;li&gt;Transitive -- Switching projects? Variables do not persist to shell session&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Tradeoffs&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Forgetting -- Must remember to include variables on &lt;em&gt;every&lt;&#x2F;em&gt; invocation&lt;&#x2F;li&gt;
&lt;li&gt;Human Error -- Grabbing what variables are needed requires parsing the docs intended for humans (which is less fault-tolerant)&lt;&#x2F;li&gt;
&lt;li&gt;Complicated -- Must be a one-liner, or else it falls under the &quot;sourced files&quot; option by definition&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;source-files-manually&quot;&gt;Source files manually&lt;a class=&quot;zola-anchor&quot; href=&quot;#source-files-manually&quot; aria-label=&quot;Anchor link for: source-files-manually&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Center on the &lt;code&gt;.env&lt;&#x2F;code&gt; convention and include a file (committed to the repository) named &lt;code&gt;.env.example&lt;&#x2F;code&gt;, containing sample values &lt;em&gt;for all variables&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;This allows people to optionally use a framework like &lt;code&gt;dotenv&lt;&#x2F;code&gt;. Do not allow yourself to commit &lt;code&gt;.env&lt;&#x2F;code&gt; to the repo, so add it to global gitignore for your workstation.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Benefits&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Simplicity -- Includes example, machine-parseable values with the project source code&lt;&#x2F;li&gt;
&lt;li&gt;Dependencies -- No additional tooling&lt;&#x2F;li&gt;
&lt;li&gt;Power -- Access to everything the shell has to offer&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Tradeoffs&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Forgetting -- Must remember to &lt;code&gt;source .&#x2F;.env&lt;&#x2F;code&gt; in every terminal running the application
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Designed well:&lt;&#x2F;em&gt; Blows up spectacularly at earliest possible phase&lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;Designed poorly:&lt;&#x2F;em&gt; Partially executes, then bombs out&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Dirty Environment -- Must open new or restart terminal when switching projects&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;frameworks-1&quot;&gt;Frameworks&lt;a class=&quot;zola-anchor&quot; href=&quot;#frameworks-1&quot; aria-label=&quot;Anchor link for: frameworks-1&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;I use &lt;code&gt;direnv&lt;&#x2F;code&gt; because I like the set-and-forget approach. For this section, I&#x27;m going to focus on &lt;code&gt;direnv&lt;&#x2F;code&gt; implementation.&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Create a &lt;code&gt;.envrc&lt;&#x2F;code&gt; file&lt;&#x2F;li&gt;
&lt;li&gt;Globally gitignore &lt;code&gt;.envrc&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Create a &lt;code&gt;.envrc.example&lt;&#x2F;code&gt; file&lt;&#x2F;li&gt;
&lt;li&gt;Store sanitized, dummy examples in &lt;code&gt;.envrc.example&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Yes, I store credentials on local machine in plaintext. Shame me. If you can think of a way around this, I&#x27;m very open to improvement.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;direnv&quot;&gt;Direnv&lt;a class=&quot;zola-anchor&quot; href=&quot;#direnv&quot; aria-label=&quot;Anchor link for: direnv&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Found at https:&#x2F;&#x2F;direnv.net&#x2F;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Benefits&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Secure -- Required to manually whitelist versions of &lt;code&gt;.envrc&lt;&#x2F;code&gt; every time it changes (&lt;code&gt;direnv allow&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Automatic -- Sets variables when &lt;code&gt;cd&lt;&#x2F;code&gt; into directory&lt;&#x2F;li&gt;
&lt;li&gt;Self-Contained -- No hooks to additional libraries inside of program (like with &lt;code&gt;dotenv&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Inheiritance -- PWD containing &lt;code&gt;.envrc&lt;&#x2F;code&gt; overwrites ones in parent dirs, on up the chain&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Tradeoffs&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Execution -- Execute from outside of project directory (&lt;code&gt;.&#x2F;project-dir&#x2F;some_script.sh&lt;&#x2F;code&gt; will not automatically set environment variables in &lt;code&gt;.&#x2F;project-dir&#x2F;.envrc&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;li&gt;Lag -- Additional program running on every &lt;code&gt;cd&lt;&#x2F;code&gt; in the shell&lt;&#x2F;li&gt;
&lt;li&gt;Shell -- Limits to one of several popular shells&lt;&#x2F;li&gt;
&lt;li&gt;Variables -- Only handles shell variables (e.g., &lt;code&gt;export SOME_VAR=&#x27;foo&#x27;&lt;&#x2F;code&gt;, not &lt;code&gt;some_var() { echo &#x27;foo&#x27; }&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>First thoughts on Habitat</title>
        <published>2017-05-25T13:46:21-04:00</published>
        <updated>2017-05-25T13:46:21-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/first-thoughts-on-habitat-sh/"/>
        <id>https://www.thelonelyghost.com/posts/first-thoughts-on-habitat-sh/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/first-thoughts-on-habitat-sh/">&lt;p&gt;This past week I was at ChefConf going to various panels, intent on learning about Inspec and the best practices therein. While there, half of the panels were on this thing called Habitat, which I originally thought to be Yet Another Docker Alternative ®. First of all, it&#x27;s not. Like Vagrant&#x27;s relationship with Virtualbox, it tends to pair nicely with it and use it by default, but it is not the only way to use it by a long shot.&lt;&#x2F;p&gt;
&lt;p&gt;During the first keynote at ChefConf, I was exposed to some of the features of Chef Automate. I had seen a lot of these features a few weeks prior when some Chef people came to my office to demo Automate and some of the features (e.g., Compliance, Delivery), but one thing stood out that I hadn&#x27;t noticed before: it looked like there was a dependency handling system that, if a dependent package had a version bump or new release, all packages that depend on it are rebuilt with that new version and packages that depend on those newly rebuilt ones are too, rippling through until all dependencies are resolved. This was especially resonant when I was dealing with Apache&#x27;s mod_ssl and Heartbleed a few years back. You mean if I use this software and a CVE comes out for a low-level dependency, I can patch it and redeploy same day? Easily? Yes.&lt;&#x2F;p&gt;
&lt;p&gt;Wait a second. This isn&#x27;t what Chef does. Chef is about configuration management, not application dependency resolution nor, hopefully, application deployment. What&#x27;s this doing in Chef&#x27;s Automate software then?&lt;&#x2F;p&gt;
&lt;p&gt;I dug in a little more and found out that build system I saw was actually hooked into a project name that tickled at the back of my brain a little bit, like I had seen it before. Its name was Habitat.&lt;&#x2F;p&gt;
&lt;p&gt;I met up with some of my colleagues so we could divvy up this 6-track conference among the three of us, which we would then collaborate on what we learned and report back to our respective teams with this information. I wanted to go to the Habitat sessions, which appeared to be a track all its own. &lt;em&gt;We could really use some better dependency rebuilding triggers&lt;&#x2F;em&gt;, I thought, &lt;em&gt;and I&#x27;m not sure how we&#x27;re going to do it with our Jenkins setup quite yet&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;session-1-getting-started-with-habitat&quot;&gt;Session 1: Getting Started with Habitat&lt;a class=&quot;zola-anchor&quot; href=&quot;#session-1-getting-started-with-habitat&quot; aria-label=&quot;Anchor link for: session-1-getting-started-with-habitat&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;This first session was lead by the lead engineer of Habitat, Jamie Winsor, and here&#x27;s where things got a little fuzzy. I hopped into this session, barely making it 2 minutes after it started (thanks to delays surrounding lunch), and Jamie was already on-stage talking about what Habitat actually is. A few weeks prior to this, I had seen some material on the Habitat homepage and it seemed to indicate to my ADHD-ridden mind that it was Yet Another Docker Alternative ®, putting a slightly different interface onto it. The difference was that, in the keynote, it was mentioned it could export to Docker, VM, or bare metal, so that didn&#x27;t quite fit with my existing mental model.&lt;&#x2F;p&gt;
&lt;p&gt;Jamie carried on about what was involved with habitat while I struggled to piece together what was going on. It was one of those classic moments of an engineer being to deep in the subject to explain things at the level of the audience. That said, the rest of the audience members appeared to be following along just fine, so maybe it was just me.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what I gathered from his talk:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Habitat is written in Rust, in a way that is intended to be entirely self-contained&lt;&#x2F;li&gt;
&lt;li&gt;The intention of Habitat is for sandboxing applications&lt;&#x2F;li&gt;
&lt;li&gt;A prime focus when creating a new bit of software like this was to make it super easy to learn (&quot;Measured in terms of lunch breaks, not weeks to learn&quot;)&lt;&#x2F;li&gt;
&lt;li&gt;Habitat is supposed to be 100% platform agnostic&lt;&#x2F;li&gt;
&lt;li&gt;There&#x27;s this thing called habitat studio (&lt;code&gt;hab studio enter&lt;&#x2F;code&gt;) where you can hop into that super sandboxed environment from your terminal&lt;&#x2F;li&gt;
&lt;li&gt;This supervisor thing appears to aggregate logs and make sure application packages are running correctly (similar to &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rubygems.org&#x2F;gems&#x2F;foreman&quot;&gt;Foreman&lt;&#x2F;a&gt; and its Procfile?)&lt;&#x2F;li&gt;
&lt;li&gt;Somehow, an application package can be scaled up from the supervisor, grouped using a naming convention of &lt;code&gt;[package].[group]&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Application packages can be started without a supervisor and assigned to an already running one ad-hoc. Why might this be useful? No idea&lt;&#x2F;li&gt;
&lt;li&gt;Building and installing your application&#x27;s components are done using some hooks, like &lt;code&gt;plan.sh&lt;&#x2F;code&gt; in a specific file in a specific directory&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This left me with a bunch of questions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What did I just watch?&lt;&#x2F;li&gt;
&lt;li&gt;What is the purpose of the Habitat Studio?&lt;&#x2F;li&gt;
&lt;li&gt;How is this different from creating a Docker container with the base being Alpine Linux?&lt;&#x2F;li&gt;
&lt;li&gt;Does this run on Windows at all, or are all the hooks built for a Unix-based system?&lt;&#x2F;li&gt;
&lt;li&gt;What would it look like if I switched some of the legacy projects we run over to this orchestration(???) framework today?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Intro to rclone</title>
        <published>2017-04-19T14:23:19-04:00</published>
        <updated>2017-04-19T14:23:19-04:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/intro-to-rclone/"/>
        <id>https://www.thelonelyghost.com/posts/intro-to-rclone/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/intro-to-rclone/">&lt;p&gt;The other day, as I was looking for a way to keep Vivaldi in sync across my devices, I stumbled onto a paid product which syncs a local directory with a remote one on one of a few cloud providers. My main ones I like to use are Dropbox, Google Drive, and MediaFire, but I would like to be able to use S3 and other more robust options for when I start scaling up in my storage needs. The problem was that this paid product had a high price tag, showed up in forum spam (and I don&#x27;t want to encourage that method of marketing), and it only worked with a handful of providers. It did not support Google Drive, for instance.&lt;&#x2F;p&gt;
&lt;p&gt;&quot;Boy, I wish they made something like rsync for these cloud providers...&quot;, I remarked.&lt;&#x2F;p&gt;
&lt;p&gt;The cloud storage providers I routinely use all have publicly available APIs, are all targeting the same problem space, and have very similar approaches to solving the problem at hand (albeit with differing free usage tiers). &quot;That shouldn&#x27;t be too difficult. A weekend hackfest to build a tool like rsync for them!&quot;&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to pause there and just mention that a &quot;weekend hackfest&quot; rarely produces the expected result, as many others have blogged to the same effect.&lt;&#x2F;p&gt;
&lt;p&gt;My first step before starting any project is to get a lay of the land. I googled for &quot;rsync [cloud provider]&quot; (e.g., dropbox) to see if there were any pre-existing solutions I had missed in the first round. If there were none, I could see what came close, figure out what worked for them, and possibly use it as a basis for a new open source tool. Luckily my search stopped there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;use-case&quot;&gt;Use case&lt;a class=&quot;zola-anchor&quot; href=&quot;#use-case&quot; aria-label=&quot;Anchor link for: use-case&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Before we get too far into this, let&#x27;s establish what I wanted to build (assuming I didn&#x27;t find a pre-existing solution).&lt;&#x2F;p&gt;
&lt;p&gt;I love Dropbox, but I don&#x27;t trust their agent if I can&#x27;t audit their source code. I also don&#x27;t like that it has sufficient permissions to read all files on my filesystem by default. Seems like a major security hole! I would like to copy individual files or recurse through a directory of files to transfer (a la &lt;code&gt;scp&lt;&#x2F;code&gt;).&lt;&#x2F;p&gt;
&lt;p&gt;I also love Google Drive because it&#x27;ll follow me wherever my ever-persistent Google account follows me.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;enter-rclone&quot;&gt;Enter &lt;code&gt;rclone&lt;&#x2F;code&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#enter-rclone&quot; aria-label=&quot;Anchor link for: enter-rclone&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Rclone is an odd tool. Much the same as the first time I used &lt;code&gt;scp&lt;&#x2F;code&gt; and overthinking how to make good use of it, I was afraid of how to get started. Really it&#x27;s very easy.&lt;&#x2F;p&gt;
&lt;p&gt;Step 1, install rclone per the documentation for your platform. I&#x27;ve got added an option in my dotfiles hooks to install it from homebrew (if I&#x27;m on my Mac) and Ubuntu (if I&#x27;m on my Chromebook). Mac allows for the super straightforward &lt;code&gt;brew install rclone&lt;&#x2F;code&gt;. Ubuntu is slightly less convenient, but very easily scriptable, so I&#x27;ll refer you to &lt;a class=&quot;external-link&quot; rel=&quot;noopener nofollow noreferrer external&quot; target=&quot;_blank&quot; href=&quot;https:&#x2F;&#x2F;rclone.org&#x2F;install&#x2F;#linux-installation-from-precompiled-binary&quot;&gt;the installation docs&lt;&#x2F;a&gt; rather than dig any deeper here.&lt;&#x2F;p&gt;
&lt;p&gt;Step 2, run &lt;code&gt;rclone config&lt;&#x2F;code&gt; and follow the prompts. I set it up with my Dropbox (as &lt;code&gt;dropbox&lt;&#x2F;code&gt;) and my Google Drive (as &lt;code&gt;drive&lt;&#x2F;code&gt;) accounts.&lt;&#x2F;p&gt;
&lt;p&gt;Step 3, ???&lt;&#x2F;p&gt;
&lt;p&gt;Step 4, profit!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;a class=&quot;zola-anchor&quot; href=&quot;#examples&quot; aria-label=&quot;Anchor link for: examples&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;restoring-gpg-keys&quot;&gt;Restoring GPG keys&lt;a class=&quot;zola-anchor&quot; href=&quot;#restoring-gpg-keys&quot; aria-label=&quot;Anchor link for: restoring-gpg-keys&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;I have my GPG signing keys (password protected, of course) backed up on Dropbox, for this example. I need to grab those locally and import them.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone copy dropbox:gpg_keys&#x2F;opensource.secret.key .&#x2F;personal.key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone copy dropbox:gpg_keys&#x2F;opensource.public.key .&#x2F;personal.pub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; gpg2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; --import --allow-secret-key-import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; .&#x2F;personal.key&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; gpg2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; --import&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; .&#x2F;personal.pub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rm .&#x2F;personal.key .&#x2F;personal.pub&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If I wanted to be more security-conscious and do this in fewer steps, it looks like &lt;code&gt;gpg&lt;&#x2F;code&gt; allows streaming in the imports via STDIN. Let&#x27;s try that route!&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone cat dropbox:gpg_keys&#x2F;opensource.secret.key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt; gpg2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; --import --allow-secret-key-import&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone cat dropbox:gpg_keys&#x2F;opensource.public.key&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt; gpg2&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; --import&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;syncing-personal-documents&quot;&gt;Syncing personal documents&lt;a class=&quot;zola-anchor&quot; href=&quot;#syncing-personal-documents&quot; aria-label=&quot;Anchor link for: syncing-personal-documents&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;Google Drive was originally intended for office-style documents, right? How about we sync it with our Documents directory?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone sync drive:local-documents&#x2F; ~&#x2F;Documents&#x2F;google-drive&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In this case, any time there&#x27;s a file or directory that exists solely on Google Drive in the &lt;code&gt;local-documents&lt;&#x2F;code&gt; directory, it syncs it to a subdirectory of our Documents directory called &lt;code&gt;google-drive&lt;&#x2F;code&gt;. If there&#x27;s a file or directory that exists solely on the local filesystem, it&#x27;ll do the opposite. When a file exists in both places and it differs, the local copy will overwrite the remote copy.&lt;&#x2F;p&gt;
&lt;p&gt;The bonus for using sync here is you can hook it up to cron for scheduled syncing with one or more of your cloud providers. No more separate Dropbox, Google Drive, and OneDrive agents! I can finally get rid of my Google Drive client on Linu--oh wait... :(&lt;&#x2F;p&gt;
&lt;h3 id=&quot;finding-files&quot;&gt;Finding files&lt;a class=&quot;zola-anchor&quot; href=&quot;#finding-files&quot; aria-label=&quot;Anchor link for: finding-files&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h3&gt;
&lt;p&gt;What if I don&#x27;t recall what the directory structure is like on my cloud hosting provider? One of the nuances with &lt;code&gt;rclone ls&lt;&#x2F;code&gt; is that it&#x27;s like the unix equivalent of &lt;code&gt;ls -R&lt;&#x2F;code&gt;, recursing through each subdirectory to list all files. Only care about going 1 level deep? There&#x27;s &lt;code&gt;rclone lsd&lt;&#x2F;code&gt; for that.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s an example of how I essentially use &lt;code&gt;find&lt;&#x2F;code&gt; (by name) with a remote provider.&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; rclone ls drive:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt; grep&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;some_file&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #FF79C6;&quot;&gt; |&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt; sed&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; -e&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;s&#x2F;^ *[0-9]* *&#x2F;&#x2F;g&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;MyFiles__local&#x2F;some_file.notes.txt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;derp.some_file&#x2F;fizz_buzz.doc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Before you puke over resorting to &lt;code&gt;sed&lt;&#x2F;code&gt;, I only use it to trim some leading whitespace and numbers (indicating file size). You can leave that in if you prefer, but I like things that can be easily scripted out. This allows for straight-up copy and paste for later iteration in a shell script using a bash for-each loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;suggested-improvements&quot;&gt;Suggested improvements&lt;a class=&quot;zola-anchor&quot; href=&quot;#suggested-improvements&quot; aria-label=&quot;Anchor link for: suggested-improvements&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Rclone is far from a perfect tool. As a reasonably well-versed Linux CLI junkie, I am very used to traversing the filesystem, copying&#x2F;moving&#x2F;renaming files locally, and transferring them across the network via SSH, CIFS (Samba, a.k.a., Windows share), and SFTP. I would love it if it were just intuitive to use the unix-y commands &lt;code&gt;ls&lt;&#x2F;code&gt;, &lt;code&gt;rm&lt;&#x2F;code&gt;, &lt;code&gt;cp&lt;&#x2F;code&gt;, &lt;code&gt;mv&lt;&#x2F;code&gt;, and so on as a subcommand of rclone. I would also accept it being a mostly drop-in replacement for rsync.&lt;&#x2F;p&gt;
&lt;p&gt;That said, I understand that rclone is cross-platform and intended to have a unified interface for Windows and unix-y systems. Is there any reason we can&#x27;t make use of command aliases here? Why not have &lt;code&gt;rclone copy&lt;&#x2F;code&gt; be the exact same interface as &lt;code&gt;rclone cp&lt;&#x2F;code&gt;? And for the Windows users, aliasing &lt;code&gt;rclone dir&lt;&#x2F;code&gt; to &lt;code&gt;rclone ls&lt;&#x2F;code&gt;?&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Iron Crystals</title>
        <published>2017-01-07T21:30:00-05:00</published>
        <updated>2017-01-07T21:30:00-05:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/iron-crystals/"/>
        <id>https://www.thelonelyghost.com/posts/iron-crystals/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/iron-crystals/">&lt;p&gt;Today, I&#x27;ve begun looking into compiling the Crystal compiler from another
direction, possibly opening up the cross-platform capability of Crystal to all
of the operating systems certain other popular languages already support.&lt;&#x2F;p&gt;
&lt;p&gt;In past weeks, I&#x27;d begun digging into porting crystal to run under Alpine Linux,
that way I can have a very small, very specialized docker container to compile
Crystal projects, or even run their binaries. The problems I ran into were that
Crystal is a garbage-collected language. Why is that? Why not make it have zero
garbage collection, if at all possible, similar to how C, C++, or Rust have it?&lt;&#x2F;p&gt;
&lt;p&gt;Crystal has boasted that it is completely self-hosted, meaning it can be
compiled, contributed to, and more using the Crystal language itself. This comes
as a liability when trying to port it to another platform because, in order to
compile crystal for that platform, you must already have a Crystal compiler for
it. See the dilemma? Chicken and the egg.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s okay though, according to the docs, there is a way to port it to other
platforms by using the &lt;code&gt;--cross-compile&lt;&#x2F;code&gt; and &lt;code&gt;--target&lt;&#x2F;code&gt; compile-time flags. I
recently tried this in my endeavor to get it to work with Crystal, but failed in
that there were segfaults all along the way. The most cryptic? Something about
&lt;code&gt;sh&lt;&#x2F;code&gt; and not being able to access a value since it was out of bounds. In &lt;code&gt;sh&lt;&#x2F;code&gt;.
Yeah.&lt;&#x2F;p&gt;
&lt;p&gt;Okay, I&#x27;ve followed every documented piece of evidence in order to cross-compile
the Crystal compiler using &lt;code&gt;--cross-compile&lt;&#x2F;code&gt;, now what? The docs support the
alternative of stepping through since v0.0.1, when the compiler was written in
Ruby, to create a crystal compiler that works on the other platform. Given the
number of released versions since then, and how many more there will likely be
in the future, this doesn&#x27;t seem very future-proof either. What else can we do?&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s a thought:&lt;&#x2F;p&gt;
&lt;p&gt;Crystal has boasted since day zero (day one?) that it is inspired by the Ruby
programming language. Why wouldn&#x27;t that inspire alternate implementations too?
why can&#x27;t we create a JRuby or Rubinius or even Opal of Crystal? That might
satisfy some of the pre-requisite of porting the official Crystal compiler to
another platform by using the cross-platform traits of the other language. How
should we proceed?&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s the ultimate question. Over the coming weeks, I&#x27;m going to be digging
into Rust, Crystal, and basic, lower-level concepts involved in constructing a
programming language.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Local gems with Bundler</title>
        <published>2016-12-28T17:27:06-05:00</published>
        <updated>2016-12-28T17:27:06-05:00</updated>
        
        <author>
          <name>
            
              David Alexander
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://www.thelonelyghost.com/posts/local-gems-with-bundler/"/>
        <id>https://www.thelonelyghost.com/posts/local-gems-with-bundler/</id>
        
        <content type="html" xml:base="https://www.thelonelyghost.com/posts/local-gems-with-bundler/">&lt;p&gt;Testing an unreleased version of a gem? Want to develop 2 unreleased projects
that are based on each other and not have to worry about the following?&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gem &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;some_gem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; path:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;..&#x2F;my-dev-snapshot&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Leaving the &lt;code&gt;Gemfile&lt;&#x2F;code&gt; as such will screw with your project history, so we want
the version of &lt;code&gt;Gemfile&lt;&#x2F;code&gt; as it will be when the gem is released, keeping that
version in our &quot;git memory&quot;. See the following:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;$&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; bundle config local.some_gem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;$(&lt;&#x2F;span&gt;&lt;span style=&quot;color: #50FA7B;&quot;&gt;realpath&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt; ..&#x2F;my-dev-snapshot)&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This will allow your &lt;code&gt;Gemfile&lt;&#x2F;code&gt; to remain pristine without the &lt;code&gt;path: &#x27;..&#x2F;foo&#x27;&lt;&#x2F;code&gt;
hacks so others can set their own path to the gem source directory.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;caveats&quot;&gt;Caveats:&lt;a class=&quot;zola-anchor&quot; href=&quot;#caveats&quot; aria-label=&quot;Anchor link for: caveats&quot;&gt;🔗&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;The gem, in this case &lt;code&gt;some_gem&lt;&#x2F;code&gt;, must be pointed at a git repository. In this
case, it would need to be:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color: #F8F8F2; background-color: #282A36;&quot;&gt;&lt;code data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gem &lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;some_gem&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; github:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;foo&#x2F;bar&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: #BD93F9;&quot;&gt; branch:&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: #F1FA8C;&quot;&gt;master&lt;&#x2F;span&gt;&lt;span style=&quot;color: #E9F284;&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;This would allow us not only to optimize network traffic so we don&#x27;t make calls
out to the git repository all the time, but also point it toward a local working
copy.&lt;&#x2F;p&gt;
&lt;p&gt;There is an additional caveat, though. The given branch -- in the example,
&lt;code&gt;master&lt;&#x2F;code&gt; -- must match the branch of the current working copy at the path
specified with &lt;code&gt;bundle config&lt;&#x2F;code&gt; from earlier.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
