<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
    <channel>
      <title>Aaron Schlesinger&#x27;s Personal Site</title>
      <link>/</link>
      <description></description>
      <generator>Zola</generator>
      <language>en</language>
      <atom:link href="/rss.xml" rel="self" type="application/rss+xml"/>
      <lastBuildDate>Wed, 14 Feb 2024 21:58:32 +0000</lastBuildDate>
      <item>
          <title>What is Hyperlight (part II)?</title>
          <pubDate>Wed, 14 Feb 2024 21:58:32 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/hyperlight-overview-2/</link>
          <guid>/blog/hyperlight-overview-2/</guid>
          <description>&lt;p&gt;In &lt;a href=&quot;&#x2F;blog&#x2F;hyperlight-overview-1&quot;&gt;part 1&lt;&#x2F;a&gt; of this series, I detailed the motivations and background of the Hyperlight project. In doing so, I also mentioned some of the challenges we’ve set out to solve and, at a high level, how we&#x27;ve solved them. In this post, I’m going to talk about these challenges in much greater detail.&lt;&#x2F;p&gt;
&lt;p&gt;Hyperlight is systems-level software, which means it interacts with some of the lowest-level details of how software runs on a computer. The concepts and challenges with which it&#x27;s concerned are very nuanced and detailed, so I think it&#x27;s especially critical to talk about them directly, right alongside the high-level description from part 1.&lt;&#x2F;p&gt;
&lt;p&gt;Thus, you&#x27;ll find this post contains much more technical content than the last, and I try to take time to explain some of the more complex terms and concepts throughout.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s get started! &lt;&#x2F;p&gt;
&lt;h2 id=&quot;virtual-cpus-the-workhorse-of-hyperlight&quot;&gt;Virtual CPUs: the workhorse of Hyperlight&lt;&#x2F;h2&gt;
&lt;p&gt;In part 1, I mentioned Hyperlight relies on hypervisor isolation to ensure we provide the security guarantees to which we’ve committed. I also explained that our use of Hypervisor isolation -- also called virtual machines or VMs -- provides a set of virtualized hardware we use to run native code inside a VM. For the purposes of this post today, it&#x27;s helpful to think of a single VM as an individual &amp;quot;slice&amp;quot; of a physical computer running in the cloud &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;With this &amp;quot;slice&amp;quot; metaphor in mind, we can visualize a physical machine as a pie with a specified number of slices, each of which can run arbitrary code, which we call a &amp;quot;guest&amp;quot; in Hyperlight parlance, that has been compiled to the underlying hardware architecture of the physical machine &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Just as a CPU generally executes code on your laptop or desktop computer, a VM&#x27;s virtual CPU (vCPU) executes code inside a Hyperlight &lt;code&gt;Sandbox&lt;&#x2F;code&gt;, which is the term we use to describe a &amp;quot;slice.&amp;quot; &lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;Sandbox&lt;&#x2F;code&gt;es abstract away the underlying VM and vCPU, and currently support KVM or Hyper-V across Windows and Linux.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;low-level-code-execution-basics&quot;&gt;“Low level” code execution basics&lt;&#x2F;h3&gt;
&lt;p&gt;On nearly all systems, the CPU&#x2F;vCPU is the most important part of the hardware&#x2F;software stack involved in making software run &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. There are, however, at least several more components involved in making most running code work, including virtual hardware devices, the virtual memory hierarchy, and many more. Since there are so many components involved, an operating system (OS) kernel is usually required to manage them all and provide a manageable abstraction for programs to compile (and link) against.&lt;&#x2F;p&gt;
&lt;p&gt;This abstraction is commonly presented as a group of &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;System_call&quot;&gt;system calls&lt;&#x2F;a&gt; (&amp;quot;syscalls&amp;quot;) that allow programs to safely interact with the OS kernel, which in turn interacts with the hardware. Through syscalls, a program can interact with a filesystem, process model, OS threads, and more. Most general-purpose software requires all of these features and more.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;low-level-code-execution-for-a-specific-use-case&quot;&gt;Low level code execution for a specific use case&lt;&#x2F;h2&gt;
&lt;p&gt;Recall, however, from part 1 that we&#x27;re not primarily aiming to power general-purpose software with Hyperlight. Instead, we&#x27;re trying to run smaller pieces of software intended to execute repeatedly in response to an event. Typically, this model is called &amp;quot;functions-as-a-service,&amp;quot; abbreviated to &amp;quot;FaaS&amp;quot;. The FaaS model presents requirements that are much different from those of general-purpose software.&lt;&#x2F;p&gt;
&lt;p&gt;In particular, this model requires a set of features much, much smaller than the those provided by an OS kernel. In many cases, we can get away with providing none of them. Thus, we default to &lt;em&gt;avoiding these features altogether&lt;&#x2F;em&gt; inside Hyperlight &lt;code&gt;Sandbox&lt;&#x2F;code&gt;es. Thus, a &lt;code&gt;Sandbox&lt;&#x2F;code&gt; provides a runtime environment that by default has only a vCPU, its virtual registers, and a single linear block of memory.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;what-you-get-when-you-run-without-an-os&quot;&gt;What you get when you run without an OS&lt;&#x2F;h3&gt;
&lt;p&gt;While it might seem like no program could ever execute in this stripped-down environment (and maybe that this is a dumb idea!), some can. And, for our specific use case, this idea turns out to work very well.&lt;&#x2F;p&gt;
&lt;p&gt;First, the main benefits to this approach are twofold, as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No boot-up process&lt;&#x2F;strong&gt;: because we&#x27;ve removed the OS entirely, there&#x27;s no kernel to boot up and we can start running a user&#x27;s code with significantly reduced latency&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Reduced attack surface&lt;&#x2F;strong&gt;: because there’s no OS, the attack surface inside the VM – the amount of running software that can be compromised by an attacker – is exactly as big as the running binary, and usually much smaller than that of a running OS&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;borrowing-from-existing-approaches&quot;&gt;Borrowing from existing approaches&lt;&#x2F;h3&gt;
&lt;p&gt;Second, this approach is not really novel; it&#x27;s been used several times throughout history, and its most recent incarnation is called &lt;a href=&quot;http:&#x2F;&#x2F;unikernel.org&quot;&gt;unikernels&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;These systems, like Hyperlight, require you to compile and link against a library, in your language of choice, that provides the abstractions you need to run your code. Thus, instead of calling OS-provided syscalls, you call a function provided by a library, which is then directly responsible for interacting with the underlying system to do what you need.&lt;&#x2F;p&gt;
&lt;p&gt;Since modern compilers and linkers are very good at optimizing code, the result of this approach is that unikernel applications ship with &lt;em&gt;only&lt;&#x2F;em&gt; the abstractions they need and nothing more. There are drawbacks of this approach (no shared implementation, harder to build software, etc...), but the benefits approximately match those in the above list.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unikernels-an-unpopular-but-useful-for-us-technology&quot;&gt;Unikernels: an unpopular but useful (for us) technology&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;ve heard of unikernels, you&#x27;re probably in the minority. They&#x27;re not very popular at all, and for good reason; they are a poor choice for running much of the software we use in our everyday lives.&lt;&#x2F;p&gt;
&lt;p&gt;As I write this, for example, I&#x27;m running at least 9 different applications on my laptop, each of which is relying on a wide range of syscalls. The OS, which implements all of those syscalls, is in turn utilizing all the different features of the hardware inside my laptop.&lt;&#x2F;p&gt;
&lt;p&gt;This is a very common scenario, and it thus makes sense to have one common, underlying operating system to manage that hardware in one place and provide a single, managed abstraction to all the applications that run atop it.&lt;&#x2F;p&gt;
&lt;p&gt;In the cloud, though, we have a very different situation. Instead of running 10 or 20 different applications, we&#x27;re running millions or more, each with wildly different requirements. Some need to run for a long time, some need to run for a very short time, some need to use a lot of memory, some need to use a lot of CPU, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;In this kind of environment, it pays to specialize as much as possible. As I mentioned above, Hyperlight targets applications that need to run for a very short time and need to use a very small set of resources.&lt;&#x2F;p&gt;
&lt;p&gt;For specifically this use case, unikernel-like technologies like Hyperlight are a great fit.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-you-actually-execute-code-without-an-operating-system&quot;&gt;How you actually execute code without an operating system&lt;&#x2F;h2&gt;
&lt;p&gt;We now know some important details behind Hyperlight and we get why it&#x27;s important. Let&#x27;s now look at how we accomplish Hyperlight&#x27;s core goal of running &lt;em&gt;arbitrary customer code&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;First, it would be a bad product experience to require our customers compile their code against some unikernel library we provide. It&#x27;s also not feasible to rewrite their code to use the unikernel library and then compile it to a unikernel binary.&lt;&#x2F;p&gt;
&lt;p&gt;The solution to this problem is (as with many problems in computer science) to add another layer of abstraction. Back in part 1, I talked about two different kinds of VMs, and how we&#x27;re running one VM inside another VM. The inner VM is the abstraction in question.&lt;&#x2F;p&gt;
&lt;p&gt;We build a relatively small number of guest binaries, each of which runs a different VM. We then load the appropriate guest binary (based on the code the customer furnishes) into a &lt;code&gt;Sandbox&lt;&#x2F;code&gt;, load their code into the right place in memory, and finally execute the &lt;code&gt;Sandbox&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;We currently have a guest binary called &lt;code&gt;hyperlight-wasm&lt;&#x2F;code&gt; that can execute the &lt;a href=&quot;https:&#x2F;&#x2F;webassembly.org&quot;&gt;Wasm&lt;&#x2F;a&gt; instruction set, and we&#x27;ve prototyped others.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wasm-and-a-different-kind-of-vm&quot;&gt;Wasm, and a different kind of VM&lt;&#x2F;h3&gt;
&lt;p&gt;The &lt;code&gt;hyperlight-wasm&lt;&#x2F;code&gt; guest is an important one because it can accommodate any programming language that can be compiled to Wasm. That list of languages is large and growing, which means Hyperlight can easily support a wide array of languages and their associated ecosystems with very little extra work from us.&lt;&#x2F;p&gt;
&lt;p&gt;If a user gives us their code in some language, we need to compile it to Wasm and load the &lt;code&gt;hyperlight-wasm&lt;&#x2F;code&gt; binary into a &lt;code&gt;Sandbox&lt;&#x2F;code&gt;&#x27;s memory. The binary then does roughly the following:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Loads the compiled Wasm instructions into memory &lt;&#x2F;li&gt;
&lt;li&gt;Launch a Wasm VM (we currently use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bytecodealliance&#x2F;wasm-micro-runtime&quot;&gt;WAMR&lt;&#x2F;a&gt; due to its small footprint and controlled feature set)&lt;&#x2F;li&gt;
&lt;li&gt;Execute the Wasm instructions inside the VM&lt;&#x2F;li&gt;
&lt;li&gt;Return the result to the host (the code that started the &lt;code&gt;Sandbox&lt;&#x2F;code&gt;)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Essentially, our &lt;code&gt;hyperlight-wasm&lt;&#x2F;code&gt; binary is a very, very lightweight approximate equivalent to an operating system that only executes Wasm instructions.&lt;&#x2F;p&gt;
&lt;p&gt;Also, the Wasm instruction set supports only a simple processing unit and a linear memory space -- a very close match to the capabilities of a default &lt;code&gt;Sandbox&lt;&#x2F;code&gt;. Wasm does, however, have facilities like &lt;a href=&quot;https:&#x2F;&#x2F;wasi.dev&quot;&gt;WASI&lt;&#x2F;a&gt; that go beyond the defaults and utilize some of the familiar OS-level abstractions. If we encounter those use cases, we can initialize and provide those abstractions in a lazy or &amp;quot;on-demand&amp;quot; fashion and preserve the fast startup times I described above.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;results-and-conclusion&quot;&gt;Results and conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;The results of this approach are exciting. In most cases, we can start a sandbox and begin executing a user’s Wasm module in less than 1 millisecond (1&#x2F;1000 of a second).&lt;&#x2F;p&gt;
&lt;p&gt;This latency is competitive with some other hosted FaaS systems, which generally don’t currently provide hardware-level isolation, and an order-of-magnitude improvement over other FaaS systems, which generally do so. I believe the key innovation Hyperlight provides, especially when used in such a FaaS scenario, is the combination of the hardware-level isolation from the latter system and the low latencies of the former system. &lt;&#x2F;p&gt;
&lt;p&gt;Because we&#x27;ve achieved this combination, I&#x27;m very excited about the future of Hyperlight for the following two reasons:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The use-cases&lt;&#x2F;strong&gt;: while FaaS is the first and primary use case, I think there are other use cases for which a system like Hyperlight could be a great fit&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;The technology&lt;&#x2F;strong&gt;: our use of Rust is expanding within the project (I talk about our use of Rust below in an addendum), we&#x27;re developing new guests to run different VMs, and we&#x27;re expanding the security features of the system&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Overall, I think Hyperlight represents a new way to run very specific kinds of software in the cloud, and I&#x27;m excited to see how it grows.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;addendum-rust-powers-everything&quot;&gt;Addendum: Rust powers everything&lt;&#x2F;h2&gt;
&lt;p&gt;Because Hyperlight is such foundational technology due to its position in the software&#x2F;hardware stack, the tools we use to build it are critically important for its security, speed, reliability, and overall success. I&#x27;m going to move from discussing how we designed Hyperlight to discussing some of the tools we used to actually build it. I believe the former is as important as the latter in this case.&lt;&#x2F;p&gt;
&lt;p&gt;Recall from part 1 that Wasm provides &lt;a href=&quot;https:&#x2F;&#x2F;www.usenix.org&#x2F;system&#x2F;files&#x2F;sec22-bosamiya.pdf&quot;&gt;mathematically-provable safety guarantees&lt;&#x2F;a&gt;, but these proofs are only valid if the software that implements the math is bug-free. The same requirement applies to Hyperlight itself.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s hard (and in many cases, impossible) to guarantee bug-free software, so we aim to eliminate several &lt;em&gt;classes&lt;&#x2F;em&gt; (or, types) of bugs and have taken aim at several types of memory safety bugs like &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Buffer_overflow&quot;&gt;buffer overflows&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;owasp.org&#x2F;www-community&#x2F;vulnerabilities&#x2F;Using_freed_memory&quot;&gt;use-after-free&lt;&#x2F;a&gt; issues.&lt;&#x2F;p&gt;
&lt;p&gt;To accomplish these goals, we must implement Hyperlight &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; in a robust, secure and efficient manner. The &lt;a href=&quot;https:&#x2F;&#x2F;www.rust-lang.org&quot;&gt;Rust programming language&lt;&#x2F;a&gt;, or &amp;quot;Rust,&amp;quot; has proven to be an invaluable tool to help us do that job.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;why-rust-for-hyperlight&quot;&gt;Why Rust for Hyperlight?&lt;&#x2F;h3&gt;
&lt;p&gt;Rust is a relatively new language, especially in the group of systems languages that let the programmer access low-level functionality on the machine. The language differs from most of those other systems languages, however, by providing some very strong memory safety guarantees.&lt;&#x2F;p&gt;
&lt;p&gt;To build Hyperlight, we need almost exactly those features. Recall from part 1 that memory safety is an area of focus for the software technology industry at-large, and Rust is being adopted and supported in large part because it does a good job of providing memory safety guarantees. In some cases, it even can provide formal mathematical guarantees, and we&#x27;ve in fact been able to reap some of those benefits in our codebase.&lt;&#x2F;p&gt;
&lt;p&gt;Bugs are still possible, but we believe Rust gives us the best chance to build safe, reliable and generally high-quality software &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;. There is much more to say about specifically how we exploit the power of Rust, but you can get some ideas from the &amp;quot;Calling Rust from C# and back again&amp;quot; series (&lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust&quot;&gt;part 1&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust-2&quot;&gt;part 2&lt;&#x2F;a&gt;) I wrote on this blog previously.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; Note this metaphor is not perfect because the physical machine is not always divided equally or even logically, and in many cases may be oversubscribed on vCPUs or memory.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; In many cases, this architecture is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;X86&quot;&gt;x86&lt;&#x2F;a&gt;, but &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;ARM_architecture_family&quot;&gt;ARM&lt;&#x2F;a&gt; is becoming more widely used, and it’s very possible &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;RISC-V&quot;&gt;RISC-V&lt;&#x2F;a&gt; will become relevant in the cloud as well&lt;&#x2F;p&gt;
&lt;p&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#3&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; These days, GPUs and other specialized computing devices are becoming more and more common, especially in modern AI&#x2F;ML and computer graphics applications.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#4&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; And not just this system as specified here and in part 1. We also need to write robust testing and monitoring&#x2F;alerting code, security features, memory management features, and everything else you’d need to run software like this in an enormous distributed system in the cloud.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#5&quot;&gt;5&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; The benefits of Rust in this domain go beyond memory safety. A good representative example is &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;stable&#x2F;clippy&#x2F;index.html&quot;&gt;Clippy&lt;&#x2F;a&gt;, which is a static analysis tool included by default with the toolchain that can either directly catch &lt;em&gt;before compile time&lt;&#x2F;em&gt; or prevent code that is likely to lead to future bugs.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What is Hyperlight?</title>
          <pubDate>Mon, 08 Jan 2024 21:58:32 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/hyperlight-overview-1/</link>
          <guid>/blog/hyperlight-overview-1/</guid>
          <description>&lt;p&gt;After I wrote those posts about C#-to-Rust and Rust-to-C# communication (&lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust&quot;&gt;part 1&lt;&#x2F;a&gt; and &lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust-2&quot;&gt;part 2&lt;&#x2F;a&gt;), a lot of people reached out with questions about what Hyperlight is in the first place. There&#x27;s not a lot of information out there about the project besides what I&#x27;ve written, what&#x27;s been presented at Microsoft Build (more on that in a second), and word-of-mouth, so I&#x27;m going to fill in the gaps here.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m going to take a break from the C# &amp;lt;-&amp;gt; Rust series (which I&#x27;ll resume soon!) to talk about Hyperlight in more detail. I&#x27;ll cover what it is, why we&#x27;re building it, and how it works.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;background&quot;&gt;Background&lt;&#x2F;h2&gt;
&lt;p&gt;At the 2023 Microsoft Build conference, &lt;a href=&quot;https:&#x2F;&#x2F;www.linkedin.com&#x2F;in&#x2F;markrussinovich&#x2F;&quot;&gt;Mark Russinovich&lt;&#x2F;a&gt; showed a &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Tz2SOjKZwVA&quot;&gt;demo of Hyperlight&lt;&#x2F;a&gt; in his Azure Innovations keynote. In that same keynote, he described Hyperlight as a solution for improving the management and security of &lt;a href=&quot;https:&#x2F;&#x2F;webassembly.org&quot;&gt;Web Assembly (Wasm)&lt;&#x2F;a&gt; workloads on Azure, and explained the motivation of the project as follows: &lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Wasm lacks the kind of isolation that we require for running a public cloud” &lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In this post, I’m going to detail what he means by that and describe Hyperlight in greater detail. This is the first of two posts, and in the second, I’ll dive into much greater detail on how it works. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;built-on-the-shoulders-of-giants&quot;&gt;Built on the shoulders of giants&lt;&#x2F;h2&gt;
&lt;p&gt;Hyperlight borrows from, and builds on, substantial prior art, and it’s important to not only credit the technologies on top of which we’ve built this project, but also to establish a common foundation before we delve deeper into the project itself.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;hypervisors&quot;&gt;Hypervisors&lt;&#x2F;h3&gt;
&lt;p&gt;One of the most important of these technologies is the hypervisor. Simply put, a hypervisor is a system, often built right into your operating system and hardware stack (but not always! Read more about the various kinds of hypervisors &lt;a href=&quot;https:&#x2F;&#x2F;www.geeksforgeeks.org&#x2F;hypervisor&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;), that can create virtual “machines” (Hypervisors are also often called Virtual Machines or VMs), or computing devices, on top of which you run some other piece of software. &lt;&#x2F;p&gt;
&lt;p&gt;These systems have a long &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Timeline_of_virtualization_development&quot;&gt;history&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;fullHtml&#x2F;10.1145&#x2F;3365199&quot;&gt;of&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;web.eecs.umich.edu&#x2F;~prabal&#x2F;teaching&#x2F;eecs582-w11&#x2F;readings&#x2F;Gol74.pdf&quot;&gt;research&lt;&#x2F;a&gt; and are one of the critical foundational technologies behind cloud computing. These days, VMs are flexible enough to run software ranging from the most demanding industrial workloads in the cloud to full-blown operating systems on a laptop for a consumer. In fact, I’m writing this article on Windows running in a virtual machine on my Mac as we speak! &lt;&#x2F;p&gt;
&lt;p&gt;Importantly for our purposes, hypervisors provide virtualized hardware devices -- including CPUs, hard disks, networking devices, and memory -- on which our “guest” software (the software that runs Wasm workloads; more on these mechanics below) runs. This hardware virtualization layer provides an important security isolation boundary we can use to guarantee a specific level of security to our users’ running applications. &lt;&#x2F;p&gt;
&lt;p&gt;VMs often ship with a lot of associated technologies, many of which we don’t use directly, but are important to understand for context. We’ll talk more about them when we dive deeper in part 2. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;rust&quot;&gt;Rust&lt;&#x2F;h3&gt;
&lt;p&gt;Another important technology we use is the &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.org&quot;&gt;Rust programming language&lt;&#x2F;a&gt;, or just “Rust”. I’ll talk less about Rust in this initial post in the series than in post 2, but I do want to cover one of Rust’s most important features for our use case: memory safety. &lt;&#x2F;p&gt;
&lt;p&gt;Traditionally, a limited set of programming languages were available to do systems programming – writing programs to interact directly with the computer’s hardware. The most popular were the C and C++ languages, and both required developers to manually manage the memory they wanted to use. Situations where a developer manages memory incorrectly – like accidentally trying to use it after they’ve already freed it – have led to &lt;a href=&quot;https:&#x2F;&#x2F;www.cisa.gov&#x2F;news-events&#x2F;news&#x2F;urgent-need-memory-safety-software-products&quot;&gt;many&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;msrc.microsoft.com&#x2F;blog&#x2F;2019&#x2F;07&#x2F;a-proactive-approach-to-more-secure-code&#x2F;&quot;&gt;security&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;www.chromium.org&#x2F;Home&#x2F;chromium-security&#x2F;memory-safety&#x2F;&quot;&gt;vulnerabilities&lt;&#x2F;a&gt; across the entire computer software industry, including in Microsoft software. &lt;&#x2F;p&gt;
&lt;p&gt;Problems like these have become so well-known and serious that our very own (and aforementioned) Mark Russinovich has &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;markrussinovich&#x2F;status&#x2F;1571995117233504257?lang=en&quot;&gt;tweeted&lt;&#x2F;a&gt; his support for deprecating use of C&#x2F;C++ in new projects in favor of Rust. He’s not alone; many large companies and well-known projects are &lt;a href=&quot;https:&#x2F;&#x2F;rustmagazine.org&#x2F;issue-1&#x2F;2022-review-the-adoption-of-rust-in-business&#x2F;&quot;&gt;investing in Rust&lt;&#x2F;a&gt; because it’s simultaneously a powerful systems programming language and a memory safe one. The way it achieves this memory safety is somewhat novel and very interesting but I don’t have space today to detail it. Anyway, with this powerful combination of features, Rust fills a niche not filled by nearly any other languages, including the mighty C and C++. &lt;&#x2F;p&gt;
&lt;p&gt;There’s much more to say about Rust and Hypervisors. We’ll discuss both at length in part 2 and beyond, but we have enough to set a foundation for part 1. Onward! &lt;&#x2F;p&gt;
&lt;h3 id=&quot;webassembly-is-executed-by-a-virtual-machine&quot;&gt;WebAssembly is executed by a virtual machine&lt;&#x2F;h3&gt;
&lt;p&gt;When we describe Hyperlight, we often need to differentiate between two different kinds of virtual machines (VMs). While we’ve described hypervisors above, we also need to consider the virtual machines that execute Wasm (called “Wasm VMs” hereafter)! Fortunately, there’s a very clear line between the two, and it’s all about how hardware is represented and utilized. &lt;&#x2F;p&gt;
&lt;p&gt;As mentioned in the previous section, hypervisor-based VMs provide a set of virtual hardware devices, but current Wasm VMs do not. Importantly, the latter provide something that looks like a virtual “CPU”, but it only executes the web assembly instruction format and provides no support for any hardware devices. There are APIs built atop Wasm, like WASI, that abstract over hardware devices (like doing network calls and file system accesses), but it is up to the user to implement these APIs in terms of any hardware they see fit. &lt;&#x2F;p&gt;
&lt;p&gt;In other words, Wasm VMs don’t provide hardware virtualization, while hypervisor-based VMs do. The latter generally provides additional flexibility and a different kind of security isolation boundary. &lt;&#x2F;p&gt;
&lt;p&gt;These differences also imply important differences in how these platforms execute code. Many (but not all) programs can be compiled to Wasm, and any system with a Wasm VM can then execute it, regardless of the underlying hardware architecture. If you’re familiar with .Net or the Java Virtual Machine (or JVM), you can think of Wasm similarly to how you think of CLR or JVM bytecode. Alternatively, I can compile any program directly to native code and run it directly on the hardware (or virtualized hardware!)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-vm-inside-a-vm&quot;&gt;A VM inside a VM?&lt;&#x2F;h2&gt;
&lt;p&gt;I just spent a whole section describing the difference between the Wasm VM and a Hypervisor-based VM, and you may be surprised to learn that Hyperlight runs a Wasm VM inside a hypervisor-based VM. Let’s discuss here why we use both. &lt;&#x2F;p&gt;
&lt;p&gt;If you recall Mark’s key comment in his Azure Innovations keynote, he said that while, Wasm provides an isolation scheme, it lacks the isolation we need to run a secure public cloud. This statement has several important points in it, and I want to elaborate on them. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-kind-of-isolation-wasm-does-have&quot;&gt;The kind of isolation Wasm does have&lt;&#x2F;h3&gt;
&lt;p&gt;We already discussed how Wasm and hypervisor-level isolation differ, so let’s discuss how those differences apply to the needs of a public cloud like Azure. You may not know that Microsoft’s online infrastructure is one of the most attacked in the world. This kind of malicious activity isn’t new to us, so we’ve been investing heavily in the security of our software for a long time. One of our most powerful tools to keep our systems secure, and maintain our customers’ trust, is &lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;blog&#x2F;microsoft-azures-defense-in-depth-approach-to-cloud-vulnerabilities&#x2F;&quot;&gt;defense-in-depth&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you haven’t heard of this defense-in-depth approach, it’s simply a way to ensure sensitive data and systems sit behind as many security boundaries as possible, while still ensuring suitable performance, efficiency, and more. We’re big believers in this approach at Microsoft, and we employ it wherever possible to achieve at least the following two major objectives: &lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;To present an attack surface for any would-be attackers that is ever more difficult to breach&lt;&#x2F;li&gt;
&lt;li&gt;To limit the scope (also called the “blast radius”) of any potential breach if one were to happen&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;While Wasm does provide a &lt;a href=&quot;https:&#x2F;&#x2F;www.usenix.org&#x2F;system&#x2F;files&#x2F;sec22-bosamiya.pdf&quot;&gt;provably safe virtualization technology&lt;&#x2F;a&gt;, it’s only “safe” if the engine we use to execute Wasm is always bug-free (it’s not). Since we usually can’t guarantee any software is bug-free (this is why we work to improve our software, software construction methods, and more!), we don’t want to rely only on a Wasm VM to provide the security boundaries we need, so we add another layer of a different type. This new layer is a hypervisor-based VM.&lt;&#x2F;p&gt;
&lt;p&gt;Now, we have two VMs, each possibly with bugs in them, with one running inside of the other. This arrangement ensures that security bugs in one VM have a lower impact because of the additional security boundary provided by the other VM layer. In other words, the impact of a security bug in one VM is reduced because of the additional security boundary provided by the other VM layer. &lt;&#x2F;p&gt;
&lt;h3 id=&quot;how-do-we-do-all-this&quot;&gt;How do we do all this?&lt;&#x2F;h3&gt;
&lt;p&gt;Well, as I mentioned at the beginning of this post, I’m going to save most of the “how” for part 2, but I want to wrap this post up with a preview. &lt;&#x2F;p&gt;
&lt;p&gt;Running software inside of a Wasm VM is relatively easy these days – there are several high quality Wasm VMs available (Microsoft uses both &lt;a href=&quot;https:&#x2F;&#x2F;wasmtime.dev&quot;&gt;Wasmtime&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bytecodealliance&#x2F;wasm-micro-runtime&quot;&gt;WAMR&lt;&#x2F;a&gt;, and Visual Studio Code uses &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;en&quot;&gt;Node&lt;&#x2F;a&gt;, backed by the &lt;a href=&quot;https:&#x2F;&#x2F;v8.dev&quot;&gt;V8 engine&lt;&#x2F;a&gt;), and most have very good APIs we can use to load and run a Wasm module. &lt;&#x2F;p&gt;
&lt;p&gt;Our challenge is twofold, as follows: &lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Bundle a Wasm VM and associated executable customer code (called a module), into a format that can run inside a hypervisor-based VM we build, and...&lt;&#x2F;li&gt;
&lt;li&gt;Build the aforementioned hypervisor-based VM that can load the Wasm VM + customer module, run a specified function, get the return value, and tear everything down, all with latency measured in microseconds (not milliseconds) &lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;As you’ll see in part 2, the first task is much simpler than the second from the above list, and we’ve spent a lot of time ensuring both low latencies and high “density”. One strategy we use to achieve both things, and reduce the attack surface even further, is to eliminate the operating system kernel entirely from the native code running inside the VM. If you’re familiar with &lt;a href=&quot;http:&#x2F;&#x2F;unikernel.org&quot;&gt;Unikernels&lt;&#x2F;a&gt;, you’ll notice some similarities. &lt;&#x2F;p&gt;
&lt;p&gt;I’m looking forward to continuing this discussion. More to come!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Calling Rust from C#, and back again: Part 2</title>
          <pubDate>Thu, 16 Nov 2023 21:58:32 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/csharp-rust-2/</link>
          <guid>/blog/csharp-rust-2/</guid>
          <description>&lt;p&gt;Back in &lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust&quot;&gt;part 1&lt;&#x2F;a&gt;, I talked about Hyperlight, its history, and some of the challenges involved with the project, including calling &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.org&quot;&gt;Rust&lt;&#x2F;a&gt; from &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;csharp&#x2F;&quot;&gt;C#&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;At the very end of that post, I promised another talking about the other direction -- calling C# from Rust. So, here we go!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-challenge&quot;&gt;The Challenge&lt;&#x2F;h2&gt;
&lt;p&gt;Fundamantally, we want to have C# call into Rust, which we covered in part 1, but in some cases, we want to pass a C# function to Rust, and have the Rust code call back into C#.&lt;&#x2F;p&gt;
&lt;p&gt;To do that second part -- the callback -- we have to dive into a topic the C#&#x2F;.Net world calls &lt;em&gt;unmanaged&lt;&#x2F;em&gt;. For our purposes, &amp;quot;unmanaged&amp;quot; means C APIs&#x2F;ABIs implemented with Rust. The process to make C# code call unmanaged code is well documented, and the C# compiler and .Net runtime give us explicit support for doing so.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s not much of a stretch to go one step further and &lt;em&gt;implement&lt;&#x2F;em&gt; those C APIs with Rust. We essentially use C as an abstraction layer between C# and Rust, and it works remarkably well.&lt;&#x2F;p&gt;
&lt;p&gt;On the other hand, it&#x27;s far less clear how to get that callback functionality working. We have to put together several different C# and .Net features and wire those things up with Rust to make this work, and I&#x27;ll show herein how to do it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pieces-of-the-puzzle&quot;&gt;Pieces of the puzzle&lt;&#x2F;h2&gt;
&lt;p&gt;The process to get this callback system working is three-fold, as follows:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Convert a C# function (usually called a &amp;quot;delegate&amp;quot;) into a C function pointer&lt;&#x2F;li&gt;
&lt;li&gt;Consume that C function pointer in Rust and call it&lt;&#x2F;li&gt;
&lt;li&gt;Finally, we need to clean up the function pointer&#x27;s memory when we&#x27;re done with it&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;I&#x27;ll describe these steps below, each in more detail.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;converting-a-c-delegate-to-a-c-function-pointer&quot;&gt;Converting a C# delegate to a C function pointer&lt;&#x2F;h2&gt;
&lt;p&gt;In this first step, the overall goal is to convert a C# &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;csharp&#x2F;programming-guide&#x2F;delegates&#x2F;&quot;&gt;&lt;code&gt;delegate&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; into an unmanaged function pointer. &amp;quot;Unmanaged function pointers&amp;quot; in C# roughly correspond to C function pointers. Given a C# &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;api&#x2F;system.action-1?view=net-7.0&quot;&gt;&lt;code&gt;Action&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; -- a function closure with no return value -- the code for converting it to a function pointer isn&#x27;t obvious, but relatively straightforward, as shown below.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c#&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-c# &quot;&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the action we want to convert to a C-compatible function pointer.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;Action&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myAction &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myBool&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;=&amp;gt; &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;WriteLine&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myBool&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;};
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; establish a delegate type, and convert our action to it
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;private delegate void &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MyDelegate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;bool &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;param&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;MyDelegate &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionWrapper &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myAction&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; then tell the .Net runtime, including the garbage collector, to &amp;quot;forget&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; about this function pointer. we need to do this so we can pass the 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; function pointer to Rust, and be sure that C# won&amp;#39;t delete it at some
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; point before Rust needs it.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;GCHandle &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionHdl &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;GCHandle&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Alloc&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionWrapper&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; finally, convert our delegate to a function pointer.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;IntPtr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionPtr &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Marshal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;GetFunctionPointerForDelegate&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;MyDelegate&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;(
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionWrapper
&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we can now pass myActionPtr to Rust via a C API
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This code could also work with C# &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;api&#x2F;system.func-2?view=net-7.0&quot;&gt;&lt;code&gt;Func&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;s, which are function closures with return values. In either case, you have to ensure your parameter types and return types (where applicable) are compatible with C.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;passing-the-c-generated-function-pointer-to-rust&quot;&gt;Passing the C#-generated function pointer to Rust&lt;&#x2F;h2&gt;
&lt;p&gt;Now, in Rust-land, we have to accept the &lt;code&gt;IntPtr myActionPtr&lt;&#x2F;code&gt; we just created in C#-land. Rust has very good, nearly zero-cost &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; support for both calling C functions and defining C-callable functions, but this topic is nevertheless very nuanced.&lt;&#x2F;p&gt;
&lt;p&gt;Collectively these features are called &amp;quot;FFI&amp;quot; (foreign-function interface), but we&#x27;re interested in only the latter feature. Defining a function callable by C code is as simple as the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;&#x2F; accept a boolean and print it out
&lt;&#x2F;span&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;no_mangle&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;C&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;my_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;val&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;println!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;hello! the given value is &lt;&#x2F;span&gt;&lt;span style=&quot;color:#aeb52b;&quot;&gt;{}&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, val);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Take note of a few important highlights of this function prototype.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - by default, &lt;code&gt;rustc&lt;&#x2F;code&gt; (the Rust compiler) reserves the right to &amp;quot;mangle&amp;quot; the names of functions, methods, and other types. The reasons for mangling are beyond the scope of this article, but we want to turn this feature off for this function so we can call it from C code&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;extern &amp;quot;C&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;strong&gt; - this nomenclature tells Rust to generate code for this function that adheres to the C &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Calling_convention&quot;&gt;calling convention&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;You can read more about calling conventions in the &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;nomicon&#x2F;ffi.html#foreign-calling-conventions&quot;&gt;Rust nomicon&lt;&#x2F;a&gt;. &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Argument and return types&lt;&#x2F;strong&gt; - the Rust compiler will make sure all types in the public function prototype -- parameters and return values -- are FFI-compatible. This feature is invaluable for writing FFI functions in Rust because you can be sure your FFI function is at least technically callable &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; from C if it compiles.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Header file and linkage&lt;&#x2F;strong&gt; - The above function prototype tells the compiler what to output for that function, but doesn&#x27;t guarantee C code can compile or link against it. I can&#x27;t go into more detail here without doubling the length of this post, but here&#x27;s the very high-level way to get that working:
&lt;ul&gt;
&lt;li&gt;Use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;mozilla&#x2F;cbindgen&quot;&gt;cbindgen&lt;&#x2F;a&gt; to generate C header files from your &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;&#x2F;code&gt; functions&lt;&#x2F;li&gt;
&lt;li&gt;Set your crate type to &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;reference&#x2F;linkage.html&quot;&gt;&lt;code&gt;cdylib&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; so your C programs can link against your code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;functions-as-parameters&quot;&gt;Functions as parameters&lt;&#x2F;h4&gt;
&lt;p&gt;Armed with this ability to define C functions in Rust, we can now create higher-order functions as follows:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;C&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;outer_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;inner_func&lt;&#x2F;span&gt;&lt;span&gt;: extern &amp;quot;C&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;bool&lt;&#x2F;span&gt;&lt;span&gt;)) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we likely want to save our inner_func value for later usage, but
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; that&amp;#39;s outside the scope.
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we can effectively call into C# by simply calling inner_func:
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;inner_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;inner_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;cleaning-everything-up&quot;&gt;Cleaning everything up&lt;&#x2F;h2&gt;
&lt;p&gt;Cleaning up Rust-owned function pointers generated from C# can be a complex topic that requires some care.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rust-vs-c-ownership&quot;&gt;Rust vs. C# ownership&lt;&#x2F;h4&gt;
&lt;p&gt;First, Rust doesn&#x27;t truly &amp;quot;own&amp;quot; the function pointer, since it can&#x27;t reliably and correctly clean up its backing memory (only C# can do that). This fact implies we need to make sure Rust is &amp;quot;done&amp;quot; with it before cleaning it up in C#. There are a lot of different possible ways to do that, varying from just &amp;quot;eyeballing it&amp;quot; to using reference-counting or other more formalized strategies. Inside Hyperlight, for example, we use a home-grown system vaguely similar to &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Reference_counting&quot;&gt;reference counting&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;c-cleanup-code&quot;&gt;C# cleanup code&lt;&#x2F;h4&gt;
&lt;p&gt;Next, after we&#x27;re sure Rust is &amp;quot;done&amp;quot; with the function pointer, we can clean it up in C#. Recall in the first step (&amp;quot;Converting a C# delegate to a C function pointer&amp;quot;, above) we called &lt;code&gt;GCHandle.Alloc&lt;&#x2F;code&gt; to get the garbage collector to forget a &lt;code&gt;delegate&lt;&#x2F;code&gt;, which means we need to manually clean it up. The following code does the cleanup work:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;c#&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-c# &quot;&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; First, if the type to which myActionHdl points is an IDisposable, clean
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; that up by calling Dispose() on it
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tgt &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionHdl&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Target &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;as &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;IDisposable&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;null &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tgt&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;tgt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Dispose&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Finally, free the actual memory for the delegate to which myActionHdl points
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myActionHdl&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Free&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Recall that &lt;code&gt;myActionHdl&lt;&#x2F;code&gt; is a &lt;code&gt;GCHandle&lt;&#x2F;code&gt; returned from &lt;code&gt;GCHandle.Alloc&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;caveat-closures&quot;&gt;Caveat: closures&lt;&#x2F;h4&gt;
&lt;p&gt;And finally, a big caveat. The C# &lt;code&gt;delegate&lt;&#x2F;code&gt; from which we created &lt;code&gt;myActionHdl&lt;&#x2F;code&gt; is a &lt;em&gt;closure&lt;&#x2F;em&gt;, which means it can &lt;em&gt;close over&lt;&#x2F;em&gt; and&#x2F;or reference memory outside of its definition. In either case, the garbage collector may still clean up that memory.&lt;&#x2F;p&gt;
&lt;p&gt;If that happens, we&#x27;ll run into use-after-free issues that will likely be very difficult to debug, since the function is invoked from Rust. There is no perfect way to solve this issue, since we&#x27;re fundamentally bridging two completely different environments (C#&#x2F;.Net and Rust) over a low-fidelity abstraction (C), but once again, reference counting can help here.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;Putting the concepts from &lt;a href=&quot;&#x2F;blog&#x2F;csharp-rust&quot;&gt;part 1&lt;&#x2F;a&gt; and this post together, we now have the tools to make C# code call Rust and vice-versa. If you build out a joint C# and Rust codebase, you&#x27;ll likely encounter several practical issues, the biggest of which will likely be memory management.&lt;&#x2F;p&gt;
&lt;p&gt;Throughout this post, I showed how to manually reconcile the .Net memory management system -- the garbage collector -- with the Rust one -- ownership, borrowing and deterministically freeing memory without any runtime overhead. Doing this reconciliation usually requires a lot of error-prone work, so in many cases it&#x27;s helpful to build a memory management system to unify the two environments.&lt;&#x2F;p&gt;
&lt;p&gt;As I implied several times herein, Hyperlight uses a system that uses something similar to reference-counting to do so. Stay tuned for part 3, where I&#x27;ll detail that system.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#1&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; Rust&#x27;s foreign-function-interface (FFI; basically, its ability to call C code) is officially a &lt;a href=&quot;https:&#x2F;&#x2F;doc.rust-lang.org&#x2F;beta&#x2F;embedded-book&#x2F;static-guarantees&#x2F;zero-cost-abstractions.html&quot;&gt;zero-cost abstraction&lt;&#x2F;a&gt; (also see &lt;a href=&quot;https:&#x2F;&#x2F;blog.rust-lang.org&#x2F;2015&#x2F;04&#x2F;24&#x2F;Rust-Once-Run-Everywhere.html#:~:text=To%20communicate%20with%20other%20languages,performance%20to%20C%20function%20calls.&quot;&gt;this document&lt;&#x2F;a&gt;), but for many practical applications of &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;&#x2F;code&gt; functions, you will need to do additional memory allocations, which incur a cost.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#2&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; There is unfortunately a big distance between theory and practice in this case but regardless, knowing your Rust code can theoretically be called from C is a big step forward.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Calling Rust from C#, and back again: Part 1</title>
          <pubDate>Thu, 09 Nov 2023 21:58:32 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/csharp-rust/</link>
          <guid>/blog/csharp-rust/</guid>
          <description>&lt;p&gt;I&#x27;m working on a project at Microsoft called Hyperlight. The project is all about making very small-footprint virtual machines that start up specific kinds of applications very quickly. At a very high level, we&#x27;re using modern virtualization technology and some concepts from Unikernels to make these things happen.&lt;&#x2F;p&gt;
&lt;p&gt;Mark Russinovich highlighted the project at &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Tz2SOjKZwVA&quot;&gt;his keynote at Microsoft Build 2023&lt;&#x2F;a&gt;, so if you want more background on the project, go &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=Tz2SOjKZwVA&quot;&gt;watch that keynote&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The project was prototyped initially with C#, but it turns out C# isn&#x27;t the best choice for a variety of reasons. Two of the biggest of those reasons are as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It takes care and effort to call native code, like &lt;code&gt;ioctl&lt;&#x2F;code&gt; system calls, from C# (or really, all .Net) applictions&lt;&#x2F;li&gt;
&lt;li&gt;It also takes care and effort to deal with memory not controlled by .Net -- often called &lt;em&gt;unmanaged memory&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;background-rewriting-c-in-rust&quot;&gt;Background: Rewriting C# in Rust&lt;&#x2F;h2&gt;
&lt;p&gt;In response to these challenges (and more), we decided to move the project to &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.org&quot;&gt;Rust&lt;&#x2F;a&gt; because it&#x27;s a much better fit for the needs of the project. Rust is a fast, safe, and modern systems programming language, and a great fit for this project.&lt;&#x2F;p&gt;
&lt;p&gt;We chose to move the C# codebase incrementally to Rust, and wanted to end up with both a C# and Rust SDK, with the former being a thin wrapper around the latter. Ironically, this work forced us to write a lot of temporary C# code that did precisely the kinds of things we were trying to avoid, like calling into native code and dealing with unmanaged memory.&lt;&#x2F;p&gt;
&lt;p&gt;As the rewrite progressed, we were able to remove some of that code, but two things became clear as we began to understand the mechanics of the final joint C#&#x2F;Rust codebase:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;We indeed would always need to call &amp;quot;system calls&amp;quot; (sometimes called &lt;a href=&quot;https:&#x2F;&#x2F;learn.microsoft.com&#x2F;en-us&#x2F;dotnet&#x2F;standard&#x2F;native-interop&#x2F;pinvoke&quot;&gt;P-Invoke&lt;&#x2F;a&gt; in C#) from C#, since that&#x27;s how the Rust APIs are exposed&lt;&#x2F;li&gt;
&lt;li&gt;In some cases, we&#x27;d need to go the other way and &lt;em&gt;call C# functions from Rust&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;In our experience so far, (1) is relatively well defined, but (2) is not. We figured out how to do both in a somewhat robust and reliable way. In this post, In this post, I&#x27;ll describe (1), and save (2) for a follow-up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;calling-rust-from-c&quot;&gt;Calling Rust from C#&lt;&#x2F;h2&gt;
&lt;p&gt;C# was designed from the start to be able to call &amp;quot;native&amp;quot; APIs, mainly so developers could still integrate with the many C&#x2F;C++ APIs already present in Windows. This feature is really useful for us, because we can implement some Rust code, expose it as a C API, then call it from C#.&lt;&#x2F;p&gt;
&lt;p&gt;Assuming we design this system carefully, the C# code won&#x27;t know it&#x27;s calling Rust code, and the Rust code won&#x27;t know it&#x27;s being called from C#. Decoupling is a beautiful thing!&lt;&#x2F;p&gt;
&lt;p&gt;In other words, C# can call C APIs [1] and Rust can produce C-compatible APIs, so on either side of the &amp;quot;fence&amp;quot;, we just need to figure out how to deal with C-compatible APIs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;creating-c-apis-in-rust&quot;&gt;Creating C APIs in Rust&lt;&#x2F;h3&gt;
&lt;p&gt;Rust has strong support for creating C-compatible APIs [2]. While there are a few gotchas, if you can compile Rust code like the following, it will be callable by C code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;rust&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-rust &quot;&gt;&lt;code class=&quot;language-rust&quot; data-lang=&quot;rust&quot;&gt;&lt;span&gt;#[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;no_mangle&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;pub &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;extern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;C&amp;quot; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;fn &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;callable_from_c&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;i&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;i64&lt;&#x2F;span&gt;&lt;span&gt;) -&amp;gt; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;i64 &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    i&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;+&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s not a lot of code here, but there are some important features to note, as follows:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;#[no_mangle]&lt;&#x2F;code&gt; is important, because it ensures the Rust compiler doesn&#x27;t modify (&amp;quot;mangle&amp;quot;) the name, so C code can call it without understanding the internals of Rust &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;rfcs&#x2F;2603-rust-symbol-name-mangling-v0.html&quot;&gt;name mangling&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;&#x2F;code&gt; is also important, because it tells the Rust compiler to produce a C-compatible ABI for the function, so C code can call it without understanding the internals of Rust &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Application_binary_interface&quot;&gt;ABIs&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Finally, the types of parameters and return types must all be &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt; is code you put atop a Rust type that tells the Rust compiler the type must be binary-compatible with C code. It will not work on all types, but if you do use it and your code compiles, it&#x27;s guaranteed to work&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Rust pointers (not references!) are compatible with C pointers, so if you have a type that is not &lt;code&gt;#[repr(C)]&lt;&#x2F;code&gt;, you can use a pointer to it in your C-compatible API
&lt;ul&gt;
&lt;li&gt;Warning: most of the Rust code you&#x27;ll write that deals with pointers in any meaningful way, will be &lt;code&gt;unsafe&lt;&#x2F;code&gt;!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Once you have a bunch of these C-compatible functions, you need to put them in a library crate that &lt;em&gt;only&lt;&#x2F;em&gt; has type &lt;code&gt;cdylib&lt;&#x2F;code&gt;, like the following:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;toml&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-toml &quot;&gt;&lt;code class=&quot;language-toml&quot; data-lang=&quot;toml&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;lib&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;name &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;my_c_lib&amp;quot;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;crate-type &lt;&#x2F;span&gt;&lt;span&gt;= [&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;cdylib&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: if you have a &lt;code&gt;cdylib&lt;&#x2F;code&gt; crate, you can&#x27;t also compile it as a &amp;quot;standard&amp;quot; Rust crate. If you want one of those too (hint: you probably will, eventually!), split your C-compatible code into a separate crate&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;calling-c-apis-from-c&quot;&gt;Calling C APIs from C#&lt;&#x2F;h3&gt;
&lt;p&gt;The other side of this is calling C APIs from C#, which as I mentioned above is a well-known feature of C#. There are a lot of resources out there (reminder: search for &amp;quot;P-Invoke&amp;quot;), but I&#x27;ll just give a quick example here:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;cs&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-cs &quot;&gt;&lt;code class=&quot;language-cs&quot; data-lang=&quot;cs&quot;&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;DllImport&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;my_c_lib&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;SetLastError &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ExactSpelling &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; You may need this, depending on how your code searches for shared libraries
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; (i.e. DLLs on Windows or shared objects on Linux).
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; If you do use it, the C# compiler will throw warnings 
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;DefaultDllImportSearchPaths&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;DllImportSearchPath&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;AssemblyDirectory&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;return&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;MarshalAs&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;UnmanagedType&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;U1&lt;&#x2F;span&gt;&lt;span&gt;)]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; An i64 in Rust corresponds to a long in C#
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;static &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;extern &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;long callable_from_c&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;long input&lt;&#x2F;span&gt;&lt;span style=&quot;background-color:#562d56bf;color:#f8f8f8;&quot;&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;calling-c-from-rust&quot;&gt;Calling C# from Rust&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;ve written a lot here, but believe it or not, we only covered the first half of this process. Going the other way -- calling C# from Rust -- involves more details, including some .Net runtime internals. I will cover those details in my next post. See you then!&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;[1] Technically, the C# compiler knows how to call C APIs, and the runtime knows how to call C &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Application_binary_interface&quot;&gt;ABI&lt;&#x2F;a&gt;s&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;[2] Similar to what the C# compiler does for calling C API&#x2F;ABIs, the Rust compiler knows how to expose ABI-compatible C APIs&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Synchronizing the KEDA HTTP Addon Request Routing Table Across Hundreds of Interceptor Pods</title>
          <pubDate>Fri, 29 Oct 2021 21:58:32 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/keda-http-addon-routing-table/</link>
          <guid>/blog/keda-http-addon-routing-table/</guid>
          <description>&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&quot;&gt;KEDA HTTP Addon project&lt;&#x2F;a&gt; contains three major components: the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&#x2F;tree&#x2F;main&#x2F;operator&quot;&gt;operator&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&#x2F;tree&#x2F;main&#x2F;scaler&quot;&gt;scaler&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&#x2F;tree&#x2F;main&#x2F;interceptor&quot;&gt;interceptor&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Of these, the interceptor is the only component that sits in the critical path of all incoming HTTP requests. We also run them in a &lt;em&gt;fleet&lt;&#x2F;em&gt; that is horizontally scaled by software.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re going to focus on how we ensure that any interceptor replica can route an incoming request to the correct backing application at any time.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;implications-of-multi-tenancy&quot;&gt;Implications of Multi-Tenancy&lt;&#x2F;h2&gt;
&lt;p&gt;The interceptor component is designed to run in a &lt;code&gt;Deployment&lt;&#x2F;code&gt; that KEDA automatically scales. This high-level design has a few implications:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;There must be a centralized, durable copy of a lookup table -- called a &lt;em&gt;routing table&lt;&#x2F;em&gt; -- that maps any incoming request to the correct backing &lt;code&gt;Service&lt;&#x2F;code&gt; and port.&lt;&#x2F;li&gt;
&lt;li&gt;All interceptor pods must reliably stay up to date with the central routing table&lt;&#x2F;li&gt;
&lt;li&gt;All interceptor pods must be able to handle any valid incoming request, regardless of application it was intended for&lt;&#x2F;li&gt;
&lt;li&gt;All interceptor pods must be able to quickly execute lookups to the routing table&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;keeping-up-to-date-with-the-routing-table&quot;&gt;Keeping up to Date with the Routing Table&lt;&#x2F;h2&gt;
&lt;p&gt;Since the interceptor needs to do a lookup to the routing table before forwarding any request (or returning an error code), lookups need to be as fast as possible. That means storing the routing table in memory and keeping each interceptor&#x27;s in-memory copy up to date with the central copy.&lt;&#x2F;p&gt;
&lt;p&gt;We do this wth a relatively simple event loop, outlined in the below (&lt;a href=&quot;https:&#x2F;&#x2F;golang.org&quot;&gt;Go&lt;&#x2F;a&gt;-like) pseudocode:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fetch_table_from_kubernetes&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;report_alive&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ticker &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;start_ticker&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;every_1_second&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;new_table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;kubernetes_events_chan&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;new_table
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ticker&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;table &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fetch_table_from_kubernetes&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few important points to note about this event loop:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;On startup, we fetch a complete copy of the routing table from Kubernetes before we report to the &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;tasks&#x2F;configure-pod-container&#x2F;configure-liveness-readiness-startup-probes&#x2F;&quot;&gt;Kubernetes liveness probe&lt;&#x2F;a&gt;. This means that Kubernetes doesn&#x27;t consider any interceptor pod &amp;quot;ready&amp;quot; until it has a complete initial copy of the routing table.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;kubernetes_events_chan&lt;&#x2F;code&gt; receives notifications about changes to the central routing table in near-real time. When we get a change notification, we immediately update the in-memory copy.&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;ticker&lt;&#x2F;code&gt; fires a signal every second, at which time we do a full refresh of the routing table. This mechanism ensures that all interceptor replicas receive changes to the routing table within 1 second of the change being made to the central copy.&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;the-event-loop-pattern-in-action&quot;&gt;The Event Loop Pattern in Action&lt;&#x2F;h2&gt;
&lt;p&gt;The KEDA HTTP Addon runs a fleet of interceptors and enlists KEDA to actively scale the fleet. This means that as we send more HTTP traffic to the cluster, we expect the interceptors to automatically scale up. This behavior is one of the most important features of the HTTP Addon.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ve built this event loop into the interceptors to ensure that there can be thousands [1] of them running at once, and they all stay up to date with the central routing table -- data that they need to do their job.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;[1] As we see in the pseudocode above, each interceptor issues requests to the Kubernetes API, so as we scale them out, we generate more consistent traffic to the cluster API. As you scale further than the low thousands of replicas, you would need to add an intermediate layer of caching between the interceptors and the API to ensure that you don&#x27;t crash the cluster control plane. &lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Fan-in and fan-out with Go</title>
          <pubDate>Mon, 11 Oct 2021 19:46:49 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/fanin-fanout/</link>
          <guid>/blog/fanin-fanout/</guid>
          <description>&lt;p&gt;Hacking on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&quot;&gt;KEDA HTTP Addon&lt;&#x2F;a&gt;, I found myself having to do something familiar:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Split some work into N pieces, do them all concurrently, wait for them all to be done, and then merge all the results together.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;ve done this a bunch of times before, but this time I forgot how to do it. I took a few minutes away from the computer to gather my thoughts and came back to it. So I don&#x27;t have to forget how to do it again, I want to write the algorithm down here!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-are-we-doing-here&quot;&gt;What are we doing here?&lt;&#x2F;h2&gt;
&lt;p&gt;First thing&#x27;s first - we need a problem we can break down into a bunch of pieces. Sometimes it&#x27;s called an &amp;quot;embarassingly parallel&amp;quot; problem. &lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that concurrency and parallelism aren&#x27;t equivalent, but I&#x27;m going to be using the word &amp;quot;parallel&amp;quot; hereafter because I&#x27;m hoping the machine you run this algorithm on will be able to execute the work on different cores simultaneously.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The primary goal here is to run each different piece of the work in a goroutine. That&#x27;s pretty easy in Go - just put &lt;code&gt;go&lt;&#x2F;code&gt; before the function call that does the work. The tougher part is to get the results of the work, check for errors, and wait for them all to be done -- not necessarrily in that order 🤣.&lt;&#x2F;p&gt;
&lt;p&gt;Even though the concept is simple, there is a big-ish gotcha when the rubber hits the road. Below is some code that does &amp;quot;fake&amp;quot; work, annotated with comments to explain it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;do&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;some&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;work&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; make sure you pass the index and work into the 
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; function that runs in the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this mechanism makes sure that the goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gets a (stack-allocated) _copy_ of the data.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; if you don&amp;#39;t do this, idx and work will change out
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; from under it as the loop progresses.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The biggest gotcha is in that comment inside the &lt;code&gt;for&lt;&#x2F;code&gt; loop.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;waiting-for-the-work-to-be-done&quot;&gt;Waiting for the work to be done&lt;&#x2F;h2&gt;
&lt;p&gt;Now that we&#x27;ve got goroutines running with the right parameters, let&#x27;s add a &lt;a href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;sync?utm_source=godoc#WaitGroup&quot;&gt;&lt;code&gt;sync.WaitGroup&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to the mix. This mechanism will let us wait for all these goroutines to finish.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;WaitGroup
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;do&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;some&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;work&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; add 1 to the waitgroup _before_ you start the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you want to do this in the same goroutine as where
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you call wg.Wait() so that you&amp;#39;re sure that, even if
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; none of the goroutines started yet, you have the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; right number of pending work.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; make sure you pass the index and work into the 
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; function that runs in the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this mechanism makes sure that the goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gets a (stack-allocated) _copy_ of the data.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; if you don&amp;#39;t do this, idx and work will change out
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; from under it as the loop progresses.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; wg.Done() tells the WaitGroup that we&amp;#39;re done in
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this goroutine. In other words, it decrements
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the internal WaitGroup counter, whereas wg.Add(1)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; above increments it.
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Most commonly, we just do this in a defer statement.
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the &amp;quot;work&amp;quot;. in the next section, we&amp;#39;ll be
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; changing this to return a value, because we&amp;#39;ll
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; need to send that value somewhere
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    }(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; wait for all the goroutines to finish. this call
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; blocks until the WaitGroup&amp;#39;s internal count goes 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; to zero
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;getting-the-results&quot;&gt;Getting the results&lt;&#x2F;h2&gt;
&lt;p&gt;So, now we know when all the work is done, we need to get the results. There are two kinds of results we need to get - the actual values of the work we&#x27;re doing -- we&#x27;ll call this the &amp;quot;success value&amp;quot; -- and the errors that it might have returned.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s focus on the success values first. We&#x27;re going to use one group of channels, one &amp;quot;final&amp;quot; channel, and a clever way of shuttling data between the former and the latter:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Read this code on the &lt;a href=&quot;https:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;CM34_zkrmrg&quot;&gt;Go Playground&lt;&#x2F;a&gt;, if you prefer&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the channel that will hold the results of the work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;WaitGroup
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;do&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;some&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;work&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; add 1 to the waitgroup _before_ you start the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you want to do this in the same goroutine as where
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you call wg.Wait() so that you&amp;#39;re sure that, even if
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; none of the goroutines started yet, you have the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; right number of pending work.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the loop-local channel that our first goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will send its results to. we&amp;#39;ll start up a second
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; goroutine to forward its results to the final channel.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; make sure you pass the index and work into the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; function that runs in the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this mechanism makes sure that the goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gets a (stack-allocated) _copy_ of the data.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; if you don&amp;#39;t do this, idx and work will change out
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; from under it as the loop progresses.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the &amp;quot;work&amp;quot;. right now, it just returns an
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; int. in the next section, it will return both an int
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; and an error
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;doSomeWork&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res
&lt;&#x2F;span&gt;&lt;span&gt;    }(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; start up another goroutine to forward the results from
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; ch to resultCh
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we want to indicate that we&amp;#39;re done after we forward
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the result to the final channel, _not_ just when we&amp;#39;re
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; done with the actual computation. this arrangement
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will be useful below, in our final goroutine that
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; runs after the for loop is done
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res
&lt;&#x2F;span&gt;&lt;span&gt;    }()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; start up a final goroutine that is going to watch for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the moment when all of the loop goroutines are both
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 1. done with their work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 2. done sending their results to the final channel
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; after that, we can close the resultCh. this closure is
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; important for the following for loop, since ranging over
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; a channel will only stop after that channel is closed
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; now that we have that final goroutine running, we can
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; be sure that this for loop will end after:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 1. all goroutines are done with their work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 2. all goroutines are done sending their work to resultCh
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 3. we have processed each result
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;  (in this case, we just print it out)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;result &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;result:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The code is extensively commented, but notice a few more &amp;quot;meta&amp;quot; things about it:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;re enlisting &lt;em&gt;another&lt;&#x2F;em&gt; goroutine for each loop iteration, so now we&#x27;re using &lt;code&gt;2N&lt;&#x2F;code&gt; goroutines rather than &lt;code&gt;N&lt;&#x2F;code&gt; (where &lt;code&gt;N&lt;&#x2F;code&gt; is the number of work items to do).
&lt;ul&gt;
&lt;li&gt;If you&#x27;re worried about the extra goroutines, remember that a Go program can run hundreds of thousands of them comfortably on a relatively modern laptop. Plan accordingly with that in mind.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;We use one extra final goroutine to determine when the final goroutine should be closed&lt;&#x2F;li&gt;
&lt;li&gt;We no longer use &lt;code&gt;wg.Wait()&lt;&#x2F;code&gt; in the main goroutine. Instead, we range over &lt;code&gt;resultCh&lt;&#x2F;code&gt; to both get the results &lt;em&gt;and&lt;&#x2F;em&gt; determine when all the work items are done.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;a-final-wrinkle-error-handling&quot;&gt;A final wrinkle: error handling&lt;&#x2F;h2&gt;
&lt;p&gt;Now that you (hopefully) have a decent grasp over the code in the previous section, consider that, for most workloads, you&#x27;ll also have to deal with error handling. It doesn&#x27;t take a lot of &lt;em&gt;additional&lt;&#x2F;em&gt; code to do it, but it does add a bit more complexity. Let&#x27;s see how it works:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Read this code on the &lt;a href=&quot;https:&#x2F;&#x2F;play.golang.org&#x2F;p&#x2F;Bcv_XQwoAi6&quot;&gt;Go Playground&lt;&#x2F;a&gt;, if you prefer&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note that you can reduce complexity by using the &lt;a href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;golang.org&#x2F;x&#x2F;sync&#x2F;errgroup&quot;&gt;&lt;code&gt;errgroup&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package. The code herein implements functionality similar to that of &lt;code&gt;errgroup&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the channel that will hold the results of the work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this channel receives all the errors that occur.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; for each work item, either resultCh or errCh will receive
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; precisely once. both channels will be closed immediately
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; after all receives happen
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;var &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg sync&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;WaitGroup
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;do&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;some&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;work&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= range &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;workToDo &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; add 1 to the waitgroup _before_ you start the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you want to do this in the same goroutine as where
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; you call wg.Wait() so that you&amp;#39;re sure that, even if
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; none of the goroutines started yet, you have the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; right number of pending work.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Add&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the loop-local channel that our first goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will send its results to. we&amp;#39;ll start up a second
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; goroutine to forward its results to the final channel.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the loop-local channel that our first goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; will send errors on. for each loop iteration, exactly
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; one of ch or errCh will receive
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;eCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;make&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;chan &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;error&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; make sure you pass the index and work into the
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; function that runs in the goroutine.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this mechanism makes sure that the goroutine
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; gets a (stack-allocated) _copy_ of the data.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; if you don&amp;#39;t do this, idx and work will change out
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; from under it as the loop progresses.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;string&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; this is the &amp;quot;work&amp;quot;. right now, it just returns an
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; int. in the next section, it will return both an int
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; and an error
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;doSomeWork&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;!= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;nil &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;eCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;work&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; start up another goroutine to forward the results from:
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; - ch to resultCh
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; - eCh to errCh
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we want to indicate that we&amp;#39;re done after we do the
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; forward operation, similar to the code in the
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; previous section
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;defer &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Done&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; only one forward operation will happen per loop
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; iteration, so we use a select to choose exactly
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; one of the channels - either the success or error
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; one.
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;ch&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;eCh&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;                &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errCh &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;lt;- &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }()
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; start up a final goroutine that is going to watch for
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the moment when all of the loop goroutines are both
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 1. done with their work
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; 2. done sending their results to the appropriate channel
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; after that, we can close both resultCh and errCh.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;go &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;wg&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Wait&lt;&#x2F;span&gt;&lt;span&gt;()
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errCh&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we&amp;#39;re now at a point where we have two &amp;quot;final&amp;quot; channels:
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; - one for the successful results
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; - one for the errors
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we have a few choices on how to handle them, and it&amp;#39;s
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; largely up to your use case how you handle errors or success
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; results. In our case, we&amp;#39;ll loop through both channels,
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; print out the result either way, and then exit when all
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; receives happen.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; these two booleans are going to keep track of when 
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; each channel is closed and done receiving
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultsDone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errsDone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;false
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we&amp;#39;re going to use an infinite loop and break out of it
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; when both channels are done receiving
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;for &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultsDone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;&amp;amp;&amp;amp; &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errsDone &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;break
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;select &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;valid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultCh&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;valid &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;resultsDone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;result:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;res&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;case &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;valid &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &amp;lt;-&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errCh&lt;&#x2F;span&gt;&lt;span&gt;:
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;if !&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;valid &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;errsDone &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true
&lt;&#x2F;span&gt;&lt;span&gt;        } &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;else &lt;&#x2F;span&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;fmt&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Println&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;error:&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;err&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        }
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;A few more things to note here:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;ve handled errors and success values with equal importance. In many cases, you might want to immediately exit if you see an error. In that case, make sure that you find a way to receive the rest of the errors and success values on &lt;code&gt;errCh&lt;&#x2F;code&gt; and &lt;code&gt;resultCh&lt;&#x2F;code&gt; (respectively), or tell the remaining goroutines to exit.
&lt;ul&gt;
&lt;li&gt;If you intend to do the latter, I highly recommend using &lt;a href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;context&quot;&gt;&lt;code&gt;context&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;There is a lot of code here! And for that reason, it&#x27;s worth repeating that you can reduce complexity by using the &lt;a href=&quot;https:&#x2F;&#x2F;pkg.go.dev&#x2F;golang.org&#x2F;x&#x2F;sync&#x2F;errgroup&quot;&gt;&lt;code&gt;errgroup&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; package.&lt;&#x2F;li&gt;
&lt;li&gt;The primary source of complexity is the parallelism (that&#x27;s the reason this blog post exists!) If you&#x27;re thinking of using this pattern, I encourage you to measure the serial (non-parallel) version of the algorithm first to determine whether you really need to take on this complexity&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>GopherCon 2021 CFP OMG!</title>
          <pubDate>Wed, 07 Apr 2021 11:06:38 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/gc-21-cfp/</link>
          <guid>/blog/gc-21-cfp/</guid>
          <description>&lt;p&gt;Hi Gophers, it&#x27;s your friendly co-chair of the GopherCon program committee!&lt;&#x2F;p&gt;
&lt;p&gt;For the past &lt;a href=&quot;&#x2F;blog&#x2F;gc-21-cfp&#x2F;.&#x2F;gc-cfp-2020&quot;&gt;two&lt;&#x2F;a&gt; &lt;a href=&quot;&#x2F;blog&#x2F;gc-21-cfp&#x2F;.&#x2F;gophercon-2019-cfp-omg&#x2F;&quot;&gt;years&lt;&#x2F;a&gt;, I&#x27;ve written about the GopherCon &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2021&quot;&gt;call for proposals (CFP)&lt;&#x2F;a&gt; when it opened. Much like 2020, this year&#x27;s conference will be a bit unique because of the lingering effects of the Coronavirus pandemic, but the CFP process is the same as always.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re looking for great proposals that will become even better talks; and this applies despite your physical presence in San Diego! Whether virtually or in-person, we intend to deliver an engaging, educational, and exceptional conference. As a reminder, we are looking especially for first-time speakers. This means that all else held equal, we’ll choose a proposal from a first-time speaker over that from a veteran. This isn’t meant to discourage experienced speakers; we just want to assure potential submitters that we continuously look for new and fresh ideas from community members we haven’t heard from before. &lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-we-do&quot;&gt;What We Do&lt;&#x2F;h2&gt;
&lt;p&gt;Before I go into details on what you need to include in your proposal, I want to explain how we review your proposal. We think it’s important to be transparent about this, and if you’re already aware of the criteria, a reminder never hurts. Here&#x27;s how it works:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You submit your proposal(s) to &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2021&quot;&gt;PaperCall&lt;&#x2F;a&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;We have a group of reviewers ranging from repeat GopherCon attendees to experienced speakers who conduct initial &lt;a href=&quot;https:&#x2F;&#x2F;www.wordnik.com&#x2F;words&#x2F;double-blind&quot;&gt;double-blind&lt;&#x2F;a&gt; reviews. We do each review based on the criteria below. &lt;em&gt;Every proposal submitted before the CFP closes is reviewed the same way during this step.&lt;&#x2F;em&gt;&lt;&#x2F;li&gt;
&lt;li&gt;After the CFP closes, we shortlist proposals with higher than average scores and choose proposals for the final program from this shortlist.
&lt;ul&gt;
&lt;li&gt;At this point, we de-anonymize proposals so we can choose the best lineup of talks for our audience and can contact you if your proposal is accepted.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;If your proposal is accepted, our mentoring team will work with you from the time you are notified until the day the conference begins to ensure your success on stage.
&lt;ul&gt;
&lt;li&gt;Our mentoring team will communicate frequently, review your slides, preview dress rehearsals of your talk, give you tips, and be a support onsite. Of course all of this is provided based on your comfort level. We won’t micromanage your preparation, except to ensure your talk is ready to go a few weeks prior to the conference.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-and-what-you-should-submit&quot;&gt;How (and What) You Should Submit&lt;&#x2F;h2&gt;
&lt;p&gt;So, given how we work, what should you keep in mind while submitting?&lt;&#x2F;p&gt;
&lt;h3 id=&quot;relevance&quot;&gt;Relevance&lt;&#x2F;h3&gt;
&lt;p&gt;GopherCon is a &lt;strong&gt;Go conference&lt;&#x2F;strong&gt; and people pay to attend (Though, you can watch the programming via a livestream for free if you choose not to travel this year!) because they want to learn more about &lt;strong&gt;Go&lt;&#x2F;strong&gt;. That means your proposal needs to be about Go and contain relevant information. Make sure you tell us, in your proposal, how your talk relates to Go if that’s not obvious. If you&#x27;re unsure how to do that, you can be explicit, i.e. &amp;quot;This talk relates to Go because ....&amp;quot;. &lt;&#x2F;p&gt;
&lt;p&gt;Also, keep in mind that Main Theater talks need to be relevant to &lt;strong&gt;everyone&lt;&#x2F;strong&gt; at the conference. If you&#x27;re planning to deep-dive into a topic, propose a 45-minute tutorial.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;clarity&quot;&gt;Clarity&lt;&#x2F;h3&gt;
&lt;p&gt;This one might sound obvious, but it&#x27;s easy to forget that the review committee and chairs only know what you want to talk about by reading your proposal. Remember that during most of the review process, reviewers don&#x27;t even know who you are.&lt;&#x2F;p&gt;
&lt;p&gt;Make sure that everything you&#x27;re thinking is clearly explained in your proposal. If you have to write more about what you plan to talk about to achieve clarity, do it. Proposals are commonly rejected because they&#x27;re unclear, but I&#x27;ve never seen a proposal rejected specifically because it was too long.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;correctness&quot;&gt;Correctness&lt;&#x2F;h3&gt;
&lt;p&gt;We only accept proposals if the speaker clearly has knowledge of the topic they are proposing. We don&#x27;t need you to be an expert, by any means, but we want you to at least be speaking from a place of experience. Remember that experience can have many meanings, including:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;What it was like for you to learn something&lt;&#x2F;li&gt;
&lt;li&gt;How you overcame a challenge at work by using &lt;code&gt;$TECHNOLOGY&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;What it was like fixing a bug in &lt;code&gt;$MODULE&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The only way we know whether you can deliver a talk from your experience or expertise is from your proposal. While we&#x27;re evaluating your proposal, we don&#x27;t know who you are or anything about you, so validating your knowledge on the subject you are proposing is key.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;achievability&quot;&gt;Achievability&lt;&#x2F;h3&gt;
&lt;p&gt;The duration of Main Theater talks is 25 minutes and tutorial sessions are 45 minutes. In either case, those timeframes are a &lt;em&gt;lot&lt;&#x2F;em&gt; shorter than they sound. You need to show us that you&#x27;ve thought about how long your talk will take and you know how you&#x27;ll fit your content into the timeframe you&#x27;ve chosen. The best way to do this is to include an outline of your talk with timing estimates. An outline shows us that you&#x27;ve thought about timing and it also greatly improves the clarity of your proposal.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;We&#x27;ve rejected talks we liked, but weren&#x27;t sure if the author knew how they would fit their talk into the time allotted.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;impact&quot;&gt;Impact&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;This is the piece that&#x27;s missing in the vast majority of rejected proposals&lt;&#x2F;strong&gt;. We look for talks that will teach the audience something &lt;strong&gt;new&lt;&#x2F;strong&gt;, so we need you to tell us what you plan to teach. Don&#x27;t assume that it&#x27;s obvious, it likely isn&#x27;t.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re not sure how to do that in your proposal, write this: &amp;quot;The audience will walk away with &lt;code&gt;$THING_YOU_PLAN_TO_TEACH&lt;&#x2F;code&gt;.&amp;quot;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more-reading&quot;&gt;More Reading&lt;&#x2F;h2&gt;
&lt;p&gt;There&#x27;s a lot of literature out there on how to submit a great proposal. We&#x27;ve added links at the bottom of &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2021&quot;&gt;the CFP page&lt;&#x2F;a&gt;, but this year, one of my co-chairs, Kris Brandow, is writing a detailed blog series on this topic. Please check out &lt;a href=&quot;https:&#x2F;&#x2F;blog.gopheracademy.com&#x2F;storytelling-on-stage-the-basics&#x2F;&quot;&gt;post 1&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;blog.gopheracademy.com&#x2F;storytelling-on-stage-storytelling&#x2F;&quot;&gt;post 2&lt;&#x2F;a&gt;, and &lt;a href=&quot;https:&#x2F;&#x2F;blog.gopheracademy.com&#x2F;storytelling-on-stage-advice&#x2F;&quot;&gt;post 3&lt;&#x2F;a&gt;, with a final installment publishing on Monday, April 19th.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;We receive anywhere from 180-225 proposals during our short 3-week CFP process and multiple people double-blind review each and every one. With that said, the following are some important tips to keep in mind:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Don&#x27;t assume that a topic is implicit, especially if it&#x27;s detailed or intricate. It may be to a coworker, but it&#x27;s likely not to a reviewer who is volunteering time to read your proposal and doesn’t know the source.&lt;&#x2F;li&gt;
&lt;li&gt;We like details! When in doubt, elaborate on an important point in your proposal. Be explicit!&lt;&#x2F;li&gt;
&lt;li&gt;Proposals that don&#x27;t state their impact on the audience have a low chance of success.&lt;&#x2F;li&gt;
&lt;li&gt;You don&#x27;t need to be an expert on your topic, but you do need to demonstrate knowledge of it. Make sure to clearly tell us what your experience is.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And with that, I wish you good luck during the CFP process and I hope to see you on stage soon!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;em&gt;Edits for clarification were made to this post on April 16, 2021&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Next Steps for KEDA HTTP</title>
          <pubDate>Wed, 09 Dec 2020 11:58:14 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/kedahttp-2/</link>
          <guid>/blog/kedahttp-2/</guid>
          <description>&lt;p&gt;I &lt;a href=&quot;&#x2F;blog&#x2F;kedahttp-2&#x2F;.&#x2F;2020-11-23-kedahttp.md&quot;&gt;recently wrote&lt;&#x2F;a&gt; about a new project I&#x27;m starting in the Kubernetes world. Its overarching goal is to build an open source, app developer friendly system built on top of Kubernetes. Since then, there has been a lot of interest and a few developments that I&#x27;m excited about.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;technical-reference-and-proposal-document&quot;&gt;Technical Reference and Proposal Document&lt;&#x2F;h2&gt;
&lt;p&gt;Along with the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;keda&#x2F;issues&#x2F;538&quot;&gt;GitHub issue&lt;&#x2F;a&gt; that started the project, there is now a &lt;a href=&quot;https:&#x2F;&#x2F;hackmd.io&#x2F;@arschles&#x2F;kedahttp&quot;&gt;proposal and technical overview&lt;&#x2F;a&gt; of the system. Like most proposals, this document will serve as the proposal submitted to the KEDA maintainers and also as a reference document that the project will build against.&lt;&#x2F;p&gt;
&lt;p&gt;Included in this document is an overview of all the components in the system, an architectural diagram of how they&#x27;re assembled, and a discussion of the extensibility of the system [1].&lt;&#x2F;p&gt;
&lt;h2 id=&quot;acceptance-to-the-keda-organization&quot;&gt;Acceptance to the KEDA Organization&lt;&#x2F;h2&gt;
&lt;p&gt;The proposal has been accepted and a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&quot;&gt;new repository&lt;&#x2F;a&gt; under the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&quot;&gt;KEDA organization&lt;&#x2F;a&gt; to house these components.&lt;&#x2F;p&gt;
&lt;p&gt;This development is important because the people and the idea now have the support [2] of the KEDA organization and a wider discussion has been started around the direction and featureset. We also have a wider team of developers that have joined to contribute.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s Next&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m pleased with the direction of this project and looking forward to the next few months. Things are moving very quickly in the new repository. I did a pairing session yesterday with coworkers in which we completed the entire serving and metrics infrastructure [3], and the next component on deck is the &lt;a href=&quot;https:&#x2F;&#x2F;hackmd.io&#x2F;@arschles&#x2F;kedahttp#Operator&quot;&gt;operator&lt;&#x2F;a&gt; [4].&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;re interested in seeing this component progress -- along with the project at-large -- I live code it &lt;a href=&quot;https:&#x2F;&#x2F;twitch.tv&#x2F;arschles&quot;&gt;on Twitch&lt;&#x2F;a&gt; 2-3 times per week. I encourage you to tune in and ask questions&#x2F;leave comments in the chat.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;contributing&quot;&gt;Contributing&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;re not yet ready for contributions from the community at-large. We have two major tasks to complete before we&#x27;ll be open for contributions from the at-large community.&lt;&#x2F;p&gt;
&lt;p&gt;First, we need to finish the minimal infrastructure. The components and supporting artifacts (Helm charts, CI scripts, etc...) are being built in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;http-add-on&#x2F;pull&#x2F;2&quot;&gt;PR #2&lt;&#x2F;a&gt;, and once we have them completed, we will merge it [5]. Second, we need to establish a roadmap. We&#x27;re beginning to outline it now and will finish it shortly after merging.&lt;&#x2F;p&gt;
&lt;p&gt;We believe that when these two tasks are complete, the project will have an established foundation and be well situated to accept contributions and support a larger community.&lt;&#x2F;p&gt;
&lt;p&gt;I along with others will broadcast when we&#x27;re ready to welcome the wider community.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;[1] In addition to the discussion in there, I&#x27;ve had more concrete discussions elsewhere about extensibility. Most of them are around alternative levels of abstraction that this system could support. The given architecture is highly extensible, and after this particular abstraction is built, we&#x27;ll explore others.&lt;&#x2F;p&gt;
&lt;p&gt;[2] The word &amp;quot;support&amp;quot; has different implications in different open source projects and communities. In this case, I mean that there is a repository created and the general idea to support HTTP based autoscaling is on the roadmap of the larger KEDA project.&lt;&#x2F;p&gt;
&lt;p&gt;[3] We based much of our code on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osscda&#x2F;kedahttp&quot;&gt;prototype codebase&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[4] This component is notable because it&#x27;s being implemented in &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.org&quot;&gt;Rust&lt;&#x2F;a&gt;. The decision to do so was inspired by my former colleagues in the &lt;a href=&quot;https:&#x2F;&#x2F;deislabs.io&quot;&gt;Deis Labs&lt;&#x2F;a&gt; group at Microsoft. They are innovating in significant ways in the Cloud Native community.&lt;&#x2F;p&gt;
&lt;p&gt;[5] Depending on how we progress, we may create and merge multiple PRs.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Deploying Apps With KEDA-HTTP</title>
          <pubDate>Mon, 23 Nov 2020 14:26:41 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/k8s-webapps-easy-button/</link>
          <guid>/blog/k8s-webapps-easy-button/</guid>
          <description>&lt;p&gt;When you search for &lt;a href=&quot;https:&#x2F;&#x2F;duckduckgo.com&#x2F;?q=what+is+kubernetes&amp;amp;t=brave&amp;amp;ia=web&quot;&gt;&amp;quot;what is Kubernetes?&amp;quot;&lt;&#x2F;a&gt;, content abounds with technical details of the system. There&#x27;s less about what Kubernetes is at a high level. Here&#x27;s my attempt at that in two sentences:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Kubernetes is an operating system for your cluster of computers. Like any OS, you get a syscall API for CPU, memory, networking, storage and more, and build abstractions from there.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you&#x27;re new to Kubernetes, let those two sentences sink in before you move on. And if you&#x27;re an app developer trying to deploy to Kubernetes, remember the things you&#x27;re &lt;em&gt;not&lt;&#x2F;em&gt; implementing: an HTTP server&#x2F;router, a database driver, a JSON parser, and so on. You&#x27;re using abstractions. Kubernetes should be no different.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;app-developers-on-the-web&quot;&gt;App Developers on the Web&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m using the term &amp;quot;app developer&amp;quot; in a very imprecise way; that&#x27;s on purpose. I&#x27;m not out to define what this group developers does or is, I&#x27;m out to assert the tasks they &lt;em&gt;shouldn&#x27;t have to do&lt;&#x2F;em&gt; while building their app. Developers should not have to understand details of virtual machines, the Kubelet, &lt;code&gt;Pod&lt;&#x2F;code&gt;s&#x2F;&lt;code&gt;Deployment&lt;&#x2F;code&gt;s&#x2F;&lt;code&gt;Service&lt;&#x2F;code&gt;s&#x2F;etc..., or &lt;code&gt;kubectl&lt;&#x2F;code&gt; &lt;em&gt;[0]&lt;&#x2F;em&gt;. They also shouldn&#x27;t need to deal with &lt;a href=&quot;https:&#x2F;&#x2F;helm.sh&quot;&gt;Helm&lt;&#x2F;a&gt; charts or watch Kubernetes dashboards after a deploy &lt;em&gt;[1]&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, they shouldn&#x27;t have to know a distributed syscall API to accomplish everyday deployment and management tasks. Kubernetes is a robust platform on which to run web applications, not an adequate experience for web developers to deploy them.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-better-experience&quot;&gt;A Better Experience&lt;&#x2F;h2&gt;
&lt;p&gt;In 2015, Rob Pike gave a talk at dotGo entitled &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=rFejpH_tAHM&quot;&gt;&amp;quot;Simplicity is Complicated&amp;quot;&lt;&#x2F;a&gt;. Referring to the Go language, he explained that while Go is simple for developers, the code that provides that simplicity is very complex. Indeed, the Go standard library provides a very &lt;em&gt;simple&lt;&#x2F;em&gt; and &lt;em&gt;flexible&lt;&#x2F;em&gt; API to access underlying system resources.&lt;&#x2F;p&gt;
&lt;p&gt;Kubernetes is the underlying system. The task we have now is (a) defining what a simple and flexible interface needs to look like, and (b) implementing it.&lt;&#x2F;p&gt;
&lt;p&gt;Many cloud providers have relevant products we can draw inspiration from &lt;em&gt;[2]&lt;&#x2F;em&gt;. While these platforms are diverse, most or all of them share roughly the following features.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You &lt;em&gt;focus&lt;&#x2F;em&gt; on running your code, not underlying resources like VMs, containers, VNets and so forth&lt;&#x2F;li&gt;
&lt;li&gt;Your app can automatically scale up and down based on demand&lt;&#x2F;li&gt;
&lt;li&gt;A CLI is available to deploy your code to production with a command&lt;&#x2F;li&gt;
&lt;li&gt;Your app can built in the cloud, instead of your computer&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s easy to connect your code to other databases and other cloud services&lt;&#x2F;li&gt;
&lt;li&gt;You can opt in to advanced features like gradual rollouts and more&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Kubernetes has the foundational technologies to build these features and more. Our challenge is to choose the right ones, manage the complexity, and expose a simple and flexible abstraction purpose-built for Kubernetes.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;keda-http&quot;&gt;KEDA-HTTP&lt;&#x2F;h2&gt;
&lt;p&gt;That brings us to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osscda&#x2F;kedahttp&quot;&gt;KEDA-HTTP&lt;&#x2F;a&gt;, a new project I&#x27;m starting. I&#x27;m building KEDA-HTTP to bring Kubernetes closer to app developers plain and simple. Knowing approximately what a good platform looks like, the technical details matter.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;under-the-hood&quot;&gt;Under the Hood&lt;&#x2F;h3&gt;
&lt;p&gt;This project primarily assembles existing building blocks to build the right developer experience &lt;em&gt;[3]&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Standard Kubernetes &lt;code&gt;Deployment&lt;&#x2F;code&gt;s, &lt;code&gt;Service&lt;&#x2F;code&gt;s and &lt;code&gt;Ingress&lt;&#x2F;code&gt;es and Ingress Controllers for running pods (&amp;quot;compute&amp;quot;) and routing HTTP traffic to them&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;keda.sh&quot;&gt;KEDA&lt;&#x2F;a&gt; for scaling &lt;code&gt;Deployment&lt;&#x2F;code&gt;s up and down&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;extend-kubernetes&#x2F;operator&#x2F;&quot;&gt;operator pattern&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;docs&#x2F;concepts&#x2F;extend-kubernetes&#x2F;api-extension&#x2F;custom-resources&#x2F;&quot;&gt;Custom Resource Definitions (CRDs)&lt;&#x2F;a&gt; for defining a comprehensive abstraction and API over KEDA and the standard Kubernetes resources&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;the-future&quot;&gt;The Future&lt;&#x2F;h3&gt;
&lt;p&gt;Kubernetes has democratized distributed systems. Any developer can access lots of distributed primitives over a standard API, but doing so requires understanding large concepts and keeping lots of context in one&#x27;s head. That requirement alone is a barrier to entry for a lot of people. Further, a large subset of applications don&#x27;t need some of these primitives &lt;em&gt;[4]&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I believe that we know what a simple and flexible interface needs to look like. As I see this system evolve, I&#x27;m optimistic that this next level of abstraction will greatly increase the number of developers who can use the technology. Work is ongoing at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osscda&#x2F;kedahttp&quot;&gt;github.com&#x2F;osscda&#x2F;kedahttp&lt;&#x2F;a&gt; and there is a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;keda&#x2F;issues&#x2F;538&quot;&gt;proposal&lt;&#x2F;a&gt; open for adding this technology to the KEDA project.&lt;&#x2F;p&gt;
&lt;p&gt;A small team of my Cloud Advocate peers at Microsoft, the great people in the KEDA project and me are working on this project. We&#x27;re not quite ready for help, but watch the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;kedacore&#x2F;keda&#x2F;issues&#x2F;538&quot;&gt;proposal issue&lt;&#x2F;a&gt; for progress. We&#x27;ll be slowly modularizing the codebase and contributing functionality piece by piece into KEDA.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;[0] As is the case with every abstraction, developers should somewhat understand inner workings.&lt;&#x2F;p&gt;
&lt;p&gt;[1] In many cases, developers also function in SRE or DevOps roles as well. In those situations, they almost certainly would need to use these dashboards to assess the health of their code in production. There is a clear separation of roles or &amp;quot;personas&amp;quot; implied here - the developer writes application code and deploys it to production, while the SRE&#x2F;DevOps engineer operates it in production.&lt;&#x2F;p&gt;
&lt;p&gt;[2]  In no order, with no preference and non-exhaustively, these include &lt;a href=&quot;https:&#x2F;&#x2F;www.digitalocean.com&#x2F;products&#x2F;app-platform&#x2F;&quot;&gt;App Platform&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;appengine&#x2F;&quot;&gt;App Engine&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;aws.amazon.com&#x2F;elasticbeanstalk&#x2F;&quot;&gt;Elastic Beanstalk&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;heroku.com&quot;&gt;Heroku&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;app-service&#x2F;&quot;&gt;App Service&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;[3] Personally, I&#x27;m excited about the fact that the project is partially being implemented in &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.org&quot;&gt;Rust&lt;&#x2F;a&gt;. While the language has a reputation for being hard to learn, I believe that the language provides several features that make it well suited for writing Kubernetes components&lt;&#x2F;p&gt;
&lt;p&gt;[4] Such as leader election and distributed leases&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>First Lines of Rust</title>
          <pubDate>Wed, 18 Nov 2020 16:10:08 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/rust-go/</link>
          <guid>/blog/rust-go/</guid>
          <description>&lt;p&gt;After ~8 years of Go as the end-all-be-all of programming languages in my life, I started writing Rust code for &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;osscda&#x2F;kedahttp&quot;&gt;a real project&lt;&#x2F;a&gt;, not just as a hobby.&lt;&#x2F;p&gt;
&lt;p&gt;The project, briefly put, is a Kubernetes system to let people easily and quickly deploy HTTP-based containers to production with a CLI. There is more to the story than that, but I don&#x27;t want to stray from what I&#x27;m excited about. I&#x27;m &lt;em&gt;really&lt;&#x2F;em&gt; learning a new language after all this time, from the ground up [1].&lt;&#x2F;p&gt;
&lt;h2 id=&quot;unlearning-go-is-hard&quot;&gt;Unlearning Go is Hard&lt;&#x2F;h2&gt;
&lt;p&gt;Lots of people come to the Go community and start writing Goava or Gython (Java-like Go or Python-like Go). I usually spend time with many of them helping un-teach that language so that they can absorb Go and build up the cognitive &amp;quot;muscle memory&amp;quot; that they need to be productive in the language.&lt;&#x2F;p&gt;
&lt;p&gt;Now, the tables have turned and I&#x27;m experiencing the same thing with Rust. One of the important first steps I&#x27;ve taken is accepting that I will still &amp;quot;know&amp;quot; Go even if I &amp;quot;unlearn&amp;quot; it so I really get productive with Rust. I&#x27;ll always be a Gopher.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;similarities-to-go&quot;&gt;Similarities to Go&lt;&#x2F;h2&gt;
&lt;p&gt;As I progress, I&#x27;m writing down some cognitive and structural similarities I see in the two languages. That&#x27;s helping me make the transition from the Go way to the Rust way. Both are modern, systems-oriented programming languages so they view the world similarly and as such, they&#x27;ve landed on a few similar and important features.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;memory-safe&quot;&gt;Memory Safe&lt;&#x2F;h3&gt;
&lt;p&gt;Go uses garbage collection and Rust uses the ownership concept, the borrow checker and (opt-in) reference counting, but both languages give you confidence &lt;em&gt;at compile time&lt;&#x2F;em&gt; that you won&#x27;t access invalid memory. They also work hard to help you ensure that memory will be cleaned up as expected.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;batteries-included&quot;&gt;Batteries Included&lt;&#x2F;h3&gt;
&lt;p&gt;I was going to entitle this section &amp;quot;Built in Tests&amp;quot;, but more is built in than just tests. Both languages ship with almost all of the tools needed to build modern software. Go has the &lt;code&gt;go&lt;&#x2F;code&gt; CLI and Rust has &lt;code&gt;cargo&lt;&#x2F;code&gt;. In both cases, you run an installer to get a built in test framework, extensive standard library, dependency management (in Go 1.9 and up!), and more.&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t understate the effect these out-of-the-box tools have on the community. Anyone can come to the language, run an installer, and be ready to write production level code. Other language ecosystems make you learn -- and sometimes even choose from -- a wide variety of tools before you write your first line of code. That&#x27;s a big barrier to entry for someone new to the language and can turn people off.&lt;&#x2F;p&gt;
&lt;p&gt;Both ecosystems do have other tools you can learn, but you can be productive with what comes out of the box.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;errors-are-values&quot;&gt;Errors are Values&lt;&#x2F;h3&gt;
&lt;p&gt;One of Go&#x27;s hallmarks is that &amp;quot;errors are values&amp;quot;. In practical terms, that means that there is no exception system and the language makes you check any error that a function might return.&lt;&#x2F;p&gt;
&lt;p&gt;Rust also lacks exceptions, but has a more expressive type system [2]. It usually represents errors using the &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;&#x2F;code&gt; type [3]. Many people use &lt;code&gt;match&lt;&#x2F;code&gt; statements to &amp;quot;pull&amp;quot; values out of the &lt;code&gt;Result&lt;&#x2F;code&gt; (you can do it in other ways too), and Rust has an exhaustiveness checker that effectively forces you to check every error.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;panic-only-in-unrecoverable-situations&quot;&gt;Panic Only in Unrecoverable Situations&lt;&#x2F;h3&gt;
&lt;p&gt;Go has a &lt;code&gt;panic()&lt;&#x2F;code&gt; function and Rust has a &lt;code&gt;panic!()&lt;&#x2F;code&gt; macro. They look almost the same and they have approximately the same purpose; if there is no way to recover and your program can&#x27;t continue under any circumstances, &lt;code&gt;panic&lt;&#x2F;code&gt;-ing will exit immediately with a stack trace (optionally in Rust).&lt;&#x2F;p&gt;
&lt;h3 id=&quot;polymorphism&quot;&gt;Polymorphism&lt;&#x2F;h3&gt;
&lt;p&gt;Polymorphism is an umbrella term to encompass many features in a programming language and Rust and Go provide a similar subset of them.&lt;&#x2F;p&gt;
&lt;p&gt;In Go, you can create an &lt;code&gt;interface{}&lt;&#x2F;code&gt; that specifies methods that a concrete type can implement. Any concrete type  that implements them automatically adheres to that interface, without specifying so. You can use any concrete type where the interface is expected.&lt;&#x2F;p&gt;
&lt;p&gt;Rust has &lt;code&gt;trait&lt;&#x2F;code&gt;s, which are very similar. You can define methods in them, and a default implementation for those methods as well (similar to an &lt;code&gt;abstract class&lt;&#x2F;code&gt; in C++).&lt;&#x2F;p&gt;
&lt;p&gt;One big difference in this area is that Rust requires that you explicitly state that a concrete type is implementing a trait. That small difference has a big impact in large codebases because with Go, you don&#x27;t have to add a dependency to your app just to implement one of its &lt;code&gt;interface&lt;&#x2F;code&gt;s [4].&lt;&#x2F;p&gt;
&lt;h2 id=&quot;more&quot;&gt;More?&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m a month or so into building this app and becoming more productive by the day. I&#x27;ve been writing Go for a long time and I&#x27;ll naturally draw more parallels to Rust as I progress. So far, I&#x27;m enjoying the process and looking forward to the future 🚀.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;[1] I&#x27;ve learned a few languages ad-hoc over the past years, but not from the ground up, and that&#x27;s why this is exciting for me
[2] Go has plans to add &lt;a href=&quot;https:&#x2F;&#x2F;go.googlesource.com&#x2F;proposal&#x2F;+&#x2F;refs&#x2F;heads&#x2F;master&#x2F;design&#x2F;go2draft-contracts.md&quot;&gt;generics&lt;&#x2F;a&gt; to the language soon, but the proposed design is different from Rust&#x27;s generics design and it lacks features that Rust has.
[3] Since &lt;code&gt;Result&lt;&#x2F;code&gt; is a regular enum in the standard library, it&#x27;s only the de-facto standard for error checking; you don&#x27;t have to use it as such
[4] Go is sometimes credited with allowing &amp;quot;duck typing&amp;quot; for this reason, but the feature is closer to structural typing&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>async&#x2F;await: under the hood</title>
          <pubDate>Tue, 30 Jun 2020 15:00:33 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/async-await/</link>
          <guid>/blog/async-await/</guid>
          <description>&lt;p&gt;I&#x27;m really interested in concurrency strategies in programming languages, and because there&#x27;s a lot of written research out there on the topic, you can find lots of strategies out there. When you look at some of the more modern stuff, you&#x27;ll find a &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;wp-content&#x2F;uploads&#x2F;2007&#x2F;01&#x2F;The-Joins-Concurrency-Library.pdf&quot;&gt;lot&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;concurnas.com&#x2F;&quot;&gt;of&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;rust-lang.github.io&#x2F;async-book&#x2F;03_async_await&#x2F;01_chapter.html&quot;&gt;literature&lt;&#x2F;a&gt; on just about the same pattern: &lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt; is picking up steam in languages because it makes concurrency &lt;em&gt;really&lt;&#x2F;em&gt; easy to see and deal with. Let&#x27;s look at how it works and why it helps, using Javascript to illustrate the concepts.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#x27;m a Javascript dabbler at best, but it&#x27;s a great language to illustrate these concepts with. Don&#x27;t go too hard on my JS code below 😅&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;what-it-s-about-thinking&quot;&gt;What It&#x27;s About 🤔&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt; is about writing concurrent code easily, but more importantly, it&#x27;s about writing the code so it&#x27;s easy to &lt;strong&gt;read&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;solving-concurrency-three-ways-clock3&quot;&gt;Solving Concurrency Three Ways 🕒&lt;&#x2F;h2&gt;
&lt;p&gt;This pattern relies on a feature called Promises in Javascript, so we&#x27;re gonna build up from basics to Promises in JS, and cap it off with integrating &lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt; into Promises.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Promises are called Futures in many other languages&#x2F;frameworks. Some use both terms! It can be confusing, but the concept is the same. We&#x27;ll go into details later in this post.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;callbacks-sob&quot;&gt;Callbacks 😭&lt;&#x2F;h3&gt;
&lt;p&gt;You&#x27;ve probably heard about callbacks in Javascript. If you haven&#x27;t, they&#x27;re a programming pattern that lets you schedule work to be done in the future, after something else finishes. Callbacks are also the foundation of what we&#x27;re talking about here.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;The core problem we&#x27;re solving in this entire article is how to run code after some concurrent work is being done.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;The syntax of callbacks is basically passing a function into a function:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;doStuff&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;callback&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; do something
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; now it&amp;#39;s done, call the callback
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;callback&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;someStuff&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;doStuff&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; when doStuff is done doing its thing, it&amp;#39;ll pass its result
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; to this function.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; we don&amp;#39;t know when that&amp;#39;ll be, just that this function will run.
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; That means that the rest of our ENTIRE PROGRAM needs to go in here
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; (most of the time)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Barf, amirite?
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;done with doStuff&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Wait, though... if you put something here ... it&amp;#39;ll run right away. It won&amp;#39;t wait for doStuff to finish
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That last comment in the code is the confusing part. In practice, most apps don&#x27;t want to continue execution. They want to wait. Callbacks make that difficult to achieve, confusing, and &lt;a href=&quot;http:&#x2F;&#x2F;callbackhell.com&#x2F;&quot;&gt;exhausting to write&lt;&#x2F;a&gt; and read 😞.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;promises-raised-hands&quot;&gt;Promises 🙌&lt;&#x2F;h3&gt;
&lt;p&gt;I&#x27;ll see your callbacks and raise you a &lt;code&gt;Promise&lt;&#x2F;code&gt;! No really, Promises are dressed up callbacks that make things easier to deal with. But you still pass functions to functions and it&#x27;s still a bit harder than it has to be.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;returnAPromiseYall&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; do some stuff!
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;somePromise&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; let&amp;#39;s call it and get our promise
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myProm &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;returnAPromiseYall&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; now we have to do some stuff after the promise is ready
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myProm&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;result&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; the result is the variable in the promise that we&amp;#39;re waiting for,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; just like in callback world
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;anotherPromise&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;newResult&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; We can chain these &amp;quot;then&amp;quot; calls together to build a pipeline of
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; code. So it&amp;#39;s a little easier to read, but still. 
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Passing functions to functions and remembering to write your code inside
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; these &amp;quot;then&amp;quot; calls is sorta tiring
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;doMoreStuff&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;newResult&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;span&gt;});
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We got a few small wins:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;No more confusing &lt;em&gt;nested&lt;&#x2F;em&gt; callbacks&lt;&#x2F;li&gt;
&lt;li&gt;This &lt;code&gt;then&lt;&#x2F;code&gt; function implies a &lt;em&gt;pipeline&lt;&#x2F;em&gt; of code. Syntactically and conceptually, that&#x27;s easier to deal with&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;But we still have a few sticky problems:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;You have to remember to put the rest of your program into a &lt;code&gt;then&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;You&#x27;re still passing functions to functions. It still gets tiring to read and write that&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;async-await-first-place-medal&quot;&gt;async&#x2F;await 🥇&lt;&#x2F;h3&gt;
&lt;p&gt;Alrighty, we&#x27;re here folks! The &lt;code&gt;Promise&lt;&#x2F;code&gt;d land 🎉🥳🍤. We can get rid of passing functions to functions, &lt;code&gt;then&lt;&#x2F;code&gt;, and all that forgetting to put the rest of your program into the &lt;code&gt;then&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;All with this 🔥 pattern. Check it:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;javascript&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-javascript &quot;&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;async function &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;doStuff&lt;&#x2F;span&gt;&lt;span&gt;() {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; just like the last two examples, return a promise
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;return &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;myPromise&lt;&#x2F;span&gt;&lt;span&gt;;
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; now, behold! we can call it with await
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;let &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;theResult &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= await &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;doStuff&lt;&#x2F;span&gt;&lt;span&gt;();
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; IN A WORLD, WHERE THERE ARE NO PROMISES ...
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; ONLY GUARANTEES
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; In other words, the value is ready right here!
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;console&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b39f04;&quot;&gt;log&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;`the result is ready: ${&lt;&#x2F;span&gt;&lt;span style=&quot;color:#acb3c2;&quot;&gt;theResult&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;}`&lt;&#x2F;span&gt;&lt;span&gt;);
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Thanks to the &lt;code&gt;await&lt;&#x2F;code&gt; keyword, we can read the code from top to bottom. This gets translated to something or other under the hood, and what exactly it is depends on the language. In JS land, it&#x27;s essentially &lt;code&gt;Promise&lt;&#x2F;code&gt;s most of the time. The results to us &lt;em&gt;programmers&lt;&#x2F;em&gt; is always the same, though:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Programmers can read&#x2F;write code from top to bottom, the way we&#x27;re used to doing it&lt;&#x2F;li&gt;
&lt;li&gt;No passing functions into functions means less &lt;code&gt;})&lt;&#x2F;code&gt; syntax to &lt;del&gt;forget&lt;&#x2F;del&gt; write&lt;&#x2F;li&gt;
&lt;li&gt;The &lt;code&gt;await&lt;&#x2F;code&gt; keyword can be an indicator that &lt;code&gt;doStuff&lt;&#x2F;code&gt; does something &amp;quot;expensive&amp;quot; (like call a REST API)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h4 id=&quot;what-about-the-async-keyword&quot;&gt;What about the &lt;code&gt;async&lt;&#x2F;code&gt; keyword⁉&lt;&#x2F;h4&gt;
&lt;p&gt;In many languages including JS, you have to mark a function &lt;code&gt;async&lt;&#x2F;code&gt; if it uses &lt;code&gt;await&lt;&#x2F;code&gt; inside of it. There are language-specific reasons to do that, but here are some that you should care about:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;To tell the caller that there are &lt;code&gt;Promise&lt;&#x2F;code&gt;s or &lt;code&gt;await&lt;&#x2F;code&gt;s happening inside of it&lt;&#x2F;li&gt;
&lt;li&gt;To tell the runtime (or compiler in other languages) to do its magic behind the scenes to &amp;quot;make it work&amp;quot;™&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;checkered-flag&quot;&gt;🏁&lt;&#x2F;h2&gt;
&lt;p&gt;And that&#x27;s it. I left a lot of implementation details out, but it&#x27;s really important to remember that this pattern exists more for human reasons rather than technical.&lt;&#x2F;p&gt;
&lt;p&gt;You can do all of this stuff with callbacks, but in almost all cases, &lt;code&gt;async&lt;&#x2F;code&gt;&#x2F;&lt;code&gt;await&lt;&#x2F;code&gt; is going to make your life easier. Enjoy! 👋&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to Run Windows With a Mac Keyboard</title>
          <pubDate>Tue, 16 Jun 2020 11:53:00 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/powertoys-mac/</link>
          <guid>/blog/powertoys-mac/</guid>
          <description>&lt;p&gt;I wrote a bit about PowerToys &lt;a href=&quot;&#x2F;blog&#x2F;2020-05-20-powertoys&quot;&gt;previously&lt;&#x2F;a&gt;, but today we&#x27;re gonna talk about my second-favorite feature: the keyboard manager.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;My first-favorite feature is Powertoys Run - the spotlight-like feature for Windows. I talked about this one last post.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;make-windows-feel-like-mac-green-apple&quot;&gt;Make Windows Feel Like Mac 🍏&lt;&#x2F;h1&gt;
&lt;p&gt;I &lt;a href=&quot;&#x2F;blog&#x2F;how-to-wsl-2&#x2F;&quot;&gt;switched to Windows from Mac&lt;&#x2F;a&gt; a few months back, and I&#x27;m used to just about everything except the keyboard. Muscle memory keeps kicking in and my thumbs reach for the &lt;code&gt;cmd&lt;&#x2F;code&gt; key instead of &lt;code&gt;ctrl&lt;&#x2F;code&gt;. There&#x27;s no &lt;code&gt;cmd&lt;&#x2F;code&gt; on Windows - it just opens the start menu. I&#x27;ve opened the start menu a &lt;strong&gt;lot&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-keyboard-manager-musical-k&quot;&gt;The Keyboard Manager ⌨&lt;&#x2F;h1&gt;
&lt;p&gt;The key feature (see what I did there?) of PowerToys that lets my keyboard be all apple-ey is the keyboard manager. It looks like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;2020-06-16-powertoys-mac&#x2F;powertoys-keyboard-manager.png&quot; alt=&quot;powertoys keyboard manager&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a lot going on in that window. Let&#x27;s break it down...&lt;&#x2F;p&gt;
&lt;h2 id=&quot;remap-keyboard&quot;&gt;Remap Keyboard 🗺&lt;&#x2F;h2&gt;
&lt;p&gt;This one is simple in theory. Like the heading says, you get to map a single key. Take the first one in there - I mapped Page Up to the home key (that one wasn&#x27;t Mac specific - I just kept accidentally hitting it 😉).&lt;&#x2F;p&gt;
&lt;p&gt;Here are the keys I remapped to Mac-ify:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Caps Lock&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Win (Left)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Win (Left)&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Ctrl (Left)&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;This is basically making the Windows key to the left of the spacebar into the Ctrl key, which does similar things as the Cmd key on a Mac. Since I was overriding the Windows key, I moved that up to Caps Lock, since I never use it anyway. You could also do this with the Windows key on the right, if you have one.&lt;&#x2F;p&gt;
&lt;p&gt;To do these mappings, you click &amp;quot;Remap a Key&#x27;, then the Plus icon at the bottom. Click on each box and type the keys you want to map. The &amp;quot;before&amp;quot; key goes on the left, and the &amp;quot;after&amp;quot; key on the right.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;❗ Do the mapping from &lt;code&gt;Win&lt;&#x2F;code&gt; -&amp;gt; &lt;code&gt;Ctrl&lt;&#x2F;code&gt; &lt;em&gt;last&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;remap-shortcuts-pizza&quot;&gt;Remap Shortcuts 🍕&lt;&#x2F;h2&gt;
&lt;p&gt;This one piggybacks on key remapping. As you know, Windows has lots of key combinations that &amp;quot;do stuff&amp;quot;™. For example, alt+tab cycles through Windows, alt+tab switches Windows, win+s opens the search menu, win+d shows the desktop, win+e the explorer, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;Here are the shortcuts I set for mac-ifying:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Ctrl (Left) + Tab&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Alt (Left) + Tab&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;cmd+tab on Mac lets you cycle through Windows. This makes it feel the same. This still has a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;PowerToys&#x2F;issues&#x2F;3331&quot;&gt;bug&lt;&#x2F;a&gt;, but it&#x27;s usable enough for now&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Ctrl (Left) + Shift (Left) + [&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Ctrl (Left) + Shift (Left) + Tab&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;This lets me cycle through tabs quickly, just like on a Mac. This particular one cycles left.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Win (Right) + Shift (Right) + [&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Ctrl (Left) + Shift + Tab&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;Cycle to the left, from the right side of the keyboard&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;Ctrl (Left) + Shift (Left) + ]&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Ctrl (Left) + Tab&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;Cycle to the right, from the left side of the keyboard&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Win (Right) + Shift (Right) + ]&lt;&#x2F;code&gt; =&amp;gt; &lt;code&gt;Ctrl (Left) + Shift + Tab&lt;&#x2F;code&gt;
&lt;ul&gt;
&lt;li&gt;Cycle to the right, from the right side of the keyboard&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I hope those help! It&#x27;s by far not &lt;em&gt;everything&lt;&#x2F;em&gt; to make Windows feel like a mac - some Windows things just don&#x27;t apply to Mac and vice versa. But I think you&#x27;ll find this a good setup.&lt;&#x2F;p&gt;
&lt;p&gt;Enjoy!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Storing Data with Dapr (Dapr Series #3)</title>
          <pubDate>Thu, 21 May 2020 10:55:19 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/dapr3/</link>
          <guid>/blog/dapr3/</guid>
          <description>&lt;p&gt;Dapr! No, not &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;1kevgriff&#x2F;what-is-dapper-and-why-you-should-consider-it-for-your-net-projects-cm4&quot;&gt;dapper&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;dapr.io&quot;&gt;dapr&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you haven&#x27;t read my past &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;arschles&#x2F;what-is-dapr-526a&quot;&gt;articles&lt;&#x2F;a&gt; about dapr, that&#x27;s ok. Let me try to boil this big project down to a sentence:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;👋 Dapr is a process you run that does all the hard stuff for your app you&#x27;d rather not do&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you want to read more about it, see &lt;a href=&quot;https:&#x2F;&#x2F;cda.ms&#x2F;1jF&quot;&gt;the intro blog post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;And with that, let&#x27;s talk about (and demo!) probably the hardest thing that most (any?) apps have to deal with: storing data. &lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;🙋‍♀️ Dapr does &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts#building-blocks&quot;&gt;other things&lt;&#x2F;a&gt; besides storage too&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;why-it-matters&quot;&gt;Why It Matters&lt;&#x2F;h1&gt;
&lt;p&gt;Dealing with data stores is hard. You have to get the right SDK, mock out your data store for unit tests, write integration tests for round-tripping data, make sure you deal with migrations, failover, and more...&lt;&#x2F;p&gt;
&lt;p&gt;Dapr helps with all that, but it also comes with some limitations too. Before we go demo the thing, some pros and cons for storage:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;➕ You read&#x2F;write data with a pretty simple REST API (or gRPC!)&lt;&#x2F;li&gt;
&lt;li&gt;➕ You get a lot of useful features (retries anyone?)&lt;&#x2F;li&gt;
&lt;li&gt;➕ You can swap out implementations (Dapr comes with a bunch of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management#state-management-api&quot;&gt;different implementations&lt;&#x2F;a&gt; of the data API across clouds and open&#x2F;closed source.)&lt;&#x2F;li&gt;
&lt;li&gt;➖ You have to fit your data model into key&#x2F;value. Not all apps fit into this model&lt;&#x2F;li&gt;
&lt;li&gt;➕ and ➖ You have to do a bit of reading to take advantage of some useful storage features:
&lt;ul&gt;
&lt;li&gt;Strong consistency&lt;&#x2F;li&gt;
&lt;li&gt;Optimistic Concurrency&lt;&#x2F;li&gt;
&lt;li&gt;Retry policies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;ℹ In Dapr land, the data storage API isn&#x27;t called a database on purpose because it gives you a key&#x2F;value storage API. It&#x27;s not SQL or any other kind of rich query language, so keep that in mind when deciding what to use.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you want, you can check out more details on storage &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management#state-management-api&quot;&gt;here&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;surfer-surf-s-up-let-s-demo-this-thing&quot;&gt;🏄‍♀️ Surf&#x27;s Up, Let&#x27;s Demo This Thing&lt;&#x2F;h1&gt;
&lt;p&gt;Dapr comes with a bunch of &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management#state-management-api&quot;&gt;different implementations&lt;&#x2F;a&gt; of the data API across clouds and open&#x2F;closed source. We&#x27;ll use the Redis implementation here.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;ℹ You&#x27;ll need to have &lt;a href=&quot;https:&#x2F;&#x2F;www.docker.com&#x2F;products&#x2F;docker-desktop&quot;&gt;Docker&lt;&#x2F;a&gt; installed and running for this demo to work&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;1-installing&quot;&gt;1️⃣ - Installing 🛠&lt;&#x2F;h2&gt;
&lt;p&gt;I followed the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;getting-started&#x2F;environment-setup.md#using-script-to-install-the-latest-release&quot;&gt;installing&lt;&#x2F;a&gt; instructions, using the Linux script (since I&#x27;m &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;arschles&#x2F;how-to-wsl-516d&quot;&gt;using WSL2&lt;&#x2F;a&gt;):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ wget -q https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;dapr&#x2F;cli&#x2F;master&#x2F;install&#x2F;install.sh -O - | &#x2F;bin&#x2F;bash
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;I had to provide my password because the script &lt;code&gt;sudo&lt;&#x2F;code&gt;s things, so if you&#x27;re not into letting a random script from GitHub get superuser on your computer, you can &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;getting-started&#x2F;environment-setup.md#from-the-binary-releases&quot;&gt;download binaries&lt;&#x2F;a&gt; instead.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It was installed and I could test things out:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ dapr --help
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;         __
&lt;&#x2F;span&gt;&lt;span&gt;    ____&#x2F; &#x2F;___ _____  _____
&lt;&#x2F;span&gt;&lt;span&gt;   &#x2F; __  &#x2F; __ &amp;#39;&#x2F; __ \&#x2F; ___&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  &#x2F; &#x2F;_&#x2F; &#x2F; &#x2F;_&#x2F; &#x2F; &#x2F;_&#x2F; &#x2F; &#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;  \__,_&#x2F;\__,_&#x2F; .___&#x2F;_&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;              &#x2F;_&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;======================================================
&lt;&#x2F;span&gt;&lt;span&gt;A serverless runtime for hyperscale, distributed systems
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Usage:
&lt;&#x2F;span&gt;&lt;span&gt;  dapr [command]
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Available Commands:
&lt;&#x2F;span&gt;&lt;span&gt;  components     List all Dapr components
&lt;&#x2F;span&gt;&lt;span&gt;  configurations List all Dapr configurations
&lt;&#x2F;span&gt;&lt;span&gt;  help           Help about any command
&lt;&#x2F;span&gt;&lt;span&gt;  init           Setup dapr in Kubernetes or Standalone modes
&lt;&#x2F;span&gt;&lt;span&gt;  invoke         Invokes a Dapr app with an optional payload (deprecated, use invokePost)
&lt;&#x2F;span&gt;&lt;span&gt;  invokeGet      Issue HTTP GET to Dapr app
&lt;&#x2F;span&gt;&lt;span&gt;  invokePost     Issue HTTP POST to Dapr app with an optional payload
&lt;&#x2F;span&gt;&lt;span&gt;  list           List all Dapr instances
&lt;&#x2F;span&gt;&lt;span&gt;  logs           Gets Dapr sidecar logs for an app in Kubernetes
&lt;&#x2F;span&gt;&lt;span&gt;  mtls           Check if mTLS is enabled in a Kubernetes cluster
&lt;&#x2F;span&gt;&lt;span&gt;  publish        Publish an event to multiple consumers
&lt;&#x2F;span&gt;&lt;span&gt;  run            Launches Dapr and (optionally) your app side by side
&lt;&#x2F;span&gt;&lt;span&gt;  status         Shows the Dapr system services (control plane) health status.
&lt;&#x2F;span&gt;&lt;span&gt;  stop           Stops a running Dapr instance and its associated app
&lt;&#x2F;span&gt;&lt;span&gt;  uninstall      Removes a Dapr installation
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Flags:
&lt;&#x2F;span&gt;&lt;span&gt;  -h, --help      help for dapr
&lt;&#x2F;span&gt;&lt;span&gt;      --version   version for dapr
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Use &amp;quot;dapr [command] --help&amp;quot; for more information about a command.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It even has ascii art 🤡!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;2-hello-world-wave&quot;&gt;2️⃣ Hello, World 👋&lt;&#x2F;h2&gt;
&lt;p&gt;Let&#x27;s get something running. The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;samples&#x2F;tree&#x2F;master&#x2F;1.hello-world&quot;&gt;hello world tutorial&lt;&#x2F;a&gt; actually goes much farther than I expected it would.&lt;&#x2F;p&gt;
&lt;p&gt;You get an app up and talking to the storage API (backed by Redis), but then you get a second app up and running and have it call an API on the first one. It&#x27;s actually showing off &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;service-invocation&quot;&gt;service invocation&lt;&#x2F;a&gt; too 🎉&lt;&#x2F;p&gt;
&lt;p&gt;I first got the hello world sample code:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ git clone https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;samples.git &amp;amp;&amp;amp; cd samples&#x2F;1.hello-world
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The sample code has a Python app and a Node app. Both has a REST API in them.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;the-node-app-tokyo-tower&quot;&gt;The Node App 🗼&lt;&#x2F;h3&gt;
&lt;p&gt;First, the Node app. The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;samples&#x2F;tree&#x2F;master&#x2F;1.hello-world#step-2---understand-the-code&quot;&gt;demo README&lt;&#x2F;a&gt; first goes through the Node code a bit.&lt;&#x2F;p&gt;
&lt;p&gt;The really interesting bit I saw was that we&#x27;re doing a &lt;code&gt;POST&lt;&#x2F;code&gt; request with the &lt;code&gt;fetch&lt;&#x2F;code&gt; API to the local Dapr API to store some data. What was cool was you can also just return some JSON and it&#x27;ll be automatically stored. &lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;state&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: [{
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;key&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;nomnomnom&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;value&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;pizza&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }]
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;I really like that part because you don&#x27;t have to manually do anything to get data into the database. It just happens.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;magic.jpg&quot; alt=&quot;Magic&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;On to running the thing. You do the standard thing to install the JS dependencies:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ npm install
&lt;&#x2F;span&gt;&lt;span&gt;npm WARN node_server@1.0.0 No repository field.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;added 55 packages from 41 contributors and audited 55 packages in 0.626s
&lt;&#x2F;span&gt;&lt;span&gt;found 0 vulnerabilities
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;   ╭────────────────────────────────────────────────────────────────╮
&lt;&#x2F;span&gt;&lt;span&gt;   │                                                                │
&lt;&#x2F;span&gt;&lt;span&gt;   │      New patch version of npm available! 6.14.4 → 6.14.5       │
&lt;&#x2F;span&gt;&lt;span&gt;   │   Changelog: https:&#x2F;&#x2F;github.com&#x2F;npm&#x2F;cli&#x2F;releases&#x2F;tag&#x2F;v6.14.5   │
&lt;&#x2F;span&gt;&lt;span&gt;   │               Run npm install -g npm to update!                │
&lt;&#x2F;span&gt;&lt;span&gt;   │                                                                │
&lt;&#x2F;span&gt;&lt;span&gt;   ╰────────────────────────────────────────────────────────────────╯
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;But you then run it with the &lt;code&gt;dapr&lt;&#x2F;code&gt; CLI, not &lt;code&gt;npm&lt;&#x2F;code&gt; or &lt;code&gt;node&lt;&#x2F;code&gt;. This spits out a lot of log lines, and that&#x27;s normal. Here&#x27;s approximately what it looks like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ dapr run --app-id nodeapp --app-port 3000 --port 3500 node app.js
&lt;&#x2F;span&gt;&lt;span&gt;ℹ️  Starting Dapr with id nodeapp. HTTP Port: 3500. gRPC Port: 42491
&lt;&#x2F;span&gt;&lt;span&gt;== DAPR == time=&amp;quot;2020-05-18T16:26:52.702694-07:00&amp;quot; level=info msg=&amp;quot;starting Dapr Runtime -- version 0.6.0 -- commit e99f712-dirty&amp;quot; app_id=nodeapp instance=DESKTOP-DQP07VM scope=dapr.runtime type=log ver=0.6.0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;snip&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== APP == Node App listening on port 3000!
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== DAPR == time=&amp;quot;2020-05-18T16:26:52.7568756-07:00&amp;quot; level=info msg=&amp;quot;application discovered on port 3000&amp;quot; app_id=nodeapp instance=DESKTOP-DQP07VM scope=dapr.runtime type=log ver=0.6.0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;snip&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;ℹ️  Updating metadata for app command: node app.js
&lt;&#x2F;span&gt;&lt;span&gt;✅  You&amp;#39;re up and running! Both Dapr and your app logs will appear here.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Ok, now it&#x27;s running. You use the &lt;code&gt;dapr&lt;&#x2F;code&gt; CLI to &lt;em&gt;also&lt;&#x2F;em&gt; make calls to the web service. I like the simplicity of a single CLI.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ dapr invoke --app-id nodeapp --method neworder --payload &amp;#39;{&amp;quot;data&amp;quot;: { &amp;quot;orderId&amp;quot;: &amp;quot;41&amp;quot; } }&amp;#39;
&lt;&#x2F;span&gt;&lt;span&gt;✅  App invoked successfully
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can also use &lt;code&gt;curl&lt;&#x2F;code&gt; for this if you want&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And verifying that the request actually went through, and the data was stored, we have logs:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;== APP == Got a new order! Order ID: 41
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== APP == Successfully persisted state.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we can also call the Node App&#x27;s API to get the new data back out of the datastore&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ curl http:&#x2F;&#x2F;localhost:3500&#x2F;v1.0&#x2F;invoke&#x2F;nodeapp&#x2F;method&#x2F;order
&lt;&#x2F;span&gt;&lt;span&gt;{&amp;quot;orderId&amp;quot;:&amp;quot;41&amp;quot;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;This and the above &lt;code&gt;dapr invoke&lt;&#x2F;code&gt; call are using one of the features in Dapr&#x27;s service discovery building block&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;the-python-app-snake&quot;&gt;The Python App 🐍&lt;&#x2F;h3&gt;
&lt;p&gt;Now for the grande finale! The Python code calls the Node app in an infinite loop, so we should be able to see the Node app responding to requests and also see the datastore fill up at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;First, we set up the Python dependency:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ pip3 install requests
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;snip&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;Successfully installed certifi-2020.4.5.1 chardet-3.0.4 idna-2.9 requests-2.23.0 urllib3-1.25.9
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then I ran it with the same &lt;code&gt;dapr run&lt;&#x2F;code&gt; command we used for the Node app, except in a new terminal tab. I cut out all the verbose log lines this time!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ dapr run --app-id pythonapp python3 app.py
&lt;&#x2F;span&gt;&lt;span&gt;ℹ️  Starting Dapr with id pythonapp. HTTP Port: 43891. gRPC Port: 42163
&lt;&#x2F;span&gt;&lt;span&gt;ℹ️  Updating metadata for app command: python3 app.py
&lt;&#x2F;span&gt;&lt;span&gt;✅  You&amp;#39;re up and running! Both Dapr and your app logs will appear here.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Now, back in the Node app logs, storage is starting to fill up!&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;== APP == Got a new order! Order ID: 1
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== APP == Successfully persisted state.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== APP == Got a new order! Order ID: 2
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;== APP == Successfully persisted state.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;snip&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;that-s-it&quot;&gt;That&#x27;s It!&lt;&#x2F;h3&gt;
&lt;p&gt;From experience, getting storage and microservice-to-microservice calls working from scratch takes time, carefully crafted code, and effort.&lt;&#x2F;p&gt;
&lt;p&gt;I wanted to compare this Node&#x2F;Python codebase to compare my experience, and was pleasantly surprised. Let&#x27;s break it down:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;87 lines of Node code, including comments
&lt;ul&gt;
&lt;li&gt;We could save a lot of lines if we returned the &lt;code&gt;&amp;quot;data&amp;quot;&lt;&#x2F;code&gt; JSON dictionary instead of using &lt;code&gt;fetch&lt;&#x2F;code&gt; calls&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;23 lines of Node code, including comments&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;From experience with Go, I can say that a battle tested storage layer for this kind of thing would be about 50 lines. That&#x27;s an apples to oranges comparison, but I know for sure that all the battle-tested bits are now behind that slick storage API the Node app is using. And funny enough, that part is written in Go 💪.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;P.S. don&#x27;t forget to clean up: &lt;code&gt;dapr stop --app-id nodeapp &amp;amp;&amp;amp; dapr stop --app-id pythonapp&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;bonus-behind-the-scenes&quot;&gt;Bonus: Behind the Scenes&lt;&#x2F;h2&gt;
&lt;p&gt;I mentioned above that you need to have Docker installed in order to get this working. That&#x27;s all because Dapr needs two images running in the background for this demo to work.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;cb722412a5d3        redis               &amp;quot;docker-entrypoint.s…&amp;quot;   4 weeks ago         Up 6 hours          0.0.0.0:6379-&amp;gt;6379&#x2F;tcp     dapr_redis
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The important one is ... Redis! By default, all the storage API calls the Node app is making get routed into Redis.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wrap-up&quot;&gt;Wrap Up&lt;&#x2F;h2&gt;
&lt;p&gt;So we&#x27;ve seen the storage API &lt;em&gt;and&lt;&#x2F;em&gt; a bit of the service invocation API. Honestly, those two pieces alone can get you a long way just on basic usage alone.&lt;&#x2F;p&gt;
&lt;p&gt;As you get more advanced, you&#x27;ll probably want to learn more about and use things like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management#concurrency&quot;&gt;concurrency management&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management#retry-policies&quot;&gt;retry policies&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Any way you go, the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&quot;&gt;docs repository&lt;&#x2F;a&gt; and particularly the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;tree&#x2F;master&#x2F;concepts&#x2F;state-management&quot;&gt;state management section&lt;&#x2F;a&gt; are great references.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s all for today. I hope you go forth and enjoy dapr-ing!&lt;&#x2F;p&gt;
&lt;p&gt;🤘🚀&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Windows, Now With More Toys</title>
          <pubDate>Wed, 20 May 2020 11:39:16 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/2020-05-20-powertoys/</link>
          <guid>/blog/2020-05-20-powertoys/</guid>
          <description>&lt;p&gt;If you were around way back in the Windows 95 days, you might remember &lt;a href=&quot;https:&#x2F;&#x2F;socket3.wordpress.com&#x2F;2016&#x2F;10&#x2F;22&#x2F;using-windows-95-powertoys&#x2F;&quot;&gt;the PowerToys from then&lt;&#x2F;a&gt;. Years and years later, it&#x27;s back in a similar form, but for Windows 10.&lt;&#x2F;p&gt;
&lt;p&gt;I just got started with it and it&#x27;s another tool in my belt to help me feel comfortable using Windows every day after &lt;a href=&quot;https:&#x2F;&#x2F;deploy-preview-70--arschles-www.netlify.app&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&#x2F;&quot;&gt;many&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;deploy-preview-70--arschles-www.netlify.app&#x2F;blog&#x2F;how-to-wsl-2&#x2F;&quot;&gt;years&lt;&#x2F;a&gt; of using Macs.&lt;&#x2F;p&gt;
&lt;p&gt;Let me take you through it.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You might have read that I recently &lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&#x2F;&quot;&gt;came from a Mac&lt;&#x2F;a&gt;. I have a follow-up post to this on how to use PowerToys Keyboard Shortcuts to make your keyboard more mac-like&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;exploding-head-grey-question&quot;&gt;🤯❔&lt;&#x2F;h1&gt;
&lt;p&gt;PowerToys is a &lt;a href=&quot;&#x2F;blog&#x2F;2020-05-20-powertoys&#x2F;(https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;PowerToys#current-powertoy-utilities)&quot;&gt;few things in one package&lt;&#x2F;a&gt;, and different folks describe it differently.&lt;&#x2F;p&gt;
&lt;p&gt;The &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;powertoys&quot;&gt;official GitHub repository&lt;&#x2F;a&gt; says this:&lt;&#x2F;p&gt;
&lt;p&gt;&amp;quot;...set of utilities for power users to tune and streamline their Windows experience for greater productivity.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;Well, I love productivity (if you&#x27;ve seen any of my screencasts, I hit the keyboard shortcuts &lt;strong&gt;hard&lt;&#x2F;strong&gt;), so I&#x27;m gonna go with that!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;😎 By the way, PowerToys is 100% open source at &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft.com&#x2F;PowerToys&quot;&gt;github.com&#x2F;microsoft.com&#x2F;PowerToys&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I just installed this thing a few hours ago and started messing around with it. It&#x27;s at version v0.18.0, but the install was not as bad as I thought. Let&#x27;s go over it!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fries-get-a-package-manager&quot;&gt;🍟 Get a Package Manager&lt;&#x2F;h2&gt;
&lt;p&gt;There are a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;PowerToys#installing-and-running-microsoft-powertoys&quot;&gt;few ways to install it&lt;&#x2F;a&gt;, but I decided to use &lt;a href=&quot;https:&#x2F;&#x2F;cda.ms&#x2F;1hP&quot;&gt;WinGet&lt;&#x2F;a&gt; because it&#x27;s also new and I hadn&#x27;t used it yet.&lt;&#x2F;p&gt;
&lt;p&gt;My colleague has &lt;a href=&quot;https:&#x2F;&#x2F;www.thomasmaurer.ch&#x2F;2020&#x2F;05&#x2F;how-to-install-winget-windows-package-manager&#x2F;&quot;&gt;detailed instructions&lt;&#x2F;a&gt; for installing WinGet, but the gist of it is you need to be a &lt;a href=&quot;https:&#x2F;&#x2F;cda.ms&#x2F;1hQ&quot;&gt;Windows Insider&lt;&#x2F;a&gt; (free and open for anyone to join, despite how the name might sound 🎃) and then you can download it from the &lt;a href=&quot;ms-windows-store:&#x2F;pdp&#x2F;?productid=9nblggh4nns1&quot;&gt;Windows Store&lt;&#x2F;a&gt;. That&#x27;s the easiest way.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;WinGet is also open source &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;winget-cli&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;pretzel-use-winget-to-install-it&quot;&gt;🥨 Use WinGet to Install It&lt;&#x2F;h2&gt;
&lt;p&gt;When you have it installed, getting PowerToys is a command:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;powershell&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-powershell &quot;&gt;&lt;code class=&quot;language-powershell&quot; data-lang=&quot;powershell&quot;&gt;&lt;span&gt;$ WinGet install powertoys
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Run this in PowerShell, not WSL2&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;pizza-get-used-to-the-features&quot;&gt;🍕 Get Used to the Features&lt;&#x2F;h2&gt;
&lt;p&gt;The very first thing to do is bask in the awesomeness of &amp;quot;PowerToys Run&amp;quot;. Seriously.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Just hit left-alt+space and a search bar pops up.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;You can type anything in there and quickly get to &amp;quot;things&amp;quot; on your computer. That generally means a metric ton of things that you didn&#x27;t even know you had. But it also means ... YOU CAN GET TO YOUR APPS WITH NO CLICKS!!!!🎉🤘🏄‍♀️. &lt;&#x2F;p&gt;
&lt;p&gt;I really missed that from my Mac days.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, hit left-alt+space, type in &amp;quot;PowerToys&amp;quot;, and hit enter. That will take you to something that looks like the standard Windows settings app, but it&#x27;s actually the &lt;em&gt;added&lt;&#x2F;em&gt; features that PowerToys gives.&lt;&#x2F;p&gt;
&lt;p&gt;Check out that left bar. 7 new features plus a &amp;quot;General&amp;quot; thing at the top. I&#x27;m gonna boil each complex feature down into a single sentence, Aaron style™. Here goes:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;rice-general&quot;&gt;🍚 General&lt;&#x2F;h4&gt;
&lt;p&gt;Go here to do general stuff (😂), most importantly getting updates to PowerToys itself.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pie-fancyzones&quot;&gt;🥧 FancyZones&lt;&#x2F;h4&gt;
&lt;p&gt;Automatically resize each window in different patterns on the screen, like side by side or in rows.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Yes, the name is amazing. The zones are indeed fancy 🍰! But the feature overall is pretty slick and powerful once you get the hang of it. You&#x27;ll want to try out a few different setups to figure out the best one for you.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;sushi-file-explorer-preview&quot;&gt;🍣 File Explorer preview&lt;&#x2F;h4&gt;
&lt;p&gt;See rendered previews of SVGs and rendered Markdown in the file explorer. Optional second sentence 💰: I imagine there will be more formats in the future.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;fried-shrimp-image-resizer&quot;&gt;🍤 Image resizer&lt;&#x2F;h4&gt;
&lt;p&gt;Resize any image with a right click from the file explorer.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pineapkeyboard-manager&quot;&gt;🍎Keyboard Manager&lt;&#x2F;h4&gt;
&lt;p&gt;Remap any key or keyboard shortcut from a nice UI.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;watermelon-powerrename&quot;&gt;🍉 PowerRename&lt;&#x2F;h4&gt;
&lt;p&gt;Rename tons and tons of files by highlighting them all and right clicking.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;pineapple-powertoys-run&quot;&gt;🍍 PowerToys run&lt;&#x2F;h4&gt;
&lt;p&gt;The belle of the ball for me - basically Alfred or Spotlight for Mac, but for Windows.&lt;&#x2F;p&gt;
&lt;h4 id=&quot;broccoli-shortcut-guide&quot;&gt;🥦 Shortcut guide&lt;&#x2F;h4&gt;
&lt;p&gt;A full-screen pop-up that reminds you any time of the keyboard shortcuts you have set up.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;place-keep-calm-and-powertoy-on&quot;&gt;🚢 Keep Calm and PowerToy On!&lt;&#x2F;h2&gt;
&lt;p&gt;Of course, I&#x27;m not doing any of these items justice in a single sentence. You can learn a bit more about each one &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;microsoft&#x2F;powertoys#current-powertoy-utilities&quot;&gt;here&lt;&#x2F;a&gt;, and follow the links in each section to go deeper.&lt;&#x2F;p&gt;
&lt;p&gt;Look out for my next post on using PowerToys to make your Mac keyboard work smoother Windows. See you soon!&lt;&#x2F;p&gt;
&lt;p&gt;👋&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Athens on Azure Kubernetes Service... Part 2</title>
          <pubDate>Thu, 07 May 2020 16:18:19 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-azure-2/</link>
          <guid>/blog/athens-azure-2/</guid>
          <description>&lt;p&gt;Howdy all! It&#x27;s been quite a while since I wrote about &lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;athens-on-azure-kubernetes-service&#x2F;&quot;&gt;Athens on Azure Kubernetes Service&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t remember much about it, don&#x27;t worry. I didn&#x27;t either... it&#x27;s been over 6 months! I jumped back into the project, so let&#x27;s recap and look to the future, and this time with plenty of emojis 👍🚀🎉🐿&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-deployment-right-now&quot;&gt;The Deployment Right Now&lt;&#x2F;h2&gt;
&lt;p&gt;The Athens that you see at &lt;a href=&quot;https:&#x2F;&#x2F;athens.azurefd.net&quot;&gt;athens.azurefd.net&lt;&#x2F;a&gt; is running a modern &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&quot;&gt;Athens&lt;&#x2F;a&gt; version, and that&#x27;s about it.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s supposed to be a global deployment, but it does some things that aren&#x27;t great for something like that behind the scenes. Right now, it&#x27;s a proxy, container and a database. We need to be doing more than that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;current-architecture-limitations&quot;&gt;Current Architecture Limitations&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m gonna pull an old PR trick here and hit you with the bad news first, and end on a good note. This section is all about what we &lt;em&gt;can&#x27;t&lt;&#x2F;em&gt; do right now.&lt;&#x2F;p&gt;
&lt;p&gt;In case you forgot, here&#x27;s the current architecture of the deployment: &lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;you ---&amp;gt; Azure Front Door
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;        ACI Container Running Athens
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;        Azure Cosmos DB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;In other words, I&#x27;m running the Athens containers inside of &lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;container-instances&#x2F;&quot;&gt;ACI containers&lt;&#x2F;a&gt; right now. Each container uses some other cloud services to do the rest of their work.&lt;&#x2F;p&gt;
&lt;p&gt;The whole system is a semi-reliable service but comes with some limitations, and when the containers crash, those limitations usually rear their ugly head. When I&#x27;m fixing a crash, I usually say something like &amp;quot;I could fix this if I could set up &lt;code&gt;$USEFUL_NEW_THING&lt;&#x2F;code&gt; ... but ACI doesn&#x27;t let you&amp;quot;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;container-instances&#x2F;&quot;&gt;ACI&lt;&#x2F;a&gt; is generally good for quickly launching one-off containers, specialized workloads, or using as &amp;quot;burst&amp;quot; computing capacity from a Kubernetes cluster (you can do that with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;virtual-kubelet&#x2F;virtual-kubelet&quot;&gt;Virtual Kubelet&lt;&#x2F;a&gt;). They&#x27;re less good for long-running servers that need to scale&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-sins-of-athens&quot;&gt;The Sins of Athens&lt;&#x2F;h2&gt;
&lt;p&gt;Here are some of the worst things that Athens &lt;em&gt;has to do&lt;&#x2F;em&gt; right now:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;git&lt;&#x2F;code&gt; to ask GitHub for a list of tags all the time&lt;&#x2F;li&gt;
&lt;li&gt;Rely on a &lt;code&gt;git&lt;&#x2F;code&gt; operation for potentially thousands of requests at one time&lt;&#x2F;li&gt;
&lt;li&gt;Try to &lt;code&gt;git clone&lt;&#x2F;code&gt; a repo while someone is waiting for their dependency to come back&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;See a pattern? (Pro-tip: don&#x27;t use &lt;code&gt;git&lt;&#x2F;code&gt; as the backbone of your public server. It wasn&#x27;t made for that)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;gopher-break&quot;&gt;Gopher Break!&lt;&#x2F;h2&gt;
&lt;p&gt;This is a long section, so let&#x27;s just take a pause and look at the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;Athens&lt;&#x2F;a&gt; gopher:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;athens-gopher.png&quot; alt=&quot;Athens Gopher&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;back-to-your-regularly-scheduled-programming&quot;&gt;Back to Your Regularly Scheduled Programming&lt;&#x2F;h2&gt;
&lt;p&gt;Most of those things above &amp;quot;don&#x27;t happen that often&amp;quot; because a lot of the stuff that people need is behind a caching proxy, so Athens doesn&#x27;t get the requests that trigger the &lt;code&gt;git&lt;&#x2F;code&gt; operations. But when you run a service long enough, the &amp;quot;don&#x27;t happen that often&amp;quot; things eventually happen, often.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;All those bad things that might happen in production will happen, just like water will eventually carve out the grand canyon.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;When problems crop up, the containers either run out of memory and crash, or just crash because they feel like it. And then I have to hope the cloud restarts them quickly. 🤞&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-kubernetes-thinking&quot;&gt;Why Kubernetes? 🤔&lt;&#x2F;h2&gt;
&lt;p&gt;I asked myself that question. First off, Kubernetes (AKA &amp;quot;k8s&amp;quot;) is complicated. If you get over that complexity, you can get some real power, though.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been working in the Kubernetes space in some capacity since early 2016, so I tried to look at this decision as pragmatically as possible.&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;🍏 PRO: The current setup has outages that greatly concern me&lt;&#x2F;li&gt;
&lt;li&gt;🍏 PRO: k8s gives me more architectural options than ACI&lt;&#x2F;li&gt;
&lt;li&gt;🔴 CON: more options = more moving parts = more complexity = more things to break in production with k8s&lt;&#x2F;li&gt;
&lt;li&gt;🔴 CON: There&#x27;s always something changing or something new to learn in the ecosystem. It&#x27;s confusing!&lt;&#x2F;li&gt;
&lt;li&gt;🍏 PRO: I&#x27;m a bit of a grouchy old man and I&#x27;m confident I&#x27;ll avoid most of the &amp;quot;bleeding edge&amp;quot; stuff that&#x27;s almost certain to break&#x2F;change. But I &lt;em&gt;will&lt;&#x2F;em&gt; nerd out on it.... 😏&lt;&#x2F;li&gt;
&lt;li&gt;🔴 CON: There&#x27;s a lot of YAML to learn (more on this later)&lt;&#x2F;li&gt;
&lt;li&gt;🍏 PRO: I already know a lot of it, enough to not write any this time around (did I mention I&#x27;m grouchy??). More on this later&lt;&#x2F;li&gt;
&lt;li&gt;🍏 PRO: I read a &lt;a href=&quot;https:&#x2F;&#x2F;m.signalvnoise.com&#x2F;seamless-branch-deploys-with-kubernetes&#x2F;&quot;&gt;Basecamp article&lt;&#x2F;a&gt; recently describing their usage of Kubernetes for their new email service. I highly respect their pragmatic view on new technologies&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That&#x27;s 5 pros to 3 cons. Honestly, the Basecamp article tipped me over the edge though. I respect the technical decisions that team makes over almost any other group of people.&lt;&#x2F;p&gt;
&lt;p&gt;Let&#x27;s get to work ⚒.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;the-plan&quot;&gt;The Plan&lt;&#x2F;h2&gt;
&lt;p&gt;In case you forgot the high level architecture from last post, here ya go!&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;you ---&amp;gt; Azure Front Door
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;Kubernetes ingress controller in AKS ---&amp;gt; Athens deployment (pods)
&lt;&#x2F;span&gt;&lt;span&gt;                                                |
&lt;&#x2F;span&gt;&lt;span&gt;                                                |
&lt;&#x2F;span&gt;&lt;span&gt;                                             CosmosDB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Let&#x27;s zoom in on the &lt;code&gt;Athens deployment (pods)&lt;&#x2F;code&gt; part in that diagram. I need to run more services to add some reliability to the system, and that part is where it gets interesting.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hint: Kubernetes is really good at running, maintaining, and connecting services together.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;what-new-services-do-we-need&quot;&gt;What New Services Do We Need?&lt;&#x2F;h2&gt;
&lt;p&gt;The most important service I need is an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;tree&#x2F;master&#x2F;pkg&#x2F;storage&#x2F;external&quot;&gt;external storage server&lt;&#x2F;a&gt; for Athens. You don&#x27;t need to know specifically what that is, just that it&#x27;s an unreleased feature and that I&#x27;m a cutting edge genius for knowing about it.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;homer-i-am-so-smrt.jpg&quot; alt=&quot;i am so smrt&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Or, I worked with the other Athens maintainers on it. I actually didn&#x27;t do that much of the work. I&#x27;m gonna go with genius though 😛.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s another service, too, called the &amp;quot;crawler for Athens&amp;quot;, also known as &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;tree&#x2F;master&#x2F;crathens&quot;&gt;crathens&lt;&#x2F;a&gt;. Same thing here, you don&#x27;t really need to know what that&#x27;s all about for now. I&#x27;ll go deeper into architecture in a future post.&lt;&#x2F;p&gt;
&lt;p&gt;So now we have Athens, the storage service, and crathens. &lt;&#x2F;p&gt;
&lt;p&gt;Dare I say we&#x27;re building &lt;em&gt;microservices&lt;&#x2F;em&gt;????&lt;&#x2F;p&gt;
&lt;h2 id=&quot;progress&quot;&gt;Progress!&lt;&#x2F;h2&gt;
&lt;p&gt;That storage server is gonna be easy to build. Most of the work is &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Copypasta&quot;&gt;copypasta&lt;&#x2F;a&gt;-ing code from Athens and making it my own. That&#x27;s how all open source projects succeed, right?&lt;&#x2F;p&gt;
&lt;p&gt;Crathens will need a little work, but it&#x27;s pretty much good enough™ for now.&lt;&#x2F;p&gt;
&lt;p&gt;So at this point, I&#x27;m working on deploying this stuff. If you haven&#x27;t deployed an app to Kubernetes before, just know that the whole system is written in YAML. I&#x27;m pretty sure they invented their own CPU in YAML.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;re gonna wade into the Kubernetes waters for the first eim, make sure you update your LinkedIn profile before you do. Your old skills don&#x27;t matter. It&#x27;s all &amp;quot;YAML engineer&amp;quot; from here on out.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Just kidding. You can also write &lt;a href=&quot;https:&#x2F;&#x2F;www.terraform.io&#x2F;&quot;&gt;Terraform&lt;&#x2F;a&gt; scripts if you want. I think that makes it a little easier.&lt;&#x2F;p&gt;
&lt;p&gt;So naturally I chose to write YAML.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;ditka-cmon-man.jpg&quot; alt=&quot;cmon man!&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;terraform-github-actions-fried-shrimp&quot;&gt;Terraform + GitHub Actions 🍤&lt;&#x2F;h2&gt;
&lt;p&gt;Just kidding. I went with Terraform 😅.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to cut to the chase, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;tree&#x2F;tf-azure&#x2F;tf&quot;&gt;here are all my Terraform scripts&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here&#x27;s how they work:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;They have a bunch of &lt;a href=&quot;https:&#x2F;&#x2F;www.terraform.io&#x2F;docs&#x2F;configuration&#x2F;variables.html&quot;&gt;input variables&lt;&#x2F;a&gt; in them so that I can control where I deploy to, all from &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;features&#x2F;actions&quot;&gt;GitHub Actions&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;features&#x2F;actions&quot;&gt;GitHub Actions&lt;&#x2F;a&gt; runs on every push I do to that repo. That would be the way I deploy a new version of Athens, for example&lt;&#x2F;li&gt;
&lt;li&gt;The GitHub actions YAML files figure out if it&#x27;s a pull request or the master branch. If it&#x27;s a PR, it deploys to a staging AKS cluster. Otherwise, it deploys to a production one
&lt;ul&gt;
&lt;li&gt;This part is in progress. Check out &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;blob&#x2F;tf-azure&#x2F;.github&#x2F;workflows&#x2F;terraform-pr.yml&quot;&gt;my Terraform file&lt;&#x2F;a&gt;, and shield your eyes from my sheer Terraform brilliance 😂&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;???&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Profit&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;that-s-it&quot;&gt;That&#x27;s It?&lt;&#x2F;h2&gt;
&lt;p&gt;Yup! Terraform, AKS, and all the cloud stuff that&#x27;s backing this make it pretty easy. My Terraform scripts and GitHub actions are just tying them all together.&lt;&#x2F;p&gt;
&lt;p&gt;You already know some of what I&#x27;m using, but below is a complete list for you. I even incuded all the fancy acronyms so you can sound like a boss.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;like-a-boss.jpg&quot; alt=&quot;like a boss&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;container-registry&#x2F;&quot;&gt;Azure container registry (ACR)&lt;&#x2F;a&gt; - for storing all the Docker containers of my code. Not Athens, though. That&#x27;s already &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;gomods&#x2F;athens&quot;&gt;on DockerHub&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;storage&#x2F;&quot;&gt;Azure Storage&lt;&#x2F;a&gt; - for storing the current state of my cluster. Terraform uses that to figure out what to update&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;azure&#x2F;aks&#x2F;intro-kubernetes&quot;&gt;Azure Kubernetes Service (AKS)&lt;&#x2F;a&gt; - for hosting the actual Athens and supporting services&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;frontdoor&#x2F;&quot;&gt;Azure Front Door&lt;&#x2F;a&gt; - for doing all the fancy TLS&#x2F;SSL stuff and caching module ZIP files so my poor little Athens instances don&#x27;t have to serve them up over and over again&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;monitor&#x2F;&quot;&gt;Azure Monitor&lt;&#x2F;a&gt; - for keeping track of all the metrics, tracing, and logging that Athens (and the other services) are going to be spitting out. I haven&#x27;t integrated this in yet, though&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;That should do it! My work continues for now. I still have the storage server to do, and I need to finish up the Terraform scripts.&lt;&#x2F;p&gt;
&lt;p&gt;Check back soon for an update. It&#x27;s gonna be fun to see this thing take shape 😊&lt;&#x2F;p&gt;
&lt;p&gt;Til next time 👋&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>May 2020 Athens Update</title>
          <pubDate>Tue, 05 May 2020 16:29:07 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/2020-05-may-athens-update/</link>
          <guid>/blog/2020-05-may-athens-update/</guid>
          <description>&lt;p&gt;You haven&#x27;t heard from me in a while about &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;Athens&lt;&#x2F;a&gt;. If you use Athens, you&#x27;re probably all like 😒, but hopefully this post will help you be more like 😊. Plus, we&#x27;re more than 4 months into the year and I want to share ✔.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Before you read on, here&#x27;s the Athens Gopher for you, because why not!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;athens-gopher.png&quot; alt=&quot;Athens Gopher&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you just want a summary, we&#x27;ve already had 4 releases this year and I&#x27;m excited about some bigger features coming!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;why-are-you-writing-this&quot;&gt;Why are you writing this?&lt;&#x2F;h2&gt;
&lt;p&gt;Good question! Well, I&#x27;ve been writing code and some docs on &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;the site&lt;&#x2F;a&gt; for a while. I haven&#x27;t actually written about the project in a while 😨. Hopefully this post is helpful 😎.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;releases&quot;&gt;Releases&lt;&#x2F;h2&gt;
&lt;p&gt;We&#x27;ve had 4 releases in 2020, starting with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;releases&#x2F;tag&#x2F;v0.7.1&quot;&gt;v0.7.1&lt;&#x2F;a&gt;. That means mostly bug fixes. The &amp;quot;big&amp;quot; feature is in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;releases&#x2F;tag&#x2F;v0.8.0&quot;&gt;v0.8.0&lt;&#x2F;a&gt; where we added support for &lt;a href=&quot;https:&#x2F;&#x2F;redis.io&#x2F;topics&#x2F;sentinel&quot;&gt;Redis Sentinel&lt;&#x2F;a&gt; as a backend for the distributed coordination system (AKA &amp;quot;single flight&amp;quot;).&lt;&#x2F;p&gt;
&lt;p&gt;The biggest bugfix we made this year was to make it possible to set multiple values in the &lt;code&gt;ATHENS_GO_BINARY_ENV_VARS&lt;&#x2F;code&gt; environment variable (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;issues&#x2F;1404&quot;&gt;#1404&lt;&#x2F;a&gt;)&lt;&#x2F;p&gt;
&lt;h2 id=&quot;coming-soon-to-and-athens-near-you&quot;&gt;Coming Soon to and Athens Near You&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m happy with things so far but I&#x27;m really excited for what&#x27;s coming. For a while now, we&#x27;ve been thinking about how to build two major new features:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A completely-offline mode&lt;&#x2F;li&gt;
&lt;li&gt;Pluggable storages&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;The first one is still &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;discussions&#x2F;1538&quot;&gt;in progress&lt;&#x2F;a&gt; (click that link to go join the discussion).&lt;&#x2F;p&gt;
&lt;p&gt;The second one is .... &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;pull&#x2F;1587&quot;&gt;done&lt;&#x2F;a&gt;! 🥳🥳🥳🥳&lt;&#x2F;p&gt;
&lt;p&gt;We haven&#x27;t released pluggable storages officially, so if you want to try it out, grab one of the recent Docker images from the &lt;a href=&quot;https:&#x2F;&#x2F;hub.docker.com&#x2F;r&#x2F;gomods&#x2F;athens-dev&quot;&gt;athens-dev Docker repo&lt;&#x2F;a&gt; and go for it!&lt;&#x2F;p&gt;
&lt;p&gt;When we do release it (soon), anyone will be able to write a simple HTTP server for their database of choice, and Athens will use it instead of its own built-in storage systems. The server is the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;intro&#x2F;protocol&#x2F;&quot;&gt;standard download API&lt;&#x2F;a&gt; plus a few other APIs.&lt;&#x2F;p&gt;
&lt;p&gt;Athens isn&#x27;t ditching any of its built-in storage systems, but we&#x27;ll likely be building future storage backends as external ones.&lt;&#x2F;p&gt;
&lt;p&gt;Most importantly, &lt;strong&gt;this means that you can use any storage system you want - including internal, proprietary ones - without maintaining your own fork of Athens&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;a-personal-note&quot;&gt;A Personal Note&lt;&#x2F;h2&gt;
&lt;p&gt;I&#x27;m really happy with how things are going. Honestly, by the end of 2019, I was burned out. I had a reflex to get involved with everything in the project. That wasn&#x27;t sustainable for me or the project.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of sustainability, I keep this proverb in my back pocket:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to go fast, go alone. If you want to go far, go together&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;It helps me remember to &lt;em&gt;listen more than I talk&lt;&#x2F;em&gt; and &lt;em&gt;ask for help&lt;&#x2F;em&gt;. Both of those things are easy to do with everyone I&#x27;ve met in the Athens community. You are all smart (whether you think it or not), helpful, and good people.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If I haven&#x27;t met you, feel free to say hi to me. I&#x27;m &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;arschles&quot;&gt;@arschles&lt;&#x2F;a&gt; on Twitter and my DMs are open. Same username on GitHub and the Gophers Slack. Any of those channels work. And, of course I&#x27;d love to see you &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;contributing&#x2F;community&#x2F;participating&#x2F;&quot;&gt;contribute to the project&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I&#x27;ll do more of these updates from here on out, so until next time!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Aaron💚&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>What is Dapr? (Dapr Series #2)</title>
          <pubDate>Mon, 04 May 2020 16:40:09 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/what-is-dapr/</link>
          <guid>/blog/what-is-dapr/</guid>
          <description>&lt;p&gt;Welcome back to the dapr series!&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;arschles&#x2F;how-to-make-your-app-harder-to-write-k35&quot;&gt;Last time&lt;&#x2F;a&gt;, we talked about the challenges of getting software right. If you need a quick refresher:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The easiest thing about software is writing it&lt;&#x2F;li&gt;
&lt;li&gt;The next hardest thing is getting it right&lt;&#x2F;li&gt;
&lt;li&gt;The hardest thing is not crashing and burning in production&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;You might ask this very reasonable question:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;But writing code is pretty hard already. Does it really only get harder from there?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;And I&#x27;ll hit you with one-worder:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;yes&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;production&quot;&gt;Production?&lt;&#x2F;h3&gt;
&lt;p&gt;Before we move on, let me take a second to explain what &amp;quot;production&amp;quot; is:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Distributed_computing&quot;&gt;distributed system&lt;&#x2F;a&gt;. Wikipedia says it best: &lt;code&gt;A distributed system is a system whose components are located on different networked computers&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Your code getting relentlessly pounded by the forces of nature&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;(If you have an app with a DB, you&#x27;re running a distributed system)&lt;&#x2F;p&gt;
&lt;p&gt;In other words, &amp;quot;production&amp;quot; is a harsh, brutal hellscape that&#x27;ll chew your code up and spit it out like it was nothing 😱.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;https:&#x2F;&#x2F;dev-to-uploads.s3.amazonaws.com&#x2F;i&#x2F;x7svgfpd5rdqokswg2rf.jpg&quot; alt=&quot;Alt Text&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;uh-thanks-a-lot-sad-man&quot;&gt;Uh, thanks a lot, sad man!&lt;&#x2F;h2&gt;
&lt;p&gt;Yea, that was a little bit of hyperbole but to be honest, I&#x27;m hitting you with all these scary things to ... scare you. But only a bit.&lt;&#x2F;p&gt;
&lt;p&gt;Why? To get my point across, of course! And to recommend some tools to make your life easier now and down the line.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously I&#x27;m gonna talk about Dapr here, but here are other good tools you can and should consider. Here are some I can personally recommend from experience:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Platform_as_a_service&quot;&gt;PaaS systems&lt;&#x2F;a&gt; take your code, run it on some internal&#x2F;proprietary system, and give you a few APIs you can call to do useful things.
&lt;ul&gt;
&lt;li&gt;Azure App Services, Google App Engine, Heroku&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Function_as_a_service&quot;&gt;FaaS platforms&lt;&#x2F;a&gt; let you upload a function in the language of your choice to the system, and tell it when to run the function (e.g. an HTTP request to &lt;code&gt;GET &#x2F;dostuff&lt;&#x2F;code&gt;)
&lt;ul&gt;
&lt;li&gt;Azure Functions, Google Cloud Functions&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Orchestration_%28computing%29&quot;&gt;Orchestrators&lt;&#x2F;a&gt; take your code and make sure it&#x27;s always running exactly as you tell it to
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;kubernetes.io&#x2F;&quot;&gt;Kubernetes&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;www.nomadproject.io&#x2F;&quot;&gt;Nomad&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Service_mesh&quot;&gt;Service meshes&lt;&#x2F;a&gt; take care of making talking over any network more reliable. You get &lt;a href=&quot;https:&#x2F;&#x2F;www.codeproject.com&#x2F;articles&#x2F;326574&#x2F;an-introduction-to-mutual-ssl-authentication&quot;&gt;mTLS&lt;&#x2F;a&gt; for free too 🎉
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;linkerd.io&#x2F;&quot;&gt;LinkerD&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;dapr&quot;&gt;Dapr&lt;&#x2F;h2&gt;
&lt;p&gt;Dapr takes some of the above things and builds on some others. The idea is that you run this &lt;code&gt;daprd&lt;&#x2F;code&gt; process (and maybe some others) &amp;quot;next to&amp;quot; your code (on &lt;code&gt;localhost&lt;&#x2F;code&gt;). You talk to this thing via a standard API (HTTP or &lt;a href=&quot;https:&#x2F;&#x2F;grpc.io&quot;&gt;gRPC&lt;&#x2F;a&gt;) to get lots of things done:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;service-invocation&#x2F;README.md&quot;&gt;Service discovery&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Getting the IP for the REST API that you need, when all you know is the name. Kind of like DNS for your app, but designed for apps in production instead of billions of connected devices on the internet. Design tradeoffs, amirite!?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;state-management&#x2F;README.md&quot;&gt;Databases&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Storing your data somewhere, of course :)&lt;&#x2F;li&gt;
&lt;li&gt;Dapr calls these &amp;quot;State Stores&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;publish-subscribe-messaging&#x2F;README.md&quot;&gt;Publish&#x2F;subscribe&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;One part of your app broadcasts &amp;quot;I did X&amp;quot; into the world. Another app hears it and does things&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;bindings&#x2F;README.md&quot;&gt;Bindings&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Automatically calling your app when something in the outside world happens&lt;&#x2F;li&gt;
&lt;li&gt;Or automatically calling something in the outside world when your app does something&lt;&#x2F;li&gt;
&lt;li&gt;These have some overlap with publish&#x2F;subscribe &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;actors&#x2F;README.md&quot;&gt;Actors&lt;&#x2F;a&gt; 
&lt;ul&gt;
&lt;li&gt;Write pieces of your app like it&#x27;s running on their own, and let dapr run each piece in the right place at the right time (or keep them running forever!)&lt;&#x2F;li&gt;
&lt;li&gt;These are great if you need to break your app up into tons of little pieces&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;observability&#x2F;README.md&quot;&gt;Observability&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Collecting logs and various metrics so you can figure out how to fix your app when it spectacularly fails :)&lt;&#x2F;li&gt;
&lt;li&gt;and so you can figure out how it&#x27;s doing before it fails!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;docs&#x2F;blob&#x2F;master&#x2F;concepts&#x2F;secrets&#x2F;README.md&quot;&gt;Secrets management&lt;&#x2F;a&gt;
&lt;ul&gt;
&lt;li&gt;Safely storing your database password, API keys, and other stuff you&#x27;d rather not check into your GitHub repo &lt;&#x2F;li&gt;
&lt;li&gt;&lt;em&gt;And&lt;&#x2F;em&gt;, getting them back when your app starts up&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Each of these things is a standard API backed by pluggable implementations. Dapr takes care of the dirty details behind the scenes. Database implementations take care of getting the right SDK, hooking you to the right DB in the right place, helping you make sure you don&#x27;t accidentally overwrite or corrupt your data at runtime, and more.&lt;&#x2F;p&gt;
&lt;p&gt;As I write this, there are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;components-contrib&#x2F;tree&#x2F;master&#x2F;state&quot;&gt;15 implementations&lt;&#x2F;a&gt; you can use for Databases, from Aerospike to Zookeeper.&lt;&#x2F;p&gt;
&lt;p&gt;Possibly most importantly, you don&#x27;t have to pick up any SDKs to use any of this stuff. It&#x27;s compiled into Dapr itself.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;next-up&quot;&gt;Next up&lt;&#x2F;h2&gt;
&lt;p&gt;If you took nothing else away, just remember that you should pick up &amp;quot;distributed systems tools&amp;quot; to help you build your app. Even if you don&#x27;t think that your app is a &amp;quot;hardcore distributed system&amp;quot;. The best ones will make your life easier &lt;em&gt;while you&#x27;re building it&lt;&#x2F;em&gt;, too.&lt;&#x2F;p&gt;
&lt;p&gt;I think Dapr is one of those tools. Right when I start building, I can just use the simple(ish) REST API for my database instead of picking up some new SDK and reading all the DB docs up front. Same goes for some of the other features, they just come later. I recommend giving &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;dapr&quot;&gt;the README&lt;&#x2F;a&gt; a skim and seeing if it fits your app.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>So you want to build a computer</title>
          <pubDate>Mon, 27 Apr 2020 16:42:35 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/build-a-computer/</link>
          <guid>/blog/build-a-computer/</guid>
          <description>&lt;p&gt;I once built a cheap tower with clearance parts from Micro Center. See if you can guess how long ago it was from the specs:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CPU: Intel Pentium 3&lt;&#x2F;li&gt;
&lt;li&gt;RAM: 16GB&lt;&#x2F;li&gt;
&lt;li&gt;Disk: 128mb&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I haven&#x27;t done much DIY electronic stuff since then except for some IoT tinkering and playing around with drones. So when I realized I need a &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;arschles&#x2F;how-to-wsl-516d&quot;&gt;Windows machine for work&lt;&#x2F;a&gt;, I decided I was gonna get back into the DIY game and build one from scratch.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s my story.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;why-build-from-scratch&quot;&gt;Why build from scratch?&lt;&#x2F;h2&gt;
&lt;p&gt;If computers are your profession, it&#x27;s nice to choose all your tools so you get exactly what you need. Just like if you look in a carpenter&#x27;s toolbox you&#x27;ll see tools from tons of different brands. They chose exactly what they need to do their best work.&lt;&#x2F;p&gt;
&lt;p&gt;Also, you have to put time and work in up front but you come out with a completely custom computer for cheaper than what you can buy off the shelf. And you know all the ins and outs of the thing, so you can upgrade individual pieces in the future.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;how-to-started&quot;&gt;How to started&lt;&#x2F;h2&gt;
&lt;p&gt;Luckily I knew the basics of a computer, so I sketched out the pieces I need. If you&#x27;re unfamiliar, here they are with crude definitions:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Motherboard - holds everything together (CPU, memory, hard drive, fans, peripherals)&lt;&#x2F;li&gt;
&lt;li&gt;CPU - &lt;strong&gt;C&lt;&#x2F;strong&gt;entral &lt;strong&gt;P&lt;&#x2F;strong&gt;rocessing &lt;strong&gt;U&lt;&#x2F;strong&gt;nit - does all the fancy things that runs your OS, compiles code, etc...&lt;&#x2F;li&gt;
&lt;li&gt;Memory (AKA: RAM) - your processor and programs you build and&#x2F;or run use this to store temporary data&lt;&#x2F;li&gt;
&lt;li&gt;Hard Disk - where your files live, forever&lt;&#x2F;li&gt;
&lt;li&gt;Case&#x2F;Fans - The actual place you put all this stuff. Fans are mounted to the case (and the CPU) to cool all these electronics off while they&#x27;re running&lt;&#x2F;li&gt;
&lt;li&gt;Video card - you plug this into the motherboard and the other side has hookups for your monitor. Some Motherboards have this built in.&lt;&#x2F;li&gt;
&lt;li&gt;Sound card - just like the video card but for sound. Just about all consumer-grade motherboards have this built in&lt;&#x2F;li&gt;
&lt;li&gt;PSU - &lt;strong&gt;P&lt;&#x2F;strong&gt;ower &lt;strong&gt;S&lt;&#x2F;strong&gt;upply &lt;strong&gt;U&lt;&#x2F;strong&gt;nit. This is what you plug into the wall. The other side has wires with different connectors that attach to the video card, fans, motherboard, etc...&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;how-to-choose-parts&quot;&gt;How to choose parts&lt;&#x2F;h2&gt;
&lt;p&gt;I started by thinking about the main reasons for this computer. I don&#x27;t game and this was gonna be for work, so my needs were mostly compiling code and running lots of VMs. That means more CPU cores and memory and you can skimp on the video card (which can be a big cost). More CPU cores usually means a more expensive motherboard too, so I had to budget for that too.&lt;&#x2F;p&gt;
&lt;p&gt;My big purchase was an AMD Ryzen Threadripper CPU. I had been saving up for a while so I could get one. Most of the work was figuring out what kind of motherboard I would need and how much power I would need from the PSU.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;&#x2F;h3&gt;
&lt;p&gt;Once I had a more specific idea of what I needed, I went to &lt;a href=&quot;https:&#x2F;&#x2F;newegg.com&quot;&gt;NewEgg&lt;&#x2F;a&gt; to look at general prices for things. They also have some nifty tools like the &lt;a href=&quot;https:&#x2F;&#x2F;www.newegg.com&#x2F;tools&#x2F;power-supply-calculator&#x2F;&quot;&gt;power supply calculator&lt;&#x2F;a&gt; that can help you decide what to buy.&lt;&#x2F;p&gt;
&lt;p&gt;I also went to &lt;a href=&quot;https:&#x2F;&#x2F;buildapc.reddit.com&quot;&gt;&lt;code&gt;&#x2F;r&#x2F;buildapc&lt;&#x2F;code&gt; on Reddit&lt;&#x2F;a&gt; to see reviews. You can also ask questions there if you want.&lt;&#x2F;p&gt;
&lt;p&gt;Amazon had cheaper prices so I bought everything there. I would have gone to a physical store like to look at parts, but there wasn&#x27;t a good option in my area.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;During the COVID pandemic, please research and buy everything online so you don&#x27;t have to go out, even if you do have a parts store near you that&#x27;s open&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;do-the-build&quot;&gt;Do the build!&lt;&#x2F;h2&gt;
&lt;p&gt;I made too many mistakes to list here, so I&#x27;ll share the advice I learned from it all:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Read through the entire step-by-step process for the motherboard, CPU and fan before doing anything&lt;&#x2F;li&gt;
&lt;li&gt;Don&#x27;t force anything except for the memory. You&#x27;ll probably need to apply pressure there until it clicks&lt;&#x2F;li&gt;
&lt;li&gt;Do everything on a clean table if you can. Don&#x27;t kill your back doing it on the floor like I did 🤣&lt;&#x2F;li&gt;
&lt;li&gt;Attach the PSU before anything else&lt;&#x2F;li&gt;
&lt;li&gt;... then attach the CPU and fan before you screw in the motherboard&lt;&#x2F;li&gt;
&lt;li&gt;... then plug in all your components&lt;&#x2F;li&gt;
&lt;li&gt;... then put in the hard disk, video card, etc...&lt;&#x2F;li&gt;
&lt;li&gt;When you&#x27;re plugging things in, the wires will come from the PSU up to the motherboard and other components (often the hard disk and video card). Try to run  everything below the motherboard
&lt;ul&gt;
&lt;li&gt;Some cases come with zip-ties and loops so you can bunch wires together and attach them in place to the case. Use everything you can to organize the wires. You&#x27;ll be happy you did later!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;enjoy&quot;&gt;Enjoy!&lt;&#x2F;h2&gt;
&lt;p&gt;I got all my components in January and I&#x27;ve had the thing running since February.  As I said, I made a bunch of mistakes along the way. I still have things to improve (liquid-cooling and a bigger PSU for example!), but I&#x27;m actually &lt;em&gt;happy&lt;&#x2F;em&gt; I do. This is both a tool and a hobby!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m really excited to get creative and keep my machine healthy and up to date as time passes.&lt;&#x2F;p&gt;
&lt;p&gt;If you do a build, I hope you get the same amount of joy out of it as I am 😊&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>What Happens to Your Career After 10 Years?</title>
          <pubDate>Thu, 23 Apr 2020 16:41:19 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/career-10-years/</link>
          <guid>/blog/career-10-years/</guid>
          <description>&lt;p&gt;I see a lot of great career posts on how to get into programming, and some about how to get into senior engineer roles. I&#x27;m coming up on 15 years as a software engineer now and I&#x27;ve been thinking lately about how I got here. I&#x27;m not an expert by any measure but I wanted to share some advice I&#x27;ve learned.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;new-positions-are-scary-you-should-take-them-anyway&quot;&gt;New positions are scary, you should take them anyway&lt;&#x2F;h2&gt;
&lt;p&gt;I worked right out of college at Microsoft as the only junior engineer on a team of database engineers. I &amp;quot;knew&amp;quot; I wasn&#x27;t good enough going in. I was terrified. I was going to get fired on day two.&lt;&#x2F;p&gt;
&lt;p&gt;By the time I had left that job, I felt like I had mastered C++ (not even close, but I learned a lot and it felt really good 😂) and made some good friends.&lt;&#x2F;p&gt;
&lt;p&gt;The next job I took was at a gaming startup that was growing fast. I had no idea what I would be doing and had no experience with startups. The interviewers assumed I had intimate knowledge a million technologies I had never touched.&lt;&#x2F;p&gt;
&lt;p&gt;I guess they didn&#x27;t care? On my first day, I realized how much I had to learn before I could get started. Over the years I worked there, I learned what I had to learn and ended up working on a lot of new things:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Deploying code to millions of users&lt;&#x2F;li&gt;
&lt;li&gt;Testing in production&lt;&#x2F;li&gt;
&lt;li&gt;Leading a data ingestion system&lt;&#x2F;li&gt;
&lt;li&gt;A live gaming server&lt;&#x2F;li&gt;
&lt;li&gt;A Cocoa&#x2F;Objective-C gaming engine&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;lessons-i-still-carry-with-me&quot;&gt;Lessons I still carry with me&lt;&#x2F;h3&gt;
&lt;p&gt;I keep having this experience where I run up against a job that would have me doing things I have no idea about and I still get scared.&lt;&#x2F;p&gt;
&lt;p&gt;All these experiences taught me two lessons I still carry with me:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;The people who are &amp;quot;programming gods&amp;quot; are still human. If you ask them nicely for help or advice, they&#x27;ll happily help you. People in our industry want to pass on their skills&lt;&#x2F;li&gt;
&lt;li&gt;All the code that&#x27;s ever been written was built by a person. I (and you!) can figure out what they figured out, if I have the time and energy&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h2 id=&quot;you-should-write-less-code-as-you-progress&quot;&gt;You should write less code as you progress&lt;&#x2F;h2&gt;
&lt;p&gt;Non-tech skills are really important. In my first senior role, I felt like I had to work twice as hard and twice the hours. I actually did that for a while too!&lt;&#x2F;p&gt;
&lt;p&gt;Obviously, 16 hour days burn you out pretty quickly, so I pulled back on that and had to figure something else out.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Working smarter, not harder, is really easy in software roles&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I realized that about 5 years ago, but I think I&#x27;ll be practicing it forever.&lt;&#x2F;p&gt;
&lt;p&gt;The best ways to work smarter don&#x27;t involve code, they involve people.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;learn-about-new-technologies&quot;&gt;Learn about new technologies&lt;&#x2F;h3&gt;
&lt;p&gt;New technologies can give you big advantages in your career. You might be able to accomplish big projects quickly by picking up that new web framework. Or you might get that new job or higher title because you were an expert in that new language before most other people.&lt;&#x2F;p&gt;
&lt;p&gt;Learning new tech will &lt;em&gt;always&lt;&#x2F;em&gt; give you two more handy skills:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Spotting vaporware quickly&lt;&#x2F;li&gt;
&lt;li&gt;Being practical about where (or if!) the new tech fits into your stack&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;write-and-teach-more&quot;&gt;Write and teach more&lt;&#x2F;h3&gt;
&lt;p&gt;Whatever you learn, teach it to someone else. Here are some ideas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Write internal documents for your company&lt;&#x2F;li&gt;
&lt;li&gt;Blog posts&lt;&#x2F;li&gt;
&lt;li&gt;Pair programming&lt;&#x2F;li&gt;
&lt;li&gt;Screencasting or live coding&lt;&#x2F;li&gt;
&lt;li&gt;Workshops&lt;&#x2F;li&gt;
&lt;li&gt;Talking about something you&#x27;re working on at lunch&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Any of these things will help other people work smarter too. Teaching actually helps you take your knowledge to the next level, too.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;build-communities&quot;&gt;Build communities&lt;&#x2F;h3&gt;
&lt;p&gt;This is kind of like teaching, but at a larger scale. Instead of passing your knowledge on, you&#x27;ll teach other people how to pass &lt;em&gt;their knowledge&lt;&#x2F;em&gt; on.&lt;&#x2F;p&gt;
&lt;p&gt;Think about an open source project. People start contributing because they&#x27;re trying to get the same general thing done. If they want to solve something specific, they learn the best ways to do it, write up a GitHub issue, and teach other contributors what they learned.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re the maintainer of the open source project, you wouldn&#x27;t be telling anybody to do this, you&#x27;d just be making sure that they &lt;em&gt;can&lt;&#x2F;em&gt; do it.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s what building communities is all about, and it&#x27;s not just about open source projects. These days we have to do things virtually, so you could set up a virtual brown bag lunch at your work, help out at a local meetup (the organizers always need help), or write an article series and encourage folks to contribute (here&#x27;s fun example: &lt;a href=&quot;https:&#x2F;&#x2F;testing.googleblog.com&#x2F;2007&#x2F;01&#x2F;introducing-testing-on-toilet.html&quot;&gt;testing on the toilet&lt;&#x2F;a&gt;). &lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-focus-too-much-on-the-future&quot;&gt;Don&#x27;t focus too much on the future!&lt;&#x2F;h2&gt;
&lt;p&gt;Plans are guesses in disguise. In real life, if I knew what I would be doing for the last 15 years, I would have been too scared to even try.&lt;&#x2F;p&gt;
&lt;p&gt;These days, I don&#x27;t spend much time focusing on what I want to do more than about a year out.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;make-sure-you-still-dream&quot;&gt;Make sure you still dream!&lt;&#x2F;h2&gt;
&lt;p&gt;Plans are different from dreams, though! Imagine you want to become the CTO of a startup. A plan would be to do project1, become VP of engineering at company2, then become an architect at startup3, then move up to CTO at startup4. &lt;&#x2F;p&gt;
&lt;p&gt;In real life, most of those things won&#x27;t happen in that order. &lt;&#x2F;p&gt;
&lt;p&gt;A dream would be different. It would be more like &amp;quot;I want to become the CTO at a startup, but I bet I need to move a few levels up before I can get there. Maybe I&#x27;ll even need to go to a few different companies&amp;quot;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to make your app harder to build (Dapr Series #1)</title>
          <pubDate>Tue, 21 Apr 2020 11:13:56 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/how-to-make-your-app-harder-to-build/</link>
          <guid>/blog/how-to-make-your-app-harder-to-build/</guid>
          <description>&lt;blockquote&gt;
&lt;p&gt;This is the first post in a series about &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;dapr&quot;&gt;Dapr&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Software is very easy to write. For lots of us programmers, it&#x27;s really exciting to create something &lt;em&gt;new&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Then it grows and changes over time. Like the foundation of a house, it looks indestructible at first, but the earth has forever to try and break it. It moves, causes cracks and eventually water leaks. The same thing happens to software.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Software is easy to write but hard to get right&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;challenges-of-distributed-systems&quot;&gt;Challenges of distributed systems&lt;&#x2F;h2&gt;
&lt;p&gt;Look at any non-trivial, modern app, and you might be able to find 25% of the bugs that it has in it. You&#x27;ll have no idea about the rest of them, because time and pressure can only find them.&lt;&#x2F;p&gt;
&lt;p&gt;Your app gets twice as hard to get right if it has to talk to other APIs, databases or microservices. Then you&#x27;ll hit network blips, slow APIs, out-of-memory errors, and so on. In other words, the world will eventually find a way to break your app. You have to find a way to handle all of that.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;frameworks&quot;&gt;Frameworks&lt;&#x2F;h2&gt;
&lt;p&gt;That&#x27;s where distributed frameworks come in. For modern distributed systems, these are sidecar processes&#x2F;containers that give your app a simple API that helps you deal with these things. &lt;&#x2F;p&gt;
&lt;p&gt;They help you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Save things to a database
&lt;ul&gt;
&lt;li&gt;Even if there&#x27;s a problem with the database&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Talk to other services
&lt;ul&gt;
&lt;li&gt;Even when you have to try again&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Tell you what services your app is talking to
&lt;ul&gt;
&lt;li&gt;Even when you can&#x27;t tell from the code&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Responding to events like pub&#x2F;sub
&lt;ul&gt;
&lt;li&gt;Even when you don&#x27;t expect that many events&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;dapr&#x2F;dapr&quot;&gt;Dapr&lt;&#x2F;a&gt; a new framework that can help with these things. Stay tuned for my next post to learn more!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Crystal Fridays</title>
          <pubDate>Fri, 17 Apr 2020 11:02:32 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/crystal-fridays/</link>
          <guid>/blog/crystal-fridays/</guid>
          <description>&lt;p&gt;If you&#x27;ve been following my &lt;a href=&quot;https:&#x2F;&#x2F;twitch.com&#x2F;arschles&quot;&gt;Twitch&lt;&#x2F;a&gt; you&#x27;ve probably seen that I&#x27;m doing &amp;quot;Crystal Fridays&amp;quot; live streams for the past few weeks.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Catch me every Friday at 10am US Pacific time &lt;a href=&quot;https:&#x2F;&#x2F;twitch.tv&#x2F;arschles&quot;&gt;on Twitch&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;what-is-crystal-fridays&quot;&gt;What is Crystal Fridays?&lt;&#x2F;h2&gt;
&lt;p&gt;The premise is simple. I live code writing apps in &lt;a href=&quot;https:&#x2F;&#x2F;crystal-lang.org&quot;&gt;Crystal&lt;&#x2F;a&gt;. Currently I&#x27;m working on &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;cprox&quot;&gt;cprox&lt;&#x2F;a&gt; - a simple URL shortener that&#x27;s implemented with a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;CodeSteak&#x2F;Nuummite&quot;&gt;little persistent key-value store&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you don&#x27;t know about Crystal, it&#x27;s a language that looks like Ruby but it&#x27;s &lt;em&gt;compiled&lt;&#x2F;em&gt;. I&#x27;m really enjoying it and I encourage you to check it out if you have a spare moment.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;join-me&quot;&gt;Join me!&lt;&#x2F;h2&gt;
&lt;p&gt;If you&#x27;re interested in participating in Crystal Fridays, come watch me live code:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When: 10am US Pacific Time&lt;&#x2F;strong&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Where: &lt;a href=&quot;https:&#x2F;&#x2F;twitch.tv&#x2F;arschles&quot;&gt;twitch.tv&#x2F;arschles&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;
&lt;ul&gt;
&lt;li&gt;Tip: you can use &lt;a href=&quot;https:&#x2F;&#x2F;time.is&#x2F;compare&quot;&gt;time.is&#x2F;compare&lt;&#x2F;a&gt; to find out what time that is in your time zone&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;recordings&quot;&gt;Recordings&lt;&#x2F;h2&gt;
&lt;p&gt;I record every Crystal Fridays stream and upload it to &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;playlist?list=PLd5PS0DQ17GBxMQ78nmowkAxilgS_LrNB&quot;&gt;YouTube&lt;&#x2F;a&gt;, so you can catch up if you miss one.&lt;&#x2F;p&gt;
&lt;p&gt;Videos should be up shortly after each stream.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>How to WSL 2</title>
          <pubDate>Mon, 30 Mar 2020 17:21:48 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/wsl2-how-to/</link>
          <guid>/blog/wsl2-how-to/</guid>
          <description>&lt;p&gt;Welcome, Linux-on-Windows-er!&lt;&#x2F;p&gt;
&lt;p&gt;I recently wrote about my &lt;a href=&quot;&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&quot;&gt;experience with WSL 2 and Windows&lt;&#x2F;a&gt;, coming from a &lt;a href=&quot;&#x2F;blog&#x2F;wsl2-how-to&#x2F;.&#x2F;mac-dev-setup&quot;&gt;Mac perspective&lt;&#x2F;a&gt;. This is an unboxing post for how I went from vanilla Windows 10 to a full working dev environment based on &lt;a href=&quot;https:&#x2F;&#x2F;code.visualstudio.com&#x2F;&quot;&gt;VS Code&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;wsl2-index&quot;&gt;WSL 2&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you&#x27;re like me and were initially too lazy to read that link above, WSL 2 stands for &amp;quot;&lt;strong&gt;W&lt;&#x2F;strong&gt;indows &lt;strong&gt;S&lt;&#x2F;strong&gt;ubsystem for &lt;strong&gt;Linux&lt;&#x2F;strong&gt;&amp;quot;. The first version, WSL 1, was a syscall emulator that gave Linux programs most of the POSIX APIs they need.&lt;&#x2F;p&gt;
&lt;p&gt;WSL 2 is a lightweight hypervisor that actually runs a full Linux kernel.&lt;&#x2F;p&gt;
&lt;p&gt;In other words, WSL 2 is better than WSL 1 in a lot of ways.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;the-pre-reqs&quot;&gt;The pre-reqs&lt;&#x2F;h2&gt;
&lt;p&gt;At the moment, you have to jump through a hoop to get your Windows 10 setup to get WSL 2 working.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;join-windows-insiders&quot;&gt;Join Windows Insiders&lt;&#x2F;h3&gt;
&lt;blockquote&gt;
&lt;p&gt;WSL 2 will be stable and generally available in the &lt;a href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;commandline&#x2F;wsl2-will-be-generally-available-in-windows-10-version-2004&#x2F;&quot;&gt;upcoming Windows 2004 release&lt;&#x2F;a&gt;. When that new release comes out, you won&#x27;t need to be on Insiders and you&#x27;ll be able to ignore this whole section. I&#x27;m excited for that!&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Being an Insider sounds exclusive but anyone can join. It just means that you&#x27;ll run less stable versions of Windows. I haven&#x27;t had any problems with it yet, though.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s a cool tradeoff here, though. You run a development build of the OS, so you get new features in development. Such as ... drumroll please ... WSL 2!&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s pretty easy to get on Windows Insiders. Go &lt;a href=&quot;https:&#x2F;&#x2F;insider.windows.com&#x2F;en-us&#x2F;register&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;, sign up, and follow the instructions.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-wsl-1&quot;&gt;Get WSL 1&lt;&#x2F;h3&gt;
&lt;p&gt;You need WSL 1 for WSL 2 to work. That process is basically a PowerShell command and downloading&#x2F;running a distro from the &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;store&#x2F;apps&#x2F;windows&quot;&gt;Microsoft app store&lt;&#x2F;a&gt;. Yes, the app store has Linux distros. I use &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;p&#x2F;debian&#x2F;9msvkqc78pk6?activetab=pivot:overviewtab&quot;&gt;Debian&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;p&#x2F;ubuntu&#x2F;9nblggh4msv6?activetab=pivot:overviewtab&quot;&gt;Ubuntu&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;After you download it, make sure to fire up your distro at least once before you move on. Windows requires that, but also it&#x27;ll let you set up your username and password on startup, which is handy. It&#x27;ll look a little like this:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl2-how-to&#x2F;step00.png&quot; alt=&quot;firing up Debian&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice how many times I mistyped my password =P&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Check out the &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;wsl2-install&quot;&gt;instructions&lt;&#x2F;a&gt; for all the details.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;wsl2-ify-your-distro&quot;&gt;WSL2-ify your distro&lt;&#x2F;h3&gt;
&lt;p&gt;Now it&#x27;s time for your WSL 1 distro to grow up and get a real job! Convert it to WSL 2 with a PowerShell command. Make sure you run your PowerShell as an administrator.&lt;&#x2F;p&gt;
&lt;p&gt;For Debian, which is my jam, it&#x27;s &lt;code&gt;wsl --set-version Debian 2&lt;&#x2F;code&gt;. You can swap out &lt;code&gt;Debian&lt;&#x2F;code&gt; for your distro name and it&#x27;ll work just the same. It should look like this in PowerShell:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl2-how-to&#x2F;step01.png&quot; alt=&quot;upgrading a distro to wsl 2&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Check out that &lt;code&gt;wsl -l&lt;&#x2F;code&gt; command in the above screenshot. You can always run that to see the distros you have installed&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;If you ever need to delete a distro, you can always run &lt;code&gt;wsl --unregister Debian&lt;&#x2F;code&gt; and it&#x27;ll just disappear. That means everything in the VM is gone and you&#x27;ll have to re-install your distro from the app store, so be careful with that one.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-windows-terminal&quot;&gt;Get Windows terminal&lt;&#x2F;h3&gt;
&lt;p&gt;I mentioned &lt;a href=&quot;https:&#x2F;&#x2F;devblogs.microsoft.com&#x2F;commandline&#x2F;introducing-windows-terminal&#x2F;&quot;&gt;Windows terminal&lt;&#x2F;a&gt; in my &lt;a href=&quot;&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&quot;&gt;previous post about WSL 2&lt;&#x2F;a&gt;. I really, super, highly recommend it for a bunch of reasons:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;It&#x27;s a modern Windows app, so it has the most important thing: a cool icon&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;expertsinside&#x2F;how-to-customize-the-new-windows-terminal-with-visual-studio-code-56b1&quot;&gt;customizable&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;It&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Microsoft&#x2F;Terminal&quot;&gt;open source&lt;&#x2F;a&gt;!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Back in the day, you had to download the installer from GitHub and install it yourself. That wasn&#x27;t terrible by any means, but I&#x27;m super lazy and it felt like a chore. I&#x27;m happy to report you can now get it from the &lt;a href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;p&#x2F;windows-terminal-preview&#x2F;9n0dx20hk701?activetab=pivot:overviewtab&quot;&gt;app store&lt;&#x2F;a&gt; with a click.&lt;&#x2F;p&gt;
&lt;p&gt;The app ties together the traditional command prompt (&lt;code&gt;cmd&lt;&#x2F;code&gt;), PowerShell, &lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;features&#x2F;cloud-shell&#x2F;&quot;&gt;Azure cloud shell&lt;&#x2F;a&gt;, and all your installed Linux distros. When you fire up the app, it&#x27;ll have a tab that&#x27;s probably open to PowerShell. Go click on the dropdown arrow next to the tab, choose your distro, and BAM, you&#x27;re in a Linux terminal.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl2-how-to&#x2F;step02.png&quot; alt=&quot;opening your distro in Windows Terminal&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;congratulations-you-re-wsl2-ing&quot;&gt;Congratulations, you&#x27;re WSL2-ing!&lt;&#x2F;h2&gt;
&lt;p&gt;Now that you&#x27;re in a Linux terminal, you can start acting all Linux-ey and do everything on the command line with bash.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;set-your-distro-up&quot;&gt;Set your distro up&lt;&#x2F;h3&gt;
&lt;p&gt;I mentioned in my &lt;a href=&quot;&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&quot;&gt;previous post&lt;&#x2F;a&gt; that I use &lt;a href=&quot;https:&#x2F;&#x2F;yadm.io&#x2F;#&quot;&gt;Yadm&lt;&#x2F;a&gt; to configure everything. Yadm works on all Linux OSes, but I&#x27;m gonna be talking about Debian&#x2F;Ubuntu distros.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s how to bootstrap your install with Yadm so you can download all your dotfiles:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ sudo apt update
&lt;&#x2F;span&gt;&lt;span&gt;$ sudo apt install yadm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;On almost all setups, you&#x27;ll need to run these two commands with &lt;code&gt;sudo&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here&#x27;s what it looks like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl2-how-to&#x2F;step03.png&quot; alt=&quot;bootstrapping&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;ssh-keys&quot;&gt;SSH keys&lt;&#x2F;h4&gt;
&lt;p&gt;From here on out, you&#x27;ll need to interact with GitHub repos a lot. So you&#x27;ll need to set up your SSH keys and add them to GitHub. IF you don&#x27;t know what to do, follow GitHub&#x27;s guides in order:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;help.github.com&#x2F;en&#x2F;github&#x2F;authenticating-to-github&#x2F;generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent&quot;&gt;Generating a new SSH key&lt;&#x2F;a&gt;. Make sure to click on the &lt;em&gt;Linux&lt;&#x2F;em&gt; tab at the top of that page, not the Windows one.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;help.github.com&#x2F;en&#x2F;github&#x2F;authenticating-to-github&#x2F;adding-a-new-ssh-key-to-your-github-account&quot;&gt;Adding your SSH key to GitHub&lt;&#x2F;a&gt;. Again, make sure to click the Linux tab. In the first section, they say to use &lt;code&gt;xclip&lt;&#x2F;code&gt; but that won&#x27;t work on WSL 2. Instead, run &lt;code&gt;cat ~&#x2F;.ssh&#x2F;id_rsa.pub&lt;&#x2F;code&gt;, highlight the whole public key that gets spit out, and press &lt;code&gt;shift&lt;&#x2F;code&gt;-&lt;code&gt;ctrl&lt;&#x2F;code&gt;-&lt;code&gt;c&lt;&#x2F;code&gt;. That&#x27;s how you copy something out of WSL 2 so you can paste it somewhere else (including Windows apps)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;dotfiles#create-ssh-keys&quot;&gt;Here&lt;&#x2F;a&gt; are my super condensed instructions for how to do the SSH key dance, if you prefer.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;get-those-dotfiles&quot;&gt;Get those dotfiles!&lt;&#x2F;h3&gt;
&lt;p&gt;Now we have yadm, let&#x27;s do my favorite part: run one command and watch magic:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ yadm clone https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;dotfiles.git
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;You can swap out my dotfiles repo name for yours. If you want to use mine, that&#x27;s all good too (PRs welcome!)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Here&#x27;s what it looks like:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl2-how-to&#x2F;step04.png&quot; alt=&quot;opening your distro in Windows Terminal&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;what-s-next&quot;&gt;What&#x27;s next&lt;&#x2F;h2&gt;
&lt;p&gt;You&#x27;re in a Linux terminal using Windows Terminal, running on a hypervisor, on top of Windows 10. WAT. I know, right?&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll shamelessly plug my &lt;a href=&quot;&#x2F;blog&#x2F;coming-from-a-mac-to-windows-wsl-2&quot;&gt;other post&lt;&#x2F;a&gt; &lt;em&gt;again&lt;&#x2F;em&gt; here. It explains how WSL 2 was the reason I decided to go over and give Windows a try.&lt;&#x2F;p&gt;
&lt;p&gt;To complete your Linux-on-Windows coding experience, go get &lt;a href=&quot;https:&#x2F;&#x2F;code.visualstudio.com&#x2F;&quot;&gt;VS Code&lt;&#x2F;a&gt; for &lt;strong&gt;Windows&lt;&#x2F;strong&gt;. Install it, and go into a directory inside of your ... WSL 2 terminal. Type &lt;code&gt;code .&lt;&#x2F;code&gt; and watch more magic happen.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Your WSL 2 directory will open up in the VS Code Windows app. WAT&lt;&#x2F;strong&gt;. This magic works via a fast server that VS Code uses to communicate everything you do (saving, debugging, etc...) back to WSL 2. You&#x27;re editing in Windows and running on Linux.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can take this a step further and store your Linux-ey things in the cloud with &lt;a href=&quot;https:&#x2F;&#x2F;visualstudio.microsoft.com&#x2F;services&#x2F;visual-studio-online&#x2F;&quot;&gt;VS Online&lt;&#x2F;a&gt;. That&#x27;s a whole other topic but worth checking out sometime.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Finally, if you use Docker, that works for WSL 2 too. Check out &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;docker-for-windows&#x2F;wsl-tech-preview&#x2F;&quot;&gt;these instructions&lt;&#x2F;a&gt; for how to set it up.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Coming from a Mac to Windows &amp; WSL 2</title>
          <pubDate>Thu, 26 Mar 2020 10:00:41 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/wsl/</link>
          <guid>/blog/wsl/</guid>
          <description>&lt;p&gt;I&#x27;ve spent most of my life as a professional programmer using a potpurri of Apple laptops and desktops. The first machine I used to do paid development work with was a MacBook in 2004. I&#x27;ve cycled through a few Mac Minis and MacBooks since then, and I&#x27;m typing this on yet another Mac Mini.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t love Mac OS or Apple hardware per se, but I&#x27;m just so &lt;em&gt;used&lt;&#x2F;em&gt; to the Apple ecosystem. I&#x27;m talking about everything from knowing the ins and outs of an iPhone, all the way down to details like the many ways to use the &lt;code&gt;cmd&lt;&#x2F;code&gt; key on the terminal.&lt;&#x2F;p&gt;
&lt;p&gt;Basically, Apple products rule everything around me&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I recently even wrote about my &lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;my-mac-dev-setup&quot;&gt;Mac Dev Setup&lt;&#x2F;a&gt;. This is a story about why I&#x27;m using WSL 2 more and more for my daily work. I&#x27;m not going to be ditching the Mac any time soon though :)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;windows&quot;&gt;Windows...&lt;&#x2F;h2&gt;
&lt;p&gt;Until now!&lt;&#x2F;p&gt;
&lt;p&gt;Apple has been taking a little bit of flack in the developer ecosystem. Lately it&#x27;s been the keyboards and there have been some other minor hardware issues in the past. They&#x27;ve also made some software decisions that affect me in my day-to-day life.&lt;&#x2F;p&gt;
&lt;p&gt;My list of grievances isn&#x27;t huge, but it got me thinking that what I really want is a Linux environment to do coding in and macs aren&#x27;t the only game in town. I&#x27;d go with Ubuntu, but there&#x27;s something shiny out there that I wanted to try - WSL2!&lt;&#x2F;p&gt;
&lt;p&gt;I haven&#x27;t done anything on Windows since 2008, so I figured I would flail around, but it turned out to be fairly easy to get my day to day development etc... done.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Doesn&#x27;t hurt that I work at Microsoft, and unsurprisingly, Windows works &lt;em&gt;really&lt;&#x2F;em&gt; well on all of our internal work stuff.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;my-setup&quot;&gt;My setup&lt;&#x2F;h2&gt;
&lt;p&gt;First off, I built a new machine just to run Windows on. I&#x27;ll let a picture do the talking. I love this thing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl&#x2F;windows-tower.jpg&quot; alt=&quot;windows tower&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;After I installed windows, the very first thing I noticed was muscle memory. I&#x27;m still catching myself reaching for the &lt;code&gt;cmd&lt;&#x2F;code&gt; key and I&#x27;m retraining myself to hit &lt;code&gt;ctrl&lt;&#x2F;code&gt; instead. C&#x27;est la vie!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;vs-code&quot;&gt;VS Code&lt;&#x2F;h3&gt;
&lt;p&gt;I was never an Emacs or Vim person. I like a nice looking GUI that I can write code in and I&#x27;m used to VS Code, so I picked it up and it feels just as comfortable as it did on Mac. Sometimes I feel homesick and I just open VSCode to feel better =P&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;wsl&#x2F;VSCode-Screenshot.png&quot; alt=&quot;VSCode Screenshot&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;VS Code has an &lt;a href=&quot;https:&#x2F;&#x2F;marketplace.visualstudio.com&#x2F;items?itemName=ms-vscode-remote.remote-wsl&quot;&gt;extension&lt;&#x2F;a&gt; that bridges the Windows world with the WSL 2 world (I&#x27;ll get to that next), so you can use the Windows app to write code that runs on Linux...&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h3 id=&quot;wsl-2&quot;&gt;WSL 2&lt;&#x2F;h3&gt;
&lt;p&gt;This is the big one. My whole development environment aside from VS Code relies on a Linux terminal. &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;wsl2-index&quot;&gt;WSL 2&lt;&#x2F;a&gt; is a Linux VM that runs on Windows.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m not a VM expert by any means, but from my experience with &lt;a href=&quot;https:&#x2F;&#x2F;www.virtualbox.org&#x2F;&quot;&gt;VirtualBox&lt;&#x2F;a&gt; on Mac and cloud VMs, the WSL2 VMs aren&#x27;t the same thing. Startup times are just about the same as regular apps, and resource utilization grows and shrinks just like a regular app.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;windows-terminal&quot;&gt;Windows Terminal&lt;&#x2F;h3&gt;
&lt;p&gt;WSL 2 VMs are distributed as apps for each Linux distro. For example, right now I have an &amp;quot;Ubuntu&amp;quot; app and a &amp;quot;Debian&amp;quot; app (I&#x27;m writing this from the Debian one). If you open one of the apps, you get a terminal in the VM of your choice. It works but you can&#x27;t customize it and your distros are all scattered each in their own app.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Microsoft&#x2F;Terminal&quot;&gt;Windows terminal&lt;&#x2F;a&gt; is the open source terminal that gathers them all together and it supports the Windows console and Powershell too (hint: you need Powershell to set up WSL 2!).&lt;&#x2F;p&gt;
&lt;p&gt;This + WSL 2 + VSCode feels just about the same as my &lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;my-mac-dev-setup&#x2F;&quot;&gt;Mac setup&lt;&#x2F;a&gt; and I can safely say that my muscle memory is the only thing killing my productivity at this point :)&lt;&#x2F;p&gt;
&lt;h3 id=&quot;my-dotfiles&quot;&gt;My dotfiles&lt;&#x2F;h3&gt;
&lt;p&gt;Last thing! I use &lt;a href=&quot;https:&#x2F;&#x2F;thelocehiliosan.github.io&#x2F;yadm&#x2F;&quot;&gt;yadm&lt;&#x2F;a&gt; to set up my new Mac and Linux environments. I store all of the scripts that yadm uses in my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;dotfiles&quot;&gt;dotfiles repo&lt;&#x2F;a&gt;, so I went back and gave everything a shine, so it works really nicely on any Debian&#x2F;Ubuntu VM on WSL 2.&lt;&#x2F;p&gt;
&lt;p&gt;I tend to tear down my WSL 2 VMs a lot, so it&#x27;s nice to set up a new one, set up SSH keys and just run &lt;code&gt;yadm clone git@github.com:arschles&#x2F;dotfiles.git&lt;&#x2F;code&gt; to have my dev setup all ready to go.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re new to WSL 2 (or even Debian, for that matter), I encourage you to go check out -- and steal! -- the code in that repository.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&#x27;ve gone through about 5 WSL 2 VMs so far! I&#x27;ll be posting details on how I set everything up next.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Happy coding!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Streaming tomorrow March 20, 2020</title>
          <pubDate>Thu, 19 Mar 2020 16:20:19 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/stream1/</link>
          <guid>/blog/stream1/</guid>
          <description>&lt;p&gt;Two days ago, I decided to &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;arschles&#x2F;status&#x2F;1240036551687434240&quot;&gt;start streaming&lt;&#x2F;a&gt;. I had a lot of fun just talking through my dev setup and showing how to make a small contribution to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&quot;&gt;Athens&lt;&#x2F;a&gt;. It felt like doing a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;go-in-5-minutes&quot;&gt;Go in 5 Minutes&lt;&#x2F;a&gt; screencast, but I could be a lot looser and more informal. I felt ok making mistakes and saying &amp;quot;I don&#x27;t know&amp;quot;.&lt;&#x2F;p&gt;
&lt;p&gt;This wasn&#x27;t me &lt;em&gt;teaching&lt;&#x2F;em&gt; a single topic, it felt like sharing.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;I&#x27;m going to stream again tomorrow&lt;&#x2F;strong&gt;. I&#x27;m always behind on triaging issues in the Athens project, so I&#x27;ll do a bit of that.&lt;&#x2F;p&gt;
&lt;p&gt;Details of the stream:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I&#x27;ll stream on Twitch at &lt;a href=&quot;https:&#x2F;&#x2F;www.twitch.tv&#x2F;arschles&quot;&gt;twitch.tv&#x2F;arschles&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;It will be at 10 am US Pacific Daylight Time. Use &lt;a href=&quot;https:&#x2F;&#x2F;time.is&#x2F;compare&#x2F;10am_20_Mar_2020_in_Portland,_Oregon&quot;&gt;time.is&lt;&#x2F;a&gt; to convert that to your time zone&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Hope to see you there!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>New to working from home?</title>
          <pubDate>Thu, 19 Mar 2020 11:47:31 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/wfh/</link>
          <guid>/blog/wfh/</guid>
          <description>&lt;p&gt;COVID-19 has really quickly turned our lives upside down, and we&#x27;re truly living in unprecedented times. As tech folks, many of us are working from home now. From a WFH-er for over 5 years now, welcome to the WFH crew!&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve had a lot of fellow colleagues and friends over the years who&#x27;ve had the same experience as me in the work from home world:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;When you&#x27;re new to working at home. You&#x27;re in work mode 24&#x2F;7. You&#x27;re getting so much done!&lt;&#x2F;li&gt;
&lt;li&gt;A few months go by. You burn out and you have to work less. You feel guilty because you think you&#x27;re not working at all. Everyone says this is what happens when people work from home&lt;&#x2F;li&gt;
&lt;li&gt;You notice you&#x27;re still doing good work despite your feelings. Time to ramp up the hours again!&lt;&#x2F;li&gt;
&lt;li&gt;Oh wait, you&#x27;ve gone down that road before. It didn&#x27;t work out well&lt;&#x2F;li&gt;
&lt;li&gt;Then, you start learning how to be sustainable working from home&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;For some, this takes a few months. For me, it took a few years. Don&#x27;t be like me. Here are some hard-earned rules that I&#x27;ve picked up over time to help keep me sustainable.&lt;&#x2F;p&gt;
&lt;p&gt;I hope they can help you skip this whole process and go right into sustainable mode. Try them for yourself!&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I was talking to some of my fellow &lt;a href=&quot;https:&#x2F;&#x2F;blog.golang.org&#x2F;gopher&quot;&gt;Gophers&lt;&#x2F;a&gt; on the &lt;a href=&quot;https:&#x2F;&#x2F;invite.slack.golangbridge.org&#x2F;&quot;&gt;Gophers Slack&lt;&#x2F;a&gt; about the new work setup a lot of folks have. In that discussion, I verbalized these rules for the very first time. What follows is a better-written version of what I wrote in that chat.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;your-office-is-fundamentally-different&quot;&gt;Your &amp;quot;office&amp;quot; is fundamentally different&lt;&#x2F;h2&gt;
&lt;p&gt;You now work in the same place as you live. Your commute is now basically getting out of bed and there&#x27;s no traffic. That&#x27;s awesome, but it comes with some hidden gotchas!&lt;&#x2F;p&gt;
&lt;h3 id=&quot;having-a-separate-workspace&quot;&gt;Having a separate workspace&lt;&#x2F;h3&gt;
&lt;p&gt;What you want to do is draw a line between &amp;quot;work mode&amp;quot; and the rest of your life. Don&#x27;t try to be &amp;quot;on&amp;quot; every waking minute, even though it&#x27;s tempting.&lt;&#x2F;p&gt;
&lt;p&gt;If you can do so, work in a separate physical space. Maybe a spare bedroom or basement (basement office represent!).&lt;&#x2F;p&gt;
&lt;p&gt;If you don&#x27;t have that kind of extra space, separate your workspace out in your mind. One way to do that is to set an alarm on your phone to signal that work has started. It can help you put your brain into work mode in the morning. Then, set up another alarm to signal the end of the day.&lt;&#x2F;p&gt;
&lt;p&gt;Make that end time non-negotiable. That means when the alarm sounds, the laptop gets shut, no slack, no IDE, no “just one more thing before I go to dinner”.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ending your day is hard to do. Sometimes you won&#x27;t get it right. I got it wrong yesterday, in fact. That&#x27;s ok. Don&#x27;t beat yourself up and try again tomorrow.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Finally, make your workspace yours. Work in a clean and comfortable desk area. That means ... wait for it ... you should clean it up. Having stacks of papers or crumbs around is annoying and distracting.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;getting-hardware-for-your-office&quot;&gt;Getting &amp;quot;hardware&amp;quot; for your office&lt;&#x2F;h3&gt;
&lt;p&gt;This doesn&#x27;t mean you have to buy stuff. If you want to, and assuming you can get things delivered still (I have no idea), you can think about monitors, speakers, laptop stands, mice, and keyboards.&lt;&#x2F;p&gt;
&lt;p&gt;But there&#x27;s a lot you can do without spending money. If you&#x27;re working on the dining room table, put your laptop on top of a book, cutting board, or anything else like that and plug in your headphones. If you have a sweater, roll it up and put it behind your lower back. &lt;&#x2F;p&gt;
&lt;p&gt;Do the same thing and use that sweater to rest your hands on as you type.&lt;&#x2F;p&gt;
&lt;p&gt;All of these things will help you concentrate and keep your body in shape.&lt;&#x2F;p&gt;
&lt;p&gt;Any little improvement to make you more comfortable and you’re winning&lt;&#x2F;p&gt;
&lt;h2 id=&quot;don-t-cut-yourself-off-from-people&quot;&gt;Don&#x27;t cut yourself off from people&lt;&#x2F;h2&gt;
&lt;p&gt;If you were previously in an office, you were around people. Even if you hated it, don&#x27;t let that environment go away overnight. Do video calls with &lt;em&gt;people you like&lt;&#x2F;em&gt;. This could be FaceTiming with family or doing a hangout with friends or colleagues.&lt;&#x2F;p&gt;
&lt;p&gt;I do a &amp;quot;no expectations&amp;quot; Zoom chat with my work mates every few days. We just come to hang out and talk about whatever comes up. No need to talk just about work if you don&#x27;t want. If you do, keep it to brainstorming or other low-pressure topics.&lt;&#x2F;p&gt;
&lt;p&gt;Just make sure you&#x27;re not always communicating by typing. Look at someone&#x27;s face regularly. Talk to them with your voice. You know, human things!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;take-care-of-your-body&quot;&gt;Take care of your body&lt;&#x2F;h2&gt;
&lt;p&gt;At the office, you have meetings, lunch, or your car at the end of the day. That means you walked to places.&lt;&#x2F;p&gt;
&lt;p&gt;At home, there&#x27;s none of that and no more natural prompts to walk around, so you have to replace them. Take breaks on purpose and get up, walk and stretch your body. There are some apps like &lt;a href=&quot;https:&#x2F;&#x2F;apps.apple.com&#x2F;us&#x2F;app&#x2F;time-out-break-reminders&#x2F;id402592703?mt=12&quot;&gt;Time Out&lt;&#x2F;a&gt; that can help you remember to get up, but I mostly roll with my phone&#x27;s alarm.&lt;&#x2F;p&gt;
&lt;p&gt;Walking outside and putting fresh air into your lungs is best, but that depends on whether it&#x27;s allowed in your locale. If outside isn&#x27;t an option, just walking around your domicile for &lt;em&gt;five minutes&lt;&#x2F;em&gt; works wonders.&lt;&#x2F;p&gt;
&lt;p&gt;There are real physiological reasons to get up:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Get more blood flowing to your legs. This is &lt;em&gt;critical&lt;&#x2F;em&gt;. Study after study shows that, tl;dr, your body is fundametally not designed to sit all day. &lt;em&gt;You actually need to walk around to get proper bloodflow to your legs&lt;&#x2F;em&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Let your shoulders and upper back relax. Especially if you&#x27;re on a laptop that&#x27;s below your natural eye level. Stand up straight, slowly pull your shoulders up to your ears, back, and back down. Go as far as you can and then a litle less. If it hurts, stop immediately, reset, and do it way slower and more gently.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;your-eyes&quot;&gt;Your eyes!&lt;&#x2F;h3&gt;
&lt;p&gt;This one is important - I even gave it a different header!&lt;&#x2F;p&gt;
&lt;p&gt;Every 10 minutes or so, look at something far away. For at least 20 seconds. You&#x27;ll exercise a lot of the muscles in your eyes.&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s proven to help prevent various kinds of eye degeneration long term. It also helps prevent dry eyes.&lt;&#x2F;p&gt;
&lt;p&gt;Sidenote: you won&#x27;t break your flow doing this. Most of the time, it helps you keep your flow.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;try-to-enjoy&quot;&gt;Try to enjoy!&lt;&#x2F;h2&gt;
&lt;p&gt;Finally, try enjoy your time working from home! These are uncertain and possibly scary times, but the silver lining is that you can learn a new way to work and live.&lt;&#x2F;p&gt;
&lt;p&gt;Be creative with your office, maybe take up a new hobby, read a non-tech book, teach someone how to code, help your community.&lt;&#x2F;p&gt;
&lt;p&gt;Above all, be just a little bit safer than you think you should be out there.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>My Mac Dev Setup</title>
          <pubDate>Wed, 18 Mar 2020 17:04:19 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/mac-dev-setup/</link>
          <guid>/blog/mac-dev-setup/</guid>
          <description>&lt;p&gt;Recently, I got into a really interesting &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jongalloway&#x2F;status&#x2F;1240165698506067970&quot;&gt;discussion on Twitter&lt;&#x2F;a&gt; with &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;jongalloway&quot;&gt;@jongalloway&lt;&#x2F;a&gt;, and it was just about 100% constructive and civil. On Twitter!&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s all about whether or not to use &lt;a href=&quot;https:&#x2F;&#x2F;brew.sh&#x2F;&quot;&gt;Homebrew&lt;&#x2F;a&gt; to set up and maintain a Mac for software development.&lt;&#x2F;p&gt;
&lt;p&gt;I started programming on a Mac in late 2002, on Mac OS Jaguar, version 10.2. Since then I&#x27;ve used at least 10 different Macs, which means I&#x27;ve set up at least 10 different Macs. We developers are picky about our setups, so setting up a brand new vanilla computer takes time and energy.&lt;&#x2F;p&gt;
&lt;p&gt;That got me thinking: I&#x27;ve used a Mac for almost 20 years as my main development machine. What do I do to set up and maintain &lt;em&gt;my&lt;&#x2F;em&gt; machines?&lt;&#x2F;p&gt;
&lt;h2 id=&quot;command-line-tools&quot;&gt;Command Line Tools&lt;&#x2F;h2&gt;
&lt;p&gt;Like any good developer, I&#x27;ve tried a bunch to automate myself out of work, so a while back I tapped &lt;a href=&quot;https:&#x2F;&#x2F;yadm.io&#x2F;&quot;&gt;yadm&lt;&#x2F;a&gt; to install many of my tools and store, version, apply, and upgrade various configurations for them. I store all that stuff in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;dotfiles&quot;&gt;github.com&#x2F;arschles&#x2F;dotfiles&lt;&#x2F;a&gt;, so let&#x27;s start there!&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;hyper.is&quot;&gt;Hyper&lt;&#x2F;a&gt; for my terminal. I just switched to it from &lt;a href=&quot;https:&#x2F;&#x2F;iterm2.com&#x2F;&quot;&gt;iTerm 2&lt;&#x2F;a&gt; primarily because it&#x27;s configurable with a single json file. I also like the icon on the dock, not gonna lie&lt;&#x2F;li&gt;
&lt;li&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;ohmyz.sh&#x2F;&quot;&gt;oh my zsh&lt;&#x2F;a&gt; (and the Z shell by extension) because it gives me plugins and a really uniform experience across different terminals. I switch to Linux a bunch and I can move my &lt;code&gt;~&#x2F;.zshrc&lt;&#x2F;code&gt; file over and get the same plugins and everything else without any work&lt;&#x2F;li&gt;
&lt;li&gt;I use &lt;a href=&quot;https:&#x2F;&#x2F;brew.sh&quot;&gt;Homebrew&lt;&#x2F;a&gt; a &lt;em&gt;ton&lt;&#x2F;em&gt;. I was the one on Twitter advocating for it! Basically, I can give &lt;code&gt;brew&lt;&#x2F;code&gt; my &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;dotfiles&#x2F;blob&#x2F;master&#x2F;.yadm&#x2F;Brewfile&quot;&gt;Brewfile&lt;&#x2F;a&gt; and it&#x27;ll just go install everything I need with no fuss. It acts like &lt;code&gt;apt&lt;&#x2F;code&gt; on Debian. In fact, it now works on Linux (that&#x27;s pretty new) so I can move my &lt;code&gt;Brewfile&lt;&#x2F;code&gt; over just like my &lt;code&gt;~&#x2F;.zshrc&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;direnv.net&#x2F;&quot;&gt;direnv&lt;&#x2F;a&gt; is a small little utility that lets me load&#x2F;unload environment variables just in a single directory. It comes in handy &lt;strong&gt;all over the place&lt;&#x2F;strong&gt;. Ya know every time some getting started docs say &amp;quot;you need to have X, Y and Z variables in your environment&amp;quot;? That&#x27;s when I reach for direnv and write up and &lt;code&gt;.envrc&lt;&#x2F;code&gt; file inside my project.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;github&#x2F;hub&quot;&gt;&lt;code&gt;hub&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is a nice little CLI that lets me do GitHub &amp;quot;things&amp;quot;. I do a lot of open source work (&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&quot;&gt;athens&lt;&#x2F;a&gt; a lot these days) so &lt;code&gt;hub&lt;&#x2F;code&gt; can take me from committing a change alllll the way to submitting a PR to the upstream repo. I really only use it for quick PRs because I like to go into the browser to really think through my PR descriptions, but it&#x27;s really nice for quick things&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cli&#x2F;cli&quot;&gt;&lt;code&gt;gh&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; is sort of a newer version of &lt;code&gt;hub&lt;&#x2F;code&gt;. It&#x27;s called the &amp;quot;GitHub&#x27;s official command line too&amp;quot; so my money&#x27;s on this thing implementing everything that &lt;code&gt;hub&lt;&#x2F;code&gt; has. It&#x27;s not &lt;em&gt;quite&lt;&#x2F;em&gt; there yet, but I&#x27;m starting to move away from &lt;code&gt;hub&lt;&#x2F;code&gt; to this. The maintainers of the project are &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cli&#x2F;cli#we-need-your-feedback&quot;&gt;open to feedback&lt;&#x2F;a&gt; also, so I feel pretty good about the move &lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;desktop-stuff&quot;&gt;Desktop stuff&lt;&#x2F;h2&gt;
&lt;p&gt;I try to stay in the command line as much as I can when I&#x27;m programming and only break out for really good reasons. I mentioned that I go into the browser when I want to submit pull requests. That&#x27;s so I pull my head out of the sand and think about the stuff I just built.&lt;&#x2F;p&gt;
&lt;p&gt;Another good reason I pop out of the command line is for my editor. I never got into vim or emacs. I&#x27;ve always used IDEs. In recent-ish memory, I&#x27;ve used almost all of the &lt;a href=&quot;https:&#x2F;&#x2F;www.jetbrains.com&#x2F;&quot;&gt;JetBrains&lt;&#x2F;a&gt; IDEs, &lt;a href=&quot;https:&#x2F;&#x2F;visualstudio.microsoft.com&#x2F;&quot;&gt;Visual Studio&lt;&#x2F;a&gt; (I&#x27;ve written a good amount of C#!), &lt;a href=&quot;https:&#x2F;&#x2F;www.sublimetext.com&#x2F;&quot;&gt;Sublime Text&lt;&#x2F;a&gt;, &lt;a href=&quot;https:&#x2F;&#x2F;atom.io&#x2F;&quot;&gt;Atom&lt;&#x2F;a&gt; and now I&#x27;m currently on &lt;a href=&quot;https:&#x2F;&#x2F;code.visualstudio.com&#x2F;&quot;&gt;VS Code&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I plan to stick with VS Code for a while for my main editor. It&#x27;s really flexible and seems to have the biggest ecosystem of all of the open source editors&#x2F;IDEs. Plus there are some cool &amp;quot;add on&amp;quot; features like &lt;a href=&quot;https:&#x2F;&#x2F;online.visualstudio.com&#x2F;&quot;&gt;VS Online&lt;&#x2F;a&gt;. Actually, check that one out if you haven&#x27;t. It&#x27;s the VS Code GUI on your machine but you run&#x2F;debug&#x2F;whatever the code on a cloud VM&lt;&#x2F;p&gt;
&lt;p&gt;And the last one! I use &lt;a href=&quot;https:&#x2F;&#x2F;desktop.github.com&#x2F;&quot;&gt;GitHub desktop&lt;&#x2F;a&gt; a ton. Technically I can do just about everything on the command line instead of using this app, but I have lots of good reasons why a lot of the time I go into this app instead:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;The colors! Not gonna lie, I like the green and red lines showing me in the fancy, shiny Mac color scheme what I&#x27;m about to commit&lt;&#x2F;li&gt;
&lt;li&gt;Partial commits. Being able to commit only &lt;em&gt;some&lt;&#x2F;em&gt; of a file is really easy in the app, and can be super useful. Especially when I implement 5 things at once, pick my head up, and realize I need to split stuff up into 5 separate commits&lt;&#x2F;li&gt;
&lt;li&gt;Everything in one place, in a UI. This app shows everything that&#x27;s going on in your project at once. You can see what branch you&#x27;re on, whether you need to pull code down from the server, how many commits you still need to push, history of commits, etc... all at once. On the command line you have to run a different command for almost all of that stuff. It&#x27;s nice to see the whole picture sometimes&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;one-more-thing&quot;&gt;One more thing...&lt;&#x2F;h2&gt;
&lt;p&gt;Those are the big things. My development life is a hodgepodge of CLIs and GUI tools and it works great for me. I can make quick, focused progress on a PR or issue I&#x27;m working on, without many distractions, and that&#x27;s all I can ask for!&lt;&#x2F;p&gt;
&lt;p&gt;But! I&#x27;m about to start challenging myself, and change the most fundamental of all of my tools: the operating system. I&#x27;m starting to move to Windows as my main development OS. I&#x27;ll be using &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;wsl2-index&quot;&gt;WSL 2&lt;&#x2F;a&gt; to install and use many of the same CLI tools I&#x27;m already used to, but the rest will be interesting to say the least.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m challenging myself to learn (and unlearn!) a lot to get comfortable with a brand new dev setup.&lt;&#x2F;p&gt;
&lt;p&gt;Interesting times ahead - stay tuned :)&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Tips for submittiting to the GopherCon 2020 CFP</title>
          <pubDate>Tue, 17 Dec 2019 13:18:53 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/gc-cfp-2020/</link>
          <guid>/blog/gc-cfp-2020/</guid>
          <description>&lt;p&gt;The GopherCon 2020 &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2020&quot;&gt;Call for Proposals&lt;&#x2F;a&gt; is open right now, and it closes on January 10, 2020. I am a co-chair of the program committee this year and I want to encourage you to submit a proposal.&lt;&#x2F;p&gt;
&lt;p&gt;Now that the year is drawing to a close and many of us will take some time off, it&#x27;s an especially great time to sit down with your ideas and write up a great proposal.&lt;&#x2F;p&gt;
&lt;p&gt;In this post, I&#x27;m going to answer common questions and comments I hear and talk about some common mistakes that I see in proposals.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Whether or not you&#x27;ve written proposals before, I&#x27;m highly encouraging you to take 20 minutes to read &lt;a href=&quot;https:&#x2F;&#x2F;dave.cheney.net&#x2F;2017&#x2F;02&#x2F;12&#x2F;how-to-write-a-successful-conference-proposal&quot;&gt;this article&lt;&#x2F;a&gt; by Dave Cheney. Dave is a very accomplished engineer and has years of experience reviewing GopherCon proposals.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;i-ve-never-done-any-public-speaking-before-can-i-submit&quot;&gt;I&#x27;ve never done any public speaking before, can I submit?&lt;&#x2F;h2&gt;
&lt;p&gt;The Go community is a large and diverse group. People from every corner of the world write Go code and they all have different experience levels, backgrounds, races, gender identities, and so on.&lt;&#x2F;p&gt;
&lt;p&gt;Every year, we aim to make the GopherCon speaker lineup reflect the community at large. We want to hear more of the ideas that our diverse community has to offer. If you&#x27;re a new speaker and you have an experience you think is interesting, please consider submitting a proposal to us. Same goes if you&#x27;re a new programmer.&lt;&#x2F;p&gt;
&lt;p&gt;If we select your proposal, we&#x27;ll give you all the help and support you need to give a great talk.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;can-i-submit-more-than-one-proposal&quot;&gt;Can I submit more than one proposal?&lt;&#x2F;h2&gt;
&lt;p&gt;Yes, absolutely!&lt;&#x2F;p&gt;
&lt;p&gt;Please just make sure that all of your submissions are appropriate for GopherCon. See &amp;quot;Advice for Authors&amp;quot; on the &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2020&quot;&gt;CFP page&lt;&#x2F;a&gt; for a guideline on what makes a proposal appropriate.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Papercall has a feature that lets you re-submit a talk from another conference to GopherCon, but we rarely select those talks. Instead of using that, please take a few extra minutes to tailor what you have to GopherCon, and then submit the new writing.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;what-s-the-difference-between-a-tutorial-session-and-a-keynote-session&quot;&gt;What&#x27;s the difference between a tutorial session and a keynote session?&lt;&#x2F;h2&gt;
&lt;p&gt;Tutorial sessions are 45 minutes long and happen in three tracks, which means that three of them will be happening at the same time at the conference. These sessions need to talk about or teach a Go-related topic in depth.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re writing a proposal for a tutorial session, try to keep a specific audience in mind. &amp;quot;Go programmers interested in writing internet services&amp;quot; is too general. &amp;quot;Go programmers interested in building distributed databases&amp;quot; is better. &lt;&#x2F;p&gt;
&lt;p&gt;Keynote sessions are 25 minutes long and happen one at a time, so everybody from the conference will watch them at the same time.&lt;&#x2F;p&gt;
&lt;p&gt;If you submit a keynote proposal, make sure that the huge diverse group of Gophers at the conference will get something out of it.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-submitted-last-year-and-my-proposal-didn-t-get-selected-can-i-submit-one-this-year&quot;&gt;I submitted last year and my proposal didn&#x27;t get selected, can I submit one this year?&lt;&#x2F;h2&gt;
&lt;p&gt;Absolutely. There are a lot of reasons why we don&#x27;t or can&#x27;t select proposals every year, so it might not be anything you missed last year. If you think your proposal is good, feel free to re-submit it, and if you have a new idea, you can submit that too.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&#x27;s ok to submit 2 or more proposals.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h2 id=&quot;i-don-t-like-public-speaking&quot;&gt;I don&#x27;t like public speaking&lt;&#x2F;h2&gt;
&lt;p&gt;I hear this one so much that I need to convince you to submit a proposal anyway.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t like public speaking either. I&#x27;m scared I&#x27;ll mess something up and lose the respect of my peers. I still do it, though, because I want to share what I know with my colleagues in the community.&lt;&#x2F;p&gt;
&lt;p&gt;Next time you talk yourself out of writing a proposal, try to focus instead of sharing your ideas, and go for it.&lt;&#x2F;p&gt;
&lt;p&gt;If your proposal is selected, we&#x27;ll do everything we can to help you feel comfortable and prepared for you talk. Last year myself and other reviewers sat with speakers and watched them rehearse their entire talk over video chats and at the conference. We&#x27;re planning to do the same thing this year.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;i-don-t-have-any-ideas-for-a-proposal&quot;&gt;I don&#x27;t have any ideas for a proposal&lt;&#x2F;h2&gt;
&lt;p&gt;We all have unique experiences and ideas -- even if we&#x27;re brand new to programming or Go -- but it&#x27;s hard to get them out unless you can step back and think about your work and experiences at-large.&lt;&#x2F;p&gt;
&lt;p&gt;Spend some quiet time to think about some new Go-related thing you&#x27;ve learned recently or what your experience has been like solving a technology problem at work or at home. This could be something as small as 10 minutes of quiet time at lunch  or after work.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h1&gt;
&lt;p&gt;What I&#x27;ve written here is based on common questions and pitfalls I&#x27;ve seen in real life GopherCon proposals over the past 2 years. It&#x27;s certainly not everything you need to know to write a great proposal.&lt;&#x2F;p&gt;
&lt;p&gt;As I said at the beginning of this post, Dave Cheney has a much longer history of reviewing GopherCon proposals, and he&#x27;s distilled that experience into a &lt;a href=&quot;https:&#x2F;&#x2F;dave.cheney.net&#x2F;2017&#x2F;02&#x2F;12&#x2F;how-to-write-a-successful-conference-proposal&quot;&gt;hugely valuable article&lt;&#x2F;a&gt;. I can nearly guarantee you&#x27;ll find some way to improve your proposal after reading this post, so please take the time to do so.&lt;&#x2F;p&gt;
&lt;p&gt;With that, good luck on your proposals, and happy new year, Gophers!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Athens on Azure Kubernetes Service</title>
          <pubDate>Wed, 27 Nov 2019 17:09:38 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-azure-1/</link>
          <guid>/blog/athens-azure-1/</guid>
          <description>&lt;p&gt;I&#x27;ve been working on Athens for well over a year now, and I&#x27;ve mentioned before that I have been running the &lt;code&gt;athens.azurefd.net&lt;&#x2F;code&gt; server on my own for almost all that time. The infrastructure that powered that server looked like this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;you ---&amp;gt; Azure Front Door
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;        ACI Running Athens
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;        Azure Cosmos DB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;If you&#x27;re not familiar with the acronyms, here are some short explanations for you:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;container-instances&#x2F;&quot;&gt;ACI&lt;&#x2F;a&gt; stands for &amp;quot;Azure Container Instances&amp;quot;. It&#x27;s a handy way to run a group of Docker containers together in Azure, and get a public IP &amp;amp; hostname for them&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;frontdoor&#x2F;&quot;&gt;Front Door&lt;&#x2F;a&gt; is Azure&#x27;s reverse proxy. It does a bunch of stuff, but the things that I care about are TLS termination and global caching. I&#x27;ve written and spoken about front door a lot in the past, and I&#x27;m a big believer in it. I think it&#x27;s one of Azure&#x27;s most handy services&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;azure.microsoft.com&#x2F;en-us&#x2F;services&#x2F;cosmos-db&#x2F;&quot;&gt;Cosmos DB&lt;&#x2F;a&gt; is Azure&#x27;s global &amp;quot;NoSQL&amp;quot; database. It supports the MongoDB API and does replication across Azure cloud regions very nicely, with no intervention from the operator (AKA me!)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote&gt;
&lt;p&gt;It&#x27;s important to note that Athens can run anywhere. I work for Microsoft so I chose to run it on Azure infrastructure and with CosmosDB as the database. I might consider moving to using Azure blob store later.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;original-requirements&quot;&gt;Original requirements&lt;&#x2F;h1&gt;
&lt;p&gt;When I set up this public proxy, I wanted to give anyone interested an opportunity to try Athens without installing anything. I think it did exactly that, and it turns out the proxy also got Gophers to start thinking about modules and get their local environment set up.&lt;&#x2F;p&gt;
&lt;p&gt;This deployment was the first of its kind, too! I had no intention of making it &amp;quot;production ready&amp;quot; though. My goal was to encourage teams and companies to roll out their own hosted servers, so the Athens team could focus more on empowering teams to run their own &lt;em&gt;internal&lt;&#x2F;em&gt; Athens servers. I needed a deployment that would mostly run itself.&lt;&#x2F;p&gt;
&lt;p&gt;Those above technologies let me build an architecture that took care of mostly that. Most of the work I had to put into this deployment was managing the transition from Go 1.11 to 1.12 and then to 1.13.&lt;&#x2F;p&gt;
&lt;p&gt;I generally spend no more than an hour a week taking care of this infrastructure, and I&#x27;ve talked to a nontrivial amount of folks using &lt;code&gt;athens.azurefd.net&lt;&#x2F;code&gt; in their production CI&#x2F;CD pipelines. That&#x27;s a testament to how well this architecture worked over time.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;new-requirements&quot;&gt;New requirements&lt;&#x2F;h1&gt;
&lt;p&gt;These days, there are &lt;a href=&quot;https:&#x2F;&#x2F;proxy.golang.org&quot;&gt;three&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;gocenter.io&quot;&gt;good&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;gonexus.dev&quot;&gt;proxies&lt;&#x2F;a&gt; that I know of which are public and mostly production ready proxy. In theory, my deployment isn&#x27;t really necessary anymore. That being said, I firmly believe that Athens needs to live on the public internet as a testbed for our software.&lt;&#x2F;p&gt;
&lt;p&gt;I still won&#x27;t commit to it being production ready, so use it at your own risk (I&#x27;m sure some folks will!) But it&#x27;s not going anywhere.&lt;&#x2F;p&gt;
&lt;p&gt;Not only is it going to stick around for testing, I&#x27;m also going to use the infrastructure as an educational tool. Instead of running Athens on ACI, I&#x27;ll be moving the hosting infrastructure to Kubernetes. This move will open up a lot of flexibility in how I run, debug and deploy the service. Here&#x27;s a high level diagram for what I&#x27;m planning to do:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;you ---&amp;gt; Azure Front Door
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;                |
&lt;&#x2F;span&gt;&lt;span&gt;Kubernetes ingress controller in AKS ---&amp;gt; Athens deployment (pods)
&lt;&#x2F;span&gt;&lt;span&gt;                                                |
&lt;&#x2F;span&gt;&lt;span&gt;                                                |
&lt;&#x2F;span&gt;&lt;span&gt;                                             CosmosDB
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And here are some things you don&#x27;t see:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;This simple &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;blob&#x2F;71932e2df1c226163c9c62c0024e0809aca27b1b&#x2F;aci.sh&quot;&gt;script&lt;&#x2F;a&gt; deploys to a (few) regions that Athens currently runs. I&#x27;ll be using Terraform to deploy to one (and more in the future!) &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;azure&#x2F;aks&#x2F;&quot;&gt;AKS&lt;&#x2F;a&gt; cluster&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ll likely use a service mesh system to control &lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m planning to use Athens&#x27; built-in distributed tracing support to help with debugging&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m planning to use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;tree&#x2F;master&#x2F;lathens&quot;&gt;lathens&lt;&#x2F;a&gt; to cache the &lt;code&gt;&#x2F;list&lt;&#x2F;code&gt; and &lt;code&gt;&#x2F;latest&lt;&#x2F;code&gt; API calls for each module&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m planning to use &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&#x2F;tree&#x2F;master&#x2F;crathens&quot;&gt;crathens&lt;&#x2F;a&gt; to crawl upstream VCS repos and proxies to keep modules up to date&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;m planning to centralize logs somewhere. Not sure where yet!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h1 id=&quot;what-s-coming&quot;&gt;What&#x27;s coming&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;ve already gotten started. In fact, you can see a running prototype of an AKS-backed Athens at &lt;a href=&quot;https:&#x2F;&#x2F;k8s.goreg.dev&quot;&gt;k8s.goreg.dev&lt;&#x2F;a&gt;. It&#x27;s missing a lot of the features in that above list, but it&#x27;s built approximately the same as the architecture in that diagram above.&lt;&#x2F;p&gt;
&lt;p&gt;All the code to do all of this is &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens-azure&quot;&gt;open source&lt;&#x2F;a&gt;, and this is post #1 of a series that I&#x27;ll use to chronicle my experience setting up and running Athens.&lt;&#x2F;p&gt;
&lt;p&gt;See you soon!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Building Full Stack Applications with Go, Vecty &amp; WebAssembly</title>
          <pubDate>Thu, 21 Nov 2019 13:19:52 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/full-stack-vecty-wasm/</link>
          <guid>/blog/full-stack-vecty-wasm/</guid>
          <description>&lt;h1 id=&quot;building-full-stack-web-apps-with-go-vecty-and-webassembly&quot;&gt;Building full stack web apps with Go, Vecty and WebAssembly&lt;&#x2F;h1&gt;
&lt;p&gt;Welcome folks! Today, we&#x27;re going to talk about writing full stack web applications in a brand new way.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I wrote this post a while ago and I&#x27;m reposting it on my blog now. It originally appeared &lt;a href=&quot;https:&#x2F;&#x2F;blog.logrocket.com&#x2F;building-full-stack-web-apps-with-go-vecty-and-webassembly&#x2F;&quot;&gt;here&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Many of us have heard of -- and maybe written -- full stack web applications. We do them in a variety of different ways, but the common denominator is usually &lt;a href=&quot;https:&#x2F;&#x2F;developer.mozilla.org&#x2F;en-US&#x2F;docs&#x2F;Web&#x2F;javascript&quot;&gt;Javascript&lt;&#x2F;a&gt; &amp;amp; &lt;a href=&quot;https:&#x2F;&#x2F;nodejs.org&#x2F;en&#x2F;&quot;&gt;Node.js&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Today, we&#x27;re going to break with that tradition and write a complete web application - front end and backend - without writing a line of Javascript. We’ll be comparing the developer experience to JavaScript along the way, but we’re going to be only writing &lt;a href=&quot;https:&#x2F;&#x2F;golang.org&quot;&gt;Go&lt;&#x2F;a&gt; for this entire project.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;ll learn how to build a single page link shortener application with just Go, and we&#x27;ll end up with working code that shows it in action.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;prerequisites&quot;&gt;Prerequisites&lt;&#x2F;h1&gt;
&lt;p&gt;Today, we&#x27;re going to be focusing on Go so make sure you&#x27;ve &lt;a href=&quot;https:&#x2F;&#x2F;golang.org&#x2F;dl&#x2F;&quot;&gt;installed&lt;&#x2F;a&gt; the tooling on your machine. I&#x27;m going to assume you have basic knowledge of Go, so check out the free &lt;a href=&quot;https:&#x2F;&#x2F;tour.golang.org&quot;&gt;Tour of Go&lt;&#x2F;a&gt; to brush up (or learn!) if you need to.&lt;&#x2F;p&gt;
&lt;p&gt;All the shell commands that I&#x27;m going to be showing work on a Mac, but should also work on most Linux systems (including &lt;a href=&quot;https:&#x2F;&#x2F;docs.microsoft.com&#x2F;en-us&#x2F;windows&#x2F;wsl&#x2F;about&quot;&gt;WSL&lt;&#x2F;a&gt;!).&lt;&#x2F;p&gt;
&lt;p&gt;Finally, make sure to clone the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;vectyshortener&quot;&gt;repository&lt;&#x2F;a&gt; with the code for this article.&lt;&#x2F;p&gt;
&lt;p&gt;And then you&#x27;re good to go, so let&#x27;s get started!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;getting-started&quot;&gt;Getting started&lt;&#x2F;h1&gt;
&lt;p&gt;First, we&#x27;re going to get the application running locally.&lt;&#x2F;p&gt;
&lt;p&gt;Coming from &lt;a href=&quot;https:&#x2F;&#x2F;webpack.js.org&#x2F;&quot;&gt;Webpack&lt;&#x2F;a&gt; and surrounding technologies -- which you&#x27;d use to build a web app with Javascript -- building and running this application is embarrassingly easy. There&#x27;s a front-end and a backend part (more on that below), and you compile both of them with the &lt;code&gt;go&lt;&#x2F;code&gt; tool, which requires no configuration.&lt;&#x2F;p&gt;
&lt;p&gt;First, run the backend server:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ go run .
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Next, build the frontend in a new terminal window:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;$ cd frontend
&lt;&#x2F;span&gt;&lt;span&gt;$ GOOS=js GOARCH=wasm go build -o ..&#x2F;public&#x2F;frontend.wasm
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Finally, go to https:&#x2F;&#x2F;localhost:8081 in your browser to see the app in action.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;how-this-all-works&quot;&gt;How this all works&lt;&#x2F;h1&gt;
&lt;p&gt;Like most web apps, our link shortener has a front end and backend piece. In our app, the backend is just a static server written in Go. All of the magic is in the front-end directory, so let&#x27;s start there!&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re familiar with &lt;a href=&quot;https:&#x2F;&#x2F;reactjs.org&quot;&gt;React&lt;&#x2F;a&gt; or the &lt;a href=&quot;https:&#x2F;&#x2F;www.w3.org&#x2F;TR&#x2F;WD-DOM&#x2F;introduction.html&quot;&gt;DOM&lt;&#x2F;a&gt;, you&#x27;ll recognize lots of the concepts we&#x27;ll cover. If not, this stuff should come naturally as we go through it.&lt;&#x2F;p&gt;
&lt;p&gt;We&#x27;re using a new Go framework called &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gopherjs&#x2F;vecty&quot;&gt;Vecty&lt;&#x2F;a&gt; to organize our application. Vecty makes you break down your app into components and arrange them into a tree. The whole scheme is really similar to HTML and the DOM or React.&lt;&#x2F;p&gt;
&lt;p&gt;Here’s what our app’s high level components would look like if they were HTML:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;h2&lt;&#x2F;code&gt; for the title of the page&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;form&lt;&#x2F;code&gt; to enter the link to shorten&lt;&#x2F;li&gt;
&lt;li&gt;A &lt;code&gt;div&lt;&#x2F;code&gt; to hold the shortened link
&lt;ul&gt;
&lt;li&gt;This value is dynamically updated as the user types the link into the above&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;An &lt;code&gt;a&lt;&#x2F;code&gt; to save the short link&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Vecty components are so similar to React that they look like the Go equivalent of &lt;a href=&quot;https:&#x2F;&#x2F;reactjs.org&#x2F;docs&#x2F;introducing-jsx.html&quot;&gt;JSX&lt;&#x2F;a&gt;, except that they have more parentheses =P.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s zoom in on one and see how it works. Here&#x27;s the code for the &lt;code&gt;form&lt;&#x2F;code&gt; component:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;elem&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Form&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;elem&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Input&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;vecty&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Markup&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;event&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Input&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;e &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;vecty&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;Event&lt;&#x2F;span&gt;&lt;span&gt;) {
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;short &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uuid&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;NewV4&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;()[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;shortened &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;short
&lt;&#x2F;span&gt;&lt;span&gt;            &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;vecty&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Rerender&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;        }),
&lt;&#x2F;span&gt;&lt;span&gt;    )),
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;First, &lt;code&gt;elem.Form&lt;&#x2F;code&gt; and &lt;code&gt;elem.Input&lt;&#x2F;code&gt; on lines 1 and 2 are for the &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;input&amp;gt;&lt;&#x2F;code&gt; tags, respectively. Those are both Go functions that take one or more arguments. Each argument is something that goes between the opening and closing HTML tags. For example, the stuff we pass to &lt;code&gt;elem.Form&lt;&#x2F;code&gt; goes in between &lt;code&gt;&amp;lt;form&amp;gt;&lt;&#x2F;code&gt; and &lt;code&gt;&amp;lt;&#x2F;form&amp;gt;&lt;&#x2F;code&gt;. This is what the above Go code would look like in HTML:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;html&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-html &quot;&gt;&lt;code class=&quot;language-html&quot; data-lang=&quot;html&quot;&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;form&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f902a;&quot;&gt;&amp;lt;input&amp;gt;&amp;lt;&#x2F;input&amp;gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#6486ab;&quot;&gt;&amp;lt;&#x2F;form&amp;gt;
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Pretty simple, right?&lt;&#x2F;p&gt;
&lt;p&gt;The last piece of code we didn&#x27;t look at is that &lt;code&gt;event.Input&lt;&#x2F;code&gt; function. This is an event handler just like in HTML&#x2F;JavaScript. This function takes in &lt;em&gt;another&lt;&#x2F;em&gt; function, which in this case is roughly an &lt;code&gt;[onchange](https:&#x2F;&#x2F;www.w3schools.com&#x2F;jsref&#x2F;event_onchange.asp)&lt;&#x2F;code&gt; handler. Just like you&#x27;d expect, that &lt;code&gt;*vecty.Event&lt;&#x2F;code&gt; argument the handler takes in is roughly the same as the JavaScript event.&lt;&#x2F;p&gt;
&lt;p&gt;The logic to actually shorten the link is all inside this handler, and it&#x27;s fairly simple. Here is that code commented thoroughly, to explain what&#x27;s going on:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; First, make a new UUID and take the first 5 characters of it.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; This will be our new shortcode
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;short &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;uuid&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;NewV4&lt;&#x2F;span&gt;&lt;span&gt;().&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;()[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;5&lt;&#x2F;span&gt;&lt;span&gt;]
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Next, write the shortcode to a variable. This variable is shared
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; with the &amp;lt;div&amp;gt;, so when we re-render this component, the &amp;lt;div&amp;gt; will
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; get updated
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;shortened &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;short
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Finally, re-render the component so that the &amp;lt;div&amp;gt; gets the new shortcode.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; Unlike React, there&amp;#39;s no automatic diff functionality. We tell Vecty
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#7f8989;&quot;&gt;&#x2F;&#x2F; explicitly which components to re-render.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;vecty&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Rerender&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;h&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h2 id=&quot;you-get-web-assembly-for-free&quot;&gt;You Get Web Assembly for Free&lt;&#x2F;h2&gt;
&lt;p&gt;Vecty can scale to big applications because of this component structure, and we can scale our app as big as we want by adding more components as needed. For example, we can add a component &lt;em&gt;above&lt;&#x2F;em&gt; our current top level to dynamically route to different sub-components based on the URL. This would be similar to &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;ReactTraining&#x2F;react-router&quot;&gt;react-router&lt;&#x2F;a&gt; for the React community, for example.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wasm-html&quot;&gt;WASM == HTML?&lt;&#x2F;h2&gt;
&lt;p&gt;Nope! WASM is a full departure from the DOM and everything HTML.&lt;&#x2F;p&gt;
&lt;p&gt;I compared all the components in the last section to HTML tags, but they aren&#x27;t! That&#x27;s where the big difference between Vecty &#x2F; WASM and React comes in. We&#x27;re compiling our Go code &lt;em&gt;straight&lt;&#x2F;em&gt; to WASM, which represents these components differently from the DOM.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;wasm-javascript&quot;&gt;WASM == Javascript?&lt;&#x2F;h2&gt;
&lt;p&gt;The same thing goes for Javascript! That Go code we saw in the event handler doesn&#x27;t ever turn into Javascript, and the browser doesn&#x27;t run the Javascript interpreter to run it. Instead, the Go compiler translates it into a &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;WebAssembly#Wasm_program&quot;&gt;WebAssembly program&lt;&#x2F;a&gt;, and the browser runs that &lt;em&gt;directly&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You get some performance wins this way because your stuff doesn&#x27;t need to go through the Javascript runtime. On the other hand, Go-based web assembly binaries can get pretty big (they&#x27;re smaller with other langauges though), so there&#x27;s a tradeoff there.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;&#x2F;h2&gt;
&lt;p&gt;At the end of the day, you get some big benefits from using Go and Vecty to build apps:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;You get to think in terms of components and nesting, just like with React and the DOM&lt;&#x2F;li&gt;
&lt;li&gt;You can write as much dynamic logic as you want, right next to your components, all in pure Go&lt;&#x2F;li&gt;
&lt;li&gt;You can share code between the server and client, similar to writing a React client and a Node.js server&lt;&#x2F;li&gt;
&lt;li&gt;You get to take advantage of WASM
&lt;ol&gt;
&lt;li&gt;Or you can compile your Vecty code to HTML too if you want! That&#x27;s a whole other article ;)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
</description>
      </item>
      <item>
          <title>Go 1.13 for Private Repositories</title>
          <pubDate>Thu, 12 Sep 2019 13:37:57 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/go-1-13-proxy/</link>
          <guid>/blog/go-1-13-proxy/</guid>
          <description>&lt;p&gt;Go 1.13 is a new beast. It&#x27;s a patch release in semver terms, but there are some big changes going on here. Most of them are really good, but if you have private dependencies, your build is gonna break out of the box.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;m gonna explain here what&#x27;s going on and how to fix it, using only two (I know, right??) shameless plugins for my favorite project -- which happens to solve these problems and I also am on the core team for -- &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.org&quot;&gt;Athens&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;So, here&#x27;s the big thing that changed and is gonna cause you headaches:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the first time that the &lt;code&gt;go&lt;&#x2F;code&gt; toolchain has out-of-the-box integration with a hosted service&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;In more detail:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Modules are now turned on &lt;em&gt;everywhere&lt;&#x2F;em&gt; by default&lt;&#x2F;li&gt;
&lt;li&gt;The default way you download new dependencies is from &lt;a href=&quot;https:&#x2F;&#x2F;proxy.golang.org&quot;&gt;proxy.golang.org&lt;&#x2F;a&gt; - not hosted version control systems like GitHub&lt;&#x2F;li&gt;
&lt;li&gt;Go now consults the &lt;a href=&quot;https:&#x2F;&#x2F;sum.golang.org&quot;&gt;checksum database&lt;&#x2F;a&gt; every time you ask for a new module not in your &lt;code&gt;go.sum&lt;&#x2F;code&gt; file&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Let&#x27;s go through these points one by one.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;modules-are-now-turned-on-everywhere-by-default&quot;&gt;Modules are now turned on everywhere by default&lt;&#x2F;h1&gt;
&lt;p&gt;Everything else being equal, this is amazing. There&#x27;s been debate, accusations, fighting in the community over how to do Go dependency management. I stayed out of almost all of that because I don&#x27;t have any more hairs on my head to lose.&lt;&#x2F;p&gt;
&lt;p&gt;Not everyone is happy with modules, but just like every change, that will pass. More importantly, we have a good enough system, that we can make work, and that we can &lt;em&gt;agree on&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Standardization is good in software systems! Also, part of the formal modules standard is what makes Athens and all the other module proxies possible.&lt;&#x2F;p&gt;
&lt;p&gt;Speaking of proxies ....&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-default-way-you-download-new-dependencies-is-from-proxy-golang-org&quot;&gt;The default way you download new dependencies is from &lt;a href=&quot;https:&#x2F;&#x2F;proxy.golang.org&quot;&gt;proxy.golang.org&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;This one can be good or bad, depending on the type of codebase you&#x27;re working in. My explanation is long, so I&#x27;m not even gonna be that sad if you just scroll down to the bold text below.&lt;&#x2F;p&gt;
&lt;p&gt;If you want details, read on!&lt;&#x2F;p&gt;
&lt;p&gt;The module spec has a small section that details a module download API, which is fancy way to say &amp;quot;a few GET requests to download source code and &lt;code&gt;go.mod&lt;&#x2F;code&gt; files.&amp;quot; &lt;&#x2F;p&gt;
&lt;p&gt;But the API means we can build servers for modules now. So as of 1.11, you didn&#x27;t have to use &lt;code&gt;git clone&lt;&#x2F;code&gt; for you package manager anymore. By the way, how did we last for almost 10 years using &lt;code&gt;git clone&lt;&#x2F;code&gt; to install dependencies?&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, I started building one of the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&quot;&gt;first module servers&lt;&#x2F;a&gt; and one of the most widely used open source ones. I also gave a talk about it at GopherCon US this year. My big message was that Athens is one of the tools we&#x27;re going to use to keep the modules ecosystem decentralized - just like dependencies always have been in Go. I know I already bashed &lt;code&gt;git clone&lt;&#x2F;code&gt; (see what I did there?) as a package manager, but it&#x27;s a distributed versioning system, and we co-opted it to build a distributed package index.&lt;&#x2F;p&gt;
&lt;p&gt;That takes me to the change in 1.13. Before 1.13, you had to set a &lt;code&gt;GOPROXY&lt;&#x2F;code&gt; environment variable to tell Go to use this module API and to fetch modules from the server you specify. Otherwise, it would keep doing &lt;code&gt;git clone&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Now things are different! &lt;code&gt;GOPROXY&lt;&#x2F;code&gt; defaults to &lt;code&gt;https:&#x2F;&#x2F;proxy.golang.org&lt;&#x2F;code&gt; as of 1.13. &lt;code&gt;proxy.golang.org&lt;&#x2F;code&gt; is a module repository run by the Go team at Google.&lt;&#x2F;p&gt;
&lt;p&gt;This change means that, out of the box, all of your &lt;code&gt;go build&lt;&#x2F;code&gt;, &lt;code&gt;go get&lt;&#x2F;code&gt;, &lt;code&gt;go install&lt;&#x2F;code&gt;, etc... commands will be communicating with Google-run servers to fetch code.&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t mean to make that sound ominous, I&#x27;m not trying to throw any shade at Google, and Google didn&#x27;t pull a fast one as you might think (or have read?) - this change was on the roadmap like all the others. &lt;&#x2F;p&gt;
&lt;p&gt;So that&#x27;s me softening the blow. If you&#x27;re working in a private repository, this new thing sucks for you. By default, you&#x27;ll be sending Google the names of your private modules, &lt;em&gt;and&lt;&#x2F;em&gt; your builds will break because the public Google proxy can&#x27;t find your private modules.&lt;&#x2F;p&gt;
&lt;p&gt;What&#x27;s the solution? I&#x27;m glad you asked, friend! Here&#x27;s how to fix it, in bold print for people who just skimmed the article, and with a shameless plug for Athens:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;If you have a private codebase, set your &lt;code&gt;GOPROXY&lt;&#x2F;code&gt; environment variable to a private module proxy. Where do you get a private module proxy, you ask? Check out the biggest open source one if you&#x27;re on the market: &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;Athens&lt;&#x2F;a&gt;&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Ok, and there&#x27;s one more thing you need to look at...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;go-now-consults-the-checksum-database-every-time-you-ask-for-a-new-module&quot;&gt;Go now consults the &lt;a href=&quot;https:&#x2F;&#x2F;sum.golang.org&quot;&gt;checksum database&lt;&#x2F;a&gt; every time you ask for a new module&lt;&#x2F;h1&gt;
&lt;p&gt;This section&#x27;s gonna be really familiar if you read everything in the last one. If you do a &lt;code&gt;go get&lt;&#x2F;code&gt; (or similar) and the module isn&#x27;t in your &lt;code&gt;go.sum&lt;&#x2F;code&gt; file (yes, the checksum file), then the Go tool is gonna ask &lt;code&gt;sum.golang.org&lt;&#x2F;code&gt; -- the checksum database -- what the checksum is supposed to be.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s to stop a proxy from giving you the wrong stuff the first time you ask for it. Since they&#x27;re using their proxy by default, they&#x27;re actually protecting you from ... themselves. The Go team is the hero Gotham deserves, not the hero it needs.&lt;&#x2F;p&gt;
&lt;p&gt;But seriously, the idea behind the sum database is good - it helps you not get corrupted or tampered-with code, whichever proxy you decide to use. It&#x27;s a really good tool to encourage a secure, diverse, and decentralized ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;The problem is you pesky private repositories! The same send-your-data-to-Google bug is gonna bite you here too. There are two things you can do about it, and I&#x27;m not gonna bold them because I already let you lazy readers off the hook last section, dammit!&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Set &lt;code&gt;GONOSUMDB&lt;&#x2F;code&gt; to have a comma-separated-list of module prefixes you don&#x27;t want Go to send to the sum DB. For example, &lt;code&gt;export GONOSUMDB=&amp;quot;myvcs.internal&#x2F;*,secretevilplans.com&#x2F;modules&#x2F;*&amp;quot;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Use Athens (shameless plug #2) to set a global &lt;code&gt;GONOSUMDB&lt;&#x2F;code&gt; variable for your team. This will prevent anyone on the team who is misconfigured from spilling the beans to the Goog&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;So that&#x27;s it for today. Make sure you go understand what&#x27;s up with the &lt;a href=&quot;https:&#x2F;&#x2F;proxy.golang.org&quot;&gt;Google proxy&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;sum.golang.org&quot;&gt;Google Checksum DB&lt;&#x2F;a&gt; and how they affect your code.&lt;&#x2F;p&gt;
&lt;p&gt;If you have private repositories, &lt;em&gt;really&lt;&#x2F;em&gt; understand them. Your builds will break out of the box if you don&#x27;t.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Join Me For The Second Athens Office Hours</title>
          <pubDate>Thu, 27 Jun 2019 15:07:03 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-office-hours-2/</link>
          <guid>/blog/athens-office-hours-2/</guid>
          <description>&lt;p&gt;Athenians! First of all, thanks to everyone who came to the &lt;a href=&quot;&#x2F;blog&#x2F;athens-office-hours&#x2F;&quot;&gt;first office hours&lt;&#x2F;a&gt;. I think it was a success, and we&#x27;re going to keep going.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;when&quot;&gt;When&lt;&#x2F;h1&gt;
&lt;p&gt;Like last time, we&#x27;re going to vote for the time that works best for the most people. &lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Please vote for the time that works for you at this link: https:&#x2F;&#x2F;doodle.com&#x2F;poll&#x2F;tiypx72hs6ttmezq.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;what&quot;&gt;What&lt;&#x2F;h1&gt;
&lt;p&gt;Last time, we went through a few things. First, we looked at the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;configuration&#x2F;&quot;&gt;configuration system&lt;&#x2F;a&gt; internals. Then, we started to look at how the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;configuration&#x2F;storage&#x2F;&quot;&gt;storage system&lt;&#x2F;a&gt; code uses the configuration to set up the appropriate storage backend.&lt;&#x2F;p&gt;
&lt;p&gt;So, this time we&#x27;re going to start where we left off! We started talking about how the &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;configuration&#x2F;storage&#x2F;#mongo&quot;&gt;MongoDB storage backend&lt;&#x2F;a&gt; works, so we&#x27;re going to look at that part of the codebase in more detail.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;other-logistics&quot;&gt;Other Logistics&lt;&#x2F;h1&gt;
&lt;p&gt;We&#x27;ll be doing the meeting on the same &lt;a href=&quot;https:&#x2F;&#x2F;zoom.com&quot;&gt;Zoom&lt;&#x2F;a&gt; room as last time. Here&#x27;s the link: https:&#x2F;&#x2F;arschles.com&#x2F;zoom.&lt;&#x2F;p&gt;
&lt;p&gt;Finally, as always, &lt;strong&gt;absolutely everybody is welcome&lt;&#x2F;strong&gt;. Anyone is free to join, listen, ask questions, or otherwise participate. I will always do my best to include anyone who wants to be involved with the Athens community in any way.&lt;&#x2F;p&gt;
&lt;p&gt;Hope to see you there!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Athens Office Hours!</title>
          <pubDate>Tue, 25 Jun 2019 13:37:55 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-office-hours/</link>
          <guid>/blog/athens-office-hours/</guid>
          <description>&lt;p&gt;Hey Gophers! If you don&#x27;t know what Athens is, you should totally &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;go check it out&lt;&#x2F;a&gt;. The quick overview is that it&#x27;s a proxy server for Go modules, and an interesting open source project with a community full of supportive Go programmers that help, encourage, and welcome contributions from anyone interested (we make that a priority!). &lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;I&#x27;m starting an Athens office hours session.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Absolutely anyone interested in Athens is welcome to join.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;The first session will be this Thursday or Friday (June 27 or 28).&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Please vote on the time(s) that work for you at &lt;a href=&quot;https:&#x2F;&#x2F;doodle.com&#x2F;poll&#x2F;bwy8tiaxh3ehb5g6&quot;&gt;this link&lt;&#x2F;a&gt;.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Whether you&#x27;re a seasoned Go programmer, a newcomer interested in getting involved with a Go open source project, or even someone brand new to open source -- we have contributors who fall into all these categories and more -- I want to make the project even more accessible for you.&lt;&#x2F;p&gt;
&lt;p&gt;That&#x27;s why I&#x27;m starting this Athens office hours session. This session is a place for learning and discussing different pieces of the codebase and architecture of Athens. And &lt;em&gt;anyone&lt;&#x2F;em&gt; who is part or the project or interested in the project can join. &lt;&#x2F;p&gt;
&lt;h1 id=&quot;details&quot;&gt;Details&lt;&#x2F;h1&gt;
&lt;p&gt;The office hours will be held on a &lt;a href=&quot;https:&#x2F;&#x2F;zoom.com&quot;&gt;Zoom&lt;&#x2F;a&gt; video chat at this link: https:&#x2F;&#x2F;arschles.com&#x2F;zoom. Here&#x27;s the format of the first office hours:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I&#x27;ll choose a section of the codebase to start looking at&lt;&#x2F;li&gt;
&lt;li&gt;I&#x27;ll introduce the achitecture of this section at a high level&lt;&#x2F;li&gt;
&lt;li&gt;Anyone attending can ask questions, at any time&lt;&#x2F;li&gt;
&lt;li&gt;We&#x27;ll navigate the codebase as the questions lead us (not limited to the area I chose!)&lt;&#x2F;li&gt;
&lt;li&gt;I am not the “expert”. I happen to know a little bit about a lot of the codebase, but hopefully others will attend who have a lot more in-depth knowledge about individual pieces. There’s power in numbers!&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I intend to make these office hours recurring. The format might change in future office hours sessions if needed, but the only thing that won&#x27;t change is that &lt;strong&gt;absolutely everybody is welcome&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;time-and-date&quot;&gt;Time and Date&lt;&#x2F;h1&gt;
&lt;p&gt;If you&#x27;re interested in coming to the first office hours session, &lt;em&gt;please vote on the time(s) that works for you at the below link&lt;&#x2F;em&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;https:&#x2F;&#x2F;doodle.com&#x2F;poll&#x2F;bwy8tiaxh3ehb5g6&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Functional Programming in Go With dcode</title>
          <pubDate>Mon, 11 Mar 2019 14:06:10 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/dcode/</link>
          <guid>/blog/dcode/</guid>
          <description>&lt;p&gt;Back in 2017, I gave a &lt;a href=&quot;https:&#x2F;&#x2F;www.youtube.com&#x2F;watch?v=c8Fwb4KbVJM&quot;&gt;talk&lt;&#x2F;a&gt; at GopherCon about doing functional programming with Go. I went up on stage and talked about some FP basics like &lt;code&gt;Functor&lt;&#x2F;code&gt;s, higher-order functions and so on. I heard some feedback that doing FP in Go isn&#x27;t practical until Go has generics (fret not, folks, &lt;a href=&quot;https:&#x2F;&#x2F;go.googlesource.com&#x2F;proposal&#x2F;+&#x2F;master&#x2F;design&#x2F;go2draft-contracts.md&quot;&gt;contracts&lt;&#x2F;a&gt; are on the table!).&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s true that contracts will be a huge boon for functional Go code, but there&#x27;s lots of functional programming we can do in Go &lt;em&gt;without&lt;&#x2F;em&gt; generics.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;You don&#x27;t need generics to do FP in Go.&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;h1 id=&quot;dcode&quot;&gt;dcode&lt;&#x2F;h1&gt;
&lt;p&gt;When I started writing that talk in 2017, I created the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&quot;&gt;github.com&#x2F;go-functional&lt;&#x2F;a&gt; GitHub organization to hold experimental projects for doing FP in Go.&lt;&#x2F;p&gt;
&lt;p&gt;Beyond the code I showed on the slides, which is in &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&#x2F;core&quot;&gt;core&lt;&#x2F;a&gt;, I&#x27;ve been prototyping some other functional libraries like &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&#x2F;quicktest&quot;&gt;quicktest&lt;&#x2F;a&gt; and pure functional &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&#x2F;query&quot;&gt;SQL query builders&lt;&#x2F;a&gt;. The one area that I really latched onto was JSON, and that&#x27;s how &lt;code&gt;dcode&lt;&#x2F;code&gt; was born.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;inspiration&quot;&gt;Inspiration&lt;&#x2F;h2&gt;
&lt;p&gt;A long time ago, I built web services using Scala and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lift&#x2F;lift&#x2F;tree&#x2F;master&#x2F;framework&#x2F;lift-base&#x2F;lift-json&#x2F;&quot;&gt;lift-json&lt;&#x2F;a&gt;. That library provides a pure functional tree representation for a JSON object. It&#x27;s also relatively fast for deep traversals.&lt;&#x2F;p&gt;
&lt;p&gt;More recently, I found the &lt;a href=&quot;https:&#x2F;&#x2F;elm-lang.org&#x2F;&quot;&gt;Elm language&lt;&#x2F;a&gt;, and particularly its &lt;a href=&quot;https:&#x2F;&#x2F;guide.elm-lang.org&#x2F;effects&#x2F;json.html&quot;&gt;JSON parser&lt;&#x2F;a&gt;. This parser interface is a concise DSL that lets you say you you want to traverse a JSON object. It&#x27;s incredibly self-documenting, even when dealing with large JSON objects. In fact, the DSL tends to look similar to &lt;a href=&quot;http:&#x2F;&#x2F;jsonpath.com&#x2F;&quot;&gt;JSONPath&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;The Elm API makes the lift-json tree representation accessible to anyone, without requiring them to write their own tree traversal code.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;&#x2F;h2&gt;
&lt;p&gt;You write one line of code to traverse any number of levels into a JSON object  as necessary.&lt;&#x2F;p&gt;
&lt;p&gt;For example, if you have this JSON (adapted from the JSONPath page):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;json&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-json &quot;&gt;&lt;code class=&quot;language-json&quot; data-lang=&quot;json&quot;&gt;&lt;span&gt;{
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;firstName&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;John&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;lastName&amp;quot; &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;doe&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;age&amp;quot;      &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;26&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;address&amp;quot;  &lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;streetAddress&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;naist street&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;city&amp;quot;         &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Nara&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;postalCode&amp;quot;   &lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;630-0192&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;accessInstructions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: {
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;gate&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b3933a;&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;        &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;Please dial 0123 so I can buzz you into the gate&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;    }
&lt;&#x2F;span&gt;&lt;span&gt;  }
&lt;&#x2F;span&gt;&lt;span&gt;}
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And you want to get to &lt;code&gt;$.address.accessInstructions.message&lt;&#x2F;code&gt; (that is, the value of the &lt;code&gt;message&lt;&#x2F;code&gt; field inside the address access instructions object), you&#x27;d write this code to crate a decoder that, when it&#x27;s called, will try to pull that value out of any JSON you give it.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;decoder &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Field&lt;&#x2F;span&gt;&lt;span&gt;(
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;address&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Field&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;accessInstructions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;),
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Field&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;()),
&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s also an accumulator interface so you don&#x27;t have to nest all those calls to &lt;code&gt;Field&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;decoder &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;:= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;First&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;address&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;accessInstructions&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Then&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d07711;&quot;&gt;&amp;quot;message&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;).&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;Into&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;())
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h1 id=&quot;why&quot;&gt;Why?&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;code&gt;dcode&lt;&#x2F;code&gt; obviously doesn&#x27;t fit every use case. If you&#x27;re expecting to decode a big JSON object with lots of keys, and the incoming JSON value is the same every time, you&#x27;ll most likely be more successful writing a &lt;code&gt;struct&lt;&#x2F;code&gt; and using &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;encoding&#x2F;json&quot;&gt;&lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; to decode into it (although &lt;code&gt;dcode&lt;&#x2F;code&gt; does have functionality to decode into &lt;code&gt;struct&lt;&#x2F;code&gt;s).&lt;&#x2F;p&gt;
&lt;p&gt;This library does well, however, in a few cases:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You need to traverse deep JSON objects to fetch values&lt;&#x2F;strong&gt;. This use case is common with programs that need to use a small part of a large JSON API&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;You don&#x27;t know the &amp;quot;shape&amp;quot; of the object ahead of time&lt;&#x2F;strong&gt;. Often, you&#x27;ll want to try several different decoding strategies at a time. This use case is common with web applications that deal with legacy API clients&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Since &lt;code&gt;dcode&lt;&#x2F;code&gt; is based on functional programming principles, it also comes with two helpful patterns:&lt;&#x2F;p&gt;
&lt;h4 id=&quot;composable&quot;&gt;Composable&lt;&#x2F;h4&gt;
&lt;p&gt;Each &lt;code&gt;Decoder&lt;&#x2F;code&gt; that you construct (with &lt;code&gt;Field&lt;&#x2F;code&gt; or &lt;code&gt;First()...Into()&lt;&#x2F;code&gt;) is a stateless function that is approximately:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;go&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-go &quot;&gt;&lt;code class=&quot;language-go&quot; data-lang=&quot;go&quot;&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;func &lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;d &lt;&#x2F;span&gt;&lt;span style=&quot;color:#72ab00;&quot;&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;Decoder&lt;&#x2F;span&gt;&lt;span&gt;) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#c23f31;&quot;&gt;Decode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;b &lt;&#x2F;span&gt;&lt;span&gt;[]&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;byte&lt;&#x2F;span&gt;&lt;span&gt;, &lt;&#x2F;span&gt;&lt;span style=&quot;color:#5597d6;&quot;&gt;i &lt;&#x2F;span&gt;&lt;span style=&quot;color:#668f14;&quot;&gt;interface&lt;&#x2F;span&gt;&lt;span&gt;{}) &lt;&#x2F;span&gt;&lt;span style=&quot;color:#a2a001;&quot;&gt;error
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: I&#x27;ve written this function here as an example. It doesn&#x27;t exist in the library, but you can use the &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;github.com&#x2F;go-functional&#x2F;dcode#Decode&quot;&gt;top-level &lt;code&gt;Decode&lt;&#x2F;code&gt; function&lt;&#x2F;a&gt; instead to get the same result.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Of course, this function signature looks a lot like &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;encoding&#x2F;json#Unmarshal&quot;&gt;&lt;code&gt;(encoding&#x2F;json).Unmarshal&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;, which is nice for familiarity.&lt;&#x2F;p&gt;
&lt;p&gt;More importantly, these &lt;code&gt;Decode&lt;&#x2F;code&gt; functions are built up by composing other &lt;code&gt;Decode&lt;&#x2F;code&gt; functions together. The &lt;code&gt;Field&lt;&#x2F;code&gt; or &lt;code&gt;First()...Into()&lt;&#x2F;code&gt; interfaces both enable that composition.&lt;&#x2F;p&gt;
&lt;p&gt;This API makes it obvious how to structure your JSON decoding logic, and trivial to refactor it. It also really encourages the same kind of composibility as &lt;code&gt;Decoder&lt;&#x2F;code&gt;s &amp;quot;leak&amp;quot; into other parts of your codebase&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonus: &lt;code&gt;dcode&lt;&#x2F;code&gt; itself takes heavy advantage of composition. Check out &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;github.com&#x2F;go-functional&#x2F;dcode#Map&quot;&gt;&lt;code&gt;Map&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;godoc.org&#x2F;github.com&#x2F;go-functional&#x2F;dcode#OneOf&quot;&gt;&lt;code&gt;OneOf&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; for examples.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h4 id=&quot;loosely-coupled&quot;&gt;Loosely Coupled&lt;&#x2F;h4&gt;
&lt;p&gt;&lt;code&gt;Decoder&lt;&#x2F;code&gt;s are instructions for decoding a JSON object, and they can exist alone, without the values that the JSON should be decoded into. This lets you pass &lt;code&gt;Decoder&lt;&#x2F;code&gt;s to any function so that it can decode any JSON into any value as it sees fit. In the future, we may consider adding functionality to serialize and deserialize &lt;code&gt;Decoder&lt;&#x2F;code&gt;s themselves, so you can pass them over the network.&lt;&#x2F;p&gt;
&lt;p&gt;To contrast, if you use &lt;code&gt;encoding&#x2F;json&lt;&#x2F;code&gt; to decode JSON, you usually create a &lt;code&gt;struct&lt;&#x2F;code&gt; to define the &amp;quot;shape&amp;quot; of the JSON. This API requires that the values (struct fields) and the decoding instructions (the reflected field names, or struct tags) are tightly coupled.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;get-started&quot;&gt;Get Started!&lt;&#x2F;h1&gt;
&lt;p&gt;Go check out the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&#x2F;dcode&#x2F;blob&#x2F;master&#x2F;README.md&quot;&gt;README&lt;&#x2F;a&gt; for more on how and why to use &lt;code&gt;dcode&lt;&#x2F;code&gt;. Of course this is a work in progress, so if you find something missing, wrong, or just plain broken, please don&#x27;t be shy and &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;go-functional&#x2F;dcode&#x2F;issues&#x2F;new&quot;&gt;file an issue&lt;&#x2F;a&gt;!&lt;&#x2F;p&gt;
&lt;p&gt;Keep on rockin&#x27;, Gophers&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>GopherCon 2019 CFP OMG!</title>
          <pubDate>Thu, 10 Jan 2019 14:23:56 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/gophercon-2019-cfp-omg/</link>
          <guid>/blog/gophercon-2019-cfp-omg/</guid>
          <description>&lt;p&gt;Hey Gophers!&lt;&#x2F;p&gt;
&lt;p&gt;I want you to &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2019&quot;&gt;submit a proposal&lt;&#x2F;a&gt; to talk at &lt;a href=&quot;https:&#x2F;&#x2F;www.gophercon.com&#x2F;home&quot;&gt;GopherCon 2019&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;... and I want you to start now because the Call for Proposals is only open until January 31st!&lt;&#x2F;p&gt;
&lt;p&gt;Submit a proposal, you say? Let&#x27;s talk a little bit about that.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;but-i-could-never-speak-at-gophercon&quot;&gt;But I Could &lt;em&gt;Never&lt;&#x2F;em&gt; Speak At GopherCon!&lt;&#x2F;h1&gt;
&lt;p&gt;Yes, you could. All those &amp;quot;superstars&amp;quot; you see up on the stage? They submitted talks through the &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2019&quot;&gt;same form&lt;&#x2F;a&gt; as you do. You can submit, and you can get accepted.&lt;&#x2F;p&gt;
&lt;p&gt;In fact, we love new speakers. All things being equal, we tend to prefer new-to-GopherCon speakers over speakers who have already spoken.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;but-i-might-not-get-accepted&quot;&gt;But I Might Not Get Accepted :(&lt;&#x2F;h1&gt;
&lt;p&gt;We really want to see your proposal, but it&#x27;s true, you could get rejected. We get many more than spaces available in the program, and we unfortunately have to turn down some awesome proposals. Not all is lost if you don&#x27;t get accepted to this conference, though!&lt;&#x2F;p&gt;
&lt;p&gt;A proposal isn&#x27;t a one-and-done, and if it&#x27;s not accepted to GopherCon 2019, it&#x27;s far from wasted. I would encourage you to use it as inspiration for other conferences. There are tons of others &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;golang&#x2F;go&#x2F;wiki&#x2F;Conferences&quot;&gt;just for Go&lt;&#x2F;a&gt; alone!&lt;&#x2F;p&gt;
&lt;p&gt;Personally, I submit my rejected proposals - &lt;em&gt;and there are a lot of them!&lt;&#x2F;em&gt; - to other conferences, or use them as inspiration for new proposals.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;ok-how-can-i-make-my-proposal-better&quot;&gt;Ok, How Can I Make My Proposal Better?&lt;&#x2F;h1&gt;
&lt;p&gt;There are a lot of articles with tips and tricks for writing a great proposal.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;carolynvs&quot;&gt;@carolynvs&lt;&#x2F;a&gt; - who is on this year&#x27;s program committee with me - has some great tips tailored for this year&#x27;s conference in &lt;a href=&quot;https:&#x2F;&#x2F;carolynvanslyck.com&#x2F;blog&#x2F;2018&#x2F;12&#x2F;talk-at-gophercon&#x2F;&quot;&gt;her post&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There are also tons of other posts on how to &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;@fox&#x2F;how-to-write-a-successful-conference-proposal-4461509d3e32&quot;&gt;write&lt;&#x2F;a&gt; &lt;a href=&quot;https:&#x2F;&#x2F;dave.cheney.net&#x2F;2017&#x2F;02&#x2F;12&#x2F;how-to-write-a-successful-conference-proposal&quot;&gt;great&lt;&#x2F;a&gt; &lt;a href=&quot;http:&#x2F;&#x2F;russolsen.com&#x2F;articles&#x2F;2012&#x2F;06&#x2F;21&#x2F;acing-the-technical-talk-getting-your-proposal-through-the-door.html&quot;&gt;proposals&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s no single foolproof formula for writing the &amp;quot;perfect&amp;quot; proposal, but the selection criteria on the &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2019&quot;&gt;CFP page&lt;&#x2F;a&gt; lay out generally what we&#x27;re looking for. I&#x27;ll add a little bit of detail for each item.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;relevance&quot;&gt;&amp;quot;Relevance&amp;quot;&lt;&#x2F;h2&gt;
&lt;p&gt;We look for talks that are related to &lt;em&gt;Go&lt;&#x2F;em&gt;. People come to GopherCon to learn about new stuff in the Go ecosystem, pick up new skills, and generally learn about Go and the community.&lt;&#x2F;p&gt;
&lt;p&gt;If you&#x27;re writing a proposal that you couldn&#x27;t write about any other language, technology or community, you&#x27;re on the right track.&lt;&#x2F;p&gt;
&lt;p&gt;Other the other hand, if you&#x27;re writing a proposal where you could swap out Go for another language, it probably won&#x27;t be accepted. If you&#x27;re writing a proposal that&#x27;s not related to programming or technology, it almost certainly won&#x27;t be accepted.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;clarity&quot;&gt;&amp;quot;Clarity&amp;quot;&lt;&#x2F;h2&gt;
&lt;p&gt;Show us you&#x27;ve thought through what you want to say in your talk. We want to see it in clear, detailed writing. I want to really stress this part. &lt;strong&gt;Write down everything you plan to talk about&lt;&#x2F;strong&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;As long as it&#x27;s clear writing, we want to read it. I personally love it when folks write a rough outline of their talk in the proposal. You can do this in the &amp;quot;notes&amp;quot; section of the CFP.&lt;&#x2F;p&gt;
&lt;p&gt;As it says in &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2019&quot;&gt;the CFP page&lt;&#x2F;a&gt;, &lt;em&gt;no proposal has ever been rejected because it was too long&lt;&#x2F;em&gt;.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;correctness&quot;&gt;&amp;quot;Correctness&amp;quot;&lt;&#x2F;h2&gt;
&lt;p&gt;In order for us to accept your proposal, we have to know that &lt;em&gt;you&lt;&#x2F;em&gt; know what you&#x27;re talking about. So tell us that you do!&lt;&#x2F;p&gt;
&lt;p&gt;It&#x27;s easy to talk yourself out of submitting because you don&#x27;t feel like you&#x27;re an expert. I do it all the time.&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s what helps me get over that feeling every time:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;We&#x27;re not looking for experts, we&#x27;re looking for experience&lt;&#x2F;strong&gt;. Experience can mean a lot of things. Here are two experiences that are equally valid and valuable for GopherCon:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;quot;Writing a scalable system to handle 1,000,000 requests per second at a large company&amp;quot;&lt;&#x2F;li&gt;
&lt;li&gt;&amp;quot;How I learned Go as a Javascript developer&amp;quot;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So tell us &lt;em&gt;clearly&lt;&#x2F;em&gt; what your experience is and how it applies to your talk. Sometimes your experience will be easy to write about in your talk description, but if it doesn&#x27;t fit there, make sure to tell us about it in the notes and&#x2F;or bio section of the CFP.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;achievability&quot;&gt;&amp;quot;Achievability&amp;quot;&lt;&#x2F;h2&gt;
&lt;p&gt;Sometimes I want to talk about &lt;em&gt;everything&lt;&#x2F;em&gt; in my talk. I get so excited about some technology that I want to go into 3 or 4 big things in one talk. But those proposals never get accepted.&lt;&#x2F;p&gt;
&lt;p&gt;I said above that we&#x27;re looking for clarity and detail, and that means &lt;strong&gt;your proposal needs to be focused on a specific topic&lt;&#x2F;strong&gt;. The elevator pitch section on the CFP requires that you tell us what you&#x27;re going to talk about in 300 characters or less, so if you can&#x27;t clearly summarize in that space, the talk is too long.&lt;&#x2F;p&gt;
&lt;p&gt;If your proposal is too broad, it&#x27;s great opportunity to split it up into multiple different talks!&lt;&#x2F;p&gt;
&lt;h2 id=&quot;impact&quot;&gt;&amp;quot;Impact&amp;quot;&lt;&#x2F;h2&gt;
&lt;p&gt;This is the absolute most important part for me. &lt;strong&gt;Tell us what the audience will get out of your talk&lt;&#x2F;strong&gt;. A good proposal will usually have something like the following at the end of the description: &amp;quot;the audience will walk away knowing ...&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;Also, remember that you&#x27;re not just telling the reviewers what the audience will get, you&#x27;re telling the &lt;em&gt;audience&lt;&#x2F;em&gt; why they should come to your talk. You&#x27;re selling both us and the audience in your talk description.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;that-s-it&quot;&gt;That&#x27;s It!&lt;&#x2F;h1&gt;
&lt;p&gt;If you feel like you have something to talk about, I really hope you &lt;a href=&quot;https:&#x2F;&#x2F;www.papercall.io&#x2F;gophercon-2019&quot;&gt;submit a proposal&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;I can&#x27;t personally help folks with their proposals this year, but if you&#x27;d like help, please ask folks in the &lt;a href=&quot;https:&#x2F;&#x2F;invite.slack.golangbridge.org&#x2F;&quot;&gt;Gophers Slack&lt;&#x2F;a&gt; for help.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Good luck!!&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Athens and Microsoft</title>
          <pubDate>Thu, 20 Dec 2018 17:45:40 -0800</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-and-microsoft/</link>
          <guid>/blog/athens-and-microsoft/</guid>
          <description>&lt;p&gt;As you might have read, Google recently &lt;a href=&quot;https:&#x2F;&#x2F;blog.golang.org&#x2F;modules2019&quot;&gt;announced&lt;&#x2F;a&gt; some of their plans for Go modules in 2019. And because I&#x27;m obsessed with module proxies&#x2F;registries&#x2F;repositories (more on the naming of these things in a future post), I paid special attention to the announcements about the services they&#x27;re launching in 2019. I&#x27;m happy that Google is stepping up in a big way to help dependency management in the Go ecosystem.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ll be writing more in future posts about how Athens works with this new stuff, but I&#x27;m focusing here on the Athens community.&lt;&#x2F;p&gt;
&lt;p&gt;Obviously none of the opinions in here necessarily reflect the policies or opinions of Microsoft (my employer).&lt;&#x2F;p&gt;
&lt;h1 id=&quot;athens-is-a-community-project&quot;&gt;Athens is a Community Project&lt;&#x2F;h1&gt;
&lt;p&gt;The Go team mentioned the Athens project very briefly in their post. I was obviously disappointed that we weren&#x27;t mentioned a little more, but it&#x27;s their announcement and their blog, so I respect their choice. I was, however, very disappointed in how they referred to the project as &amp;quot;Microsoft&#x27;s Athens project.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;Since that post came out, I&#x27;ve seen and personally gotten lots of questions on what they meant by that.&lt;&#x2F;p&gt;
&lt;p&gt;I regret that I have to write this article, but the Athens community means more than anything else to me. Because the Go team&#x27;s post didn&#x27;t acknowledge that the vast majority of amazing Athenians are not Microsoft engineers, I&#x27;d like to clear that up here.&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Quick note: I talk to lots of folks on the Go team regularly and I like and respect them. I&#x27;m not trying to call any one of them out here, instead I want to set the record straight on Athens and to acknowledge the amazing community we have.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;h1 id=&quot;athens-is-a-community-project-not-microsoft-s&quot;&gt;Athens is a Community Project, Not Microsoft&#x27;s&lt;&#x2F;h1&gt;
&lt;p&gt;I wrote the first prototype of Athens in my free time. After I moved it out of my personal organization into the &lt;code&gt;gomods&lt;&#x2F;code&gt; org, I focused on growing a diverse open source community outside of Microsoft to work on Athens. The &lt;code&gt;gomods&lt;&#x2F;code&gt; org is administered by everyone on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orgs&#x2F;gomods&#x2F;teams&#x2F;maintainers&#x2F;members&quot;&gt;Athens maintainers team&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;Microsoft pays me now in part to work on Athens (I have other unrelated responsibilities as well), and I&#x27;m empowered to keep working on Athens and help grow the community as I have been.&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s nothing proprietary in Athens and it&#x27;s MIT licensed. Both me and lots of other folks involved use Athens (or have plans to) and contribute bugfixes and features upstream to &lt;code&gt;gomods&#x2F;athens&lt;&#x2F;code&gt;. Everything with the project happens on Github, in the open.&lt;&#x2F;p&gt;
&lt;p&gt;This is the part that I absolutely want you to read. If you want more details, read on.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;more-details&quot;&gt;More Details&lt;&#x2F;h1&gt;
&lt;p&gt;At the moment, me, &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bketelsen&quot;&gt;@bketelsen&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;carolynvs&quot;&gt;@carolynvs&lt;&#x2F;a&gt; are the 3 folks from Microsoft on the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;orgs&#x2F;gomods&#x2F;teams&#x2F;maintainers&#x2F;members&quot;&gt;core maintainers team&lt;&#x2F;a&gt; (there are 8 total), and the MS maintainers don&#x27;t have special voting rights or influence over the project.&lt;&#x2F;p&gt;
&lt;p&gt;We also have &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;contributing&#x2F;community&#x2F;participating&#x2F;&quot;&gt;clear guidelines&lt;&#x2F;a&gt; on how anyone can advance from community member to contributor to maintainer, and there&#x27;s no language in that document that indicates that you have to work at a specific company or organization to advance. We have a track record on both the contributors and maintainers teams to prove that as well.&lt;&#x2F;p&gt;
&lt;p&gt;One of our contributors, &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;chriscoffee&quot;&gt;chriscoffee&lt;&#x2F;a&gt;, submitted an &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;golang&#x2F;go&#x2F;issues&#x2F;29361&quot;&gt;issue&lt;&#x2F;a&gt; two days ago to &lt;code&gt;golang&#x2F;go&lt;&#x2F;code&gt; asking that the &amp;quot;Microsoft&amp;quot; be removed from &amp;quot;Microsoft&#x27;s Athens Project&amp;quot; on the blog. There&#x27;s been a little bit of movement on it since then.&lt;&#x2F;p&gt;
&lt;p&gt;I hope that until Google removes that language from their post, this post will be enough to show that Athens is not Microsoft&#x27;s project. It is the Go community&#x27;s project, and Microsoft engineers contribute (some of them get paid to contribute) to the project.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;more-to-come&quot;&gt;More to Come&lt;&#x2F;h1&gt;
&lt;p&gt;I&#x27;ll be writing more about the technical aspects of Athens and specifically how it fits into the awesome new module-ey stuff in the community, particularly JFrog&#x27;s GoCenter and of course the three new Google announced technologies.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Give Away Your OSS Power</title>
          <pubDate>Wed, 31 Oct 2018 11:25:58 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/give-up-power/</link>
          <guid>/blog/give-up-power/</guid>
          <description>&lt;p&gt;Pretend you&#x27;re an OSS maintainer - how do you figure out if your project is doing well? Some ideas:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Number of contributors&lt;&#x2F;li&gt;
&lt;li&gt;Forks&lt;&#x2F;li&gt;
&lt;li&gt;Stars&lt;&#x2F;li&gt;
&lt;li&gt;PRs closed&lt;&#x2F;li&gt;
&lt;li&gt;Average issues opened per day&lt;&#x2F;li&gt;
&lt;li&gt;Twitter followers to your project account&lt;&#x2F;li&gt;
&lt;li&gt;Views to your project website&lt;&#x2F;li&gt;
&lt;li&gt;All of the above?&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;And the list goes on...&lt;&#x2F;p&gt;
&lt;h1 id=&quot;the-bus-factor&quot;&gt;The Bus Factor&lt;&#x2F;h1&gt;
&lt;p&gt;There&#x27;s one more big one to add to the list. How many people have to drop out before the project is totally screwed and how hard would those people be to replace? In other words, what is your project&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Bus_factor&quot;&gt;bus factor&lt;&#x2F;a&gt;?&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;give-away-power&#x2F;bus-factor.jpg&quot; alt=&quot;bus-factor&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;That should be the first measure of any project&#x27;s success and health, because everything else is a vanity number if if can all go away in the blink of an eye.&lt;&#x2F;p&gt;
&lt;p&gt;I learned this lesson with &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;Athens&lt;&#x2F;a&gt; sorta recently when I had to drop out of the project for a week. I had to go to the hospital and get my appendix removed on Monday, and didn&#x27;t start doing work again til Friday. I wrote on the &lt;a href=&quot;https:&#x2F;&#x2F;medium.com&#x2F;that-conference&#x2F;on-stepping-away-4a1ab23be68d&quot;&gt;THAT Conference blog&lt;&#x2F;a&gt; how I learned to step away from a project, and how valuable that can be for everyone. But I also learned how crucial it is to not hold all the power on the project.&lt;&#x2F;p&gt;
&lt;p&gt;Early on with Athens, I wanted to set a precedent that I would not be the one with all the power. That meant I wasn&#x27;t the only one with the login to a thing, and also I wasn&#x27;t the only decision maker. I was lucky that the precedent stuck early, and we haven&#x27;t lost it. When we get a new core maintainer, they immediately get the keys to everything:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;On the &amp;quot;maintainers&amp;quot; Github team in the &lt;code&gt;gomods&lt;&#x2F;code&gt; org&lt;&#x2F;li&gt;
&lt;li&gt;Administrative privilege for the &lt;code&gt;gomods&lt;&#x2F;code&gt; org&lt;&#x2F;li&gt;
&lt;li&gt;Admin on our Dockerhub org&lt;&#x2F;li&gt;
&lt;li&gt;Admin on our Netlify account (for our docs site)&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;There&#x27;s another big thing, too. All the other maintainers work really hard to make sure they actually start making decisions in public. It&#x27;s really important that they and everyone else feel comfortable with that.&lt;&#x2F;p&gt;
&lt;p&gt;If we didn&#x27;t have that process, I would have probably come back from the hospital to a bunch of stagnant PRs and smart folks just stuck waiting to make progress.&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Athens Running on Zeit</title>
          <pubDate>Wed, 24 Oct 2018 12:31:07 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/athens-zeit/</link>
          <guid>/blog/athens-zeit/</guid>
          <description>&lt;p&gt;I&#x27;ve been a long time admirer-er of &lt;a href=&quot;https:&#x2F;&#x2F;zeit.co&quot;&gt;Zeit&lt;&#x2F;a&gt;. Besides the technical part (which is really good), their platform gives people really easy, understandable and (most importantly) trustworthy tools to get a real app into production. I&#x27;ve wanted something like this to exist for Kubernetes for a while (I even &lt;a href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;arschles&#x2F;7a91b621c9b259bb17c371bb4a2a8773&quot;&gt;wrote&lt;&#x2F;a&gt; about that, but I digress.)&lt;&#x2F;p&gt;
&lt;p&gt;The platform was for just Node applications for a while, but containers are a thing y&#x27;all, and Zeit lets you deploy them to its platform now (they announced it &lt;a href=&quot;https:&#x2F;&#x2F;zeit.co&#x2F;blog&#x2F;serverless-docker&quot;&gt;mid August&lt;&#x2F;a&gt;.) It does all the same stuff as it does for Node, but with Docker:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Write a &lt;code&gt;Dockerfile&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Run &lt;code&gt;now&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Profit&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;There&#x27;s a ton going on there in step 2, but the basics:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Push the &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; and context up to Zeit&lt;&#x2F;li&gt;
&lt;li&gt;Zeit does the &lt;code&gt;docker build&lt;&#x2F;code&gt; on their infrastructure&lt;&#x2F;li&gt;
&lt;li&gt;Zeit pushes the image to their registry (I assume they run an internal registry but no idea really)&lt;&#x2F;li&gt;
&lt;li&gt;Zeit pulls the image into their serving infrastructure, configures it, does routing, &lt;del&gt;checks if you paid and gives you all the amazing hardware if you have and otherwise runs you on a RaspberryPi&lt;&#x2F;del&gt; and runs your image on their servers&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I mean, I&#x27;ve blogged about &lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;az-acr-build-or-docker-build&#x2F;&quot;&gt;ACR Builds&lt;&#x2F;a&gt; before, but this is next level. One command gives you everything.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;athens-zeit&#x2F;yessss.jpg&quot; &#x2F;&gt;
&lt;h1 id=&quot;athens-ing-it-up&quot;&gt;Athens-ing it Up&lt;&#x2F;h1&gt;
&lt;p&gt;There&#x27;s this &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;rad project&lt;&#x2F;a&gt; I know about. Something about Go and dependencies IDK but it has a &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; and you can do cool stuff with it. So why not see if Zeit can handle this beast??&lt;&#x2F;p&gt;
&lt;p&gt;I had to tweak the Dockerfile a little bit because the CLI (&lt;code&gt;now&lt;&#x2F;code&gt;) doesn&#x27;t let you specify custom &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; locations (I don&#x27;t think???) but that was pretty much it (check out the &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;athens&#x2F;pull&#x2F;2&quot;&gt;PR&lt;&#x2F;a&gt;.) Now I can do this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;➜  athens git:(zeit-now) now --docker
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Deploying ~&#x2F;github&#x2F;athens under arschles
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Your deployment&amp;#39;s code and logs will be publicly accessible because you are subscribed to the OSS plan.
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; NOTE: You can use `now --public` or upgrade your plan (https:&#x2F;&#x2F;zeit.co&#x2F;account&#x2F;plan) to skip this prompt
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; https:&#x2F;&#x2F;athens-ukiespgiyz.now.sh [in clipboard] (sfo1) [5s]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Building…
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Sending build context to Docker daemon  37.83MB
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 1&#x2F;16 : FROM golang:1.11-alpine AS builder
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 95ec94706ff6
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 2&#x2F;16 : RUN mkdir &#x2F;proj
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Using cache
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 7690d95ac3b4
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 3&#x2F;16 : WORKDIR &#x2F;proj
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Using cache
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 89a1e2d291ec
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 4&#x2F;16 : COPY . .
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 6fdf77ac18ec
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 5&#x2F;16 : ENV GO111MODULE=on
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in 61601842b526
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container 61601842b526
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 75be7b20b225
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 6&#x2F;16 : ENV GOPROXY=https:&#x2F;&#x2F;microsoftgoproxy.azurewebsites.net
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in 8e92f2e31291
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container 8e92f2e31291
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 8f7e12c7fef4
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 7&#x2F;16 : RUN GO111MODULE=on CGO_ENABLED=0 go build -o &#x2F;bin&#x2F;athens-proxy .&#x2F;cmd&#x2F;proxy
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in b25589823d39
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: finding github.com&#x2F;fatih&#x2F;color v1.7.0
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: finding github.com&#x2F;golang&#x2F;glog v0.0.0-20160126235308-23def4e6c14b
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: finding github.com&#x2F;minio&#x2F;minio-go v6.0.5+incompatible
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: finding github.com&#x2F;gobuffalo&#x2F;mw-csrf v0.0.0-20180802151833-446ff26e108b
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: finding github.com&#x2F;tinylib&#x2F;msgp v1.0.2
&lt;&#x2F;span&gt;&lt;span&gt;[SNIP]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: downloading github.com&#x2F;markbates&#x2F;goth v1.46.0
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: downloading github.com&#x2F;rs&#x2F;cors v1.5.0
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: downloading github.com&#x2F;gobuffalo&#x2F;buffalo v0.13.1
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: downloading github.com&#x2F;unrolled&#x2F;secure v0.0.0-20181005190816-ff9db2ff917f
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; go: downloading github.com&#x2F;mitchellh&#x2F;go-homedir v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;[SNIP]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container b25589823d39
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; d61630e38cd9
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 8&#x2F;16 : FROM alpine
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 196d12cf6ab1
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 9&#x2F;16 : ENV GO111MODULE=on
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Using cache
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; cd147951c9a7
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 10&#x2F;16 : COPY --from=builder &#x2F;bin&#x2F;athens-proxy &#x2F;bin&#x2F;athens-proxy
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Using cache
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; a462fc7bd78b
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 11&#x2F;16 : COPY --from=builder &#x2F;proj&#x2F;config.dev.toml &#x2F;config&#x2F;config.toml
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 3c99d68507fe
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 12&#x2F;16 : COPY --from=builder &#x2F;usr&#x2F;local&#x2F;go&#x2F;bin&#x2F;go &#x2F;bin&#x2F;go
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; bcac2f09281a
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 13&#x2F;16 : RUN apk update &amp;amp;&amp;amp;      apk add --no-cache bzr git mercurial openssh-client subversion procps fossil &amp;amp;&amp;amp;     mkdir -p &#x2F;usr&#x2F;local&#x2F;go
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in 49fd4fa48969
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; fetch http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;main&#x2F;x86_64&#x2F;APKINDEX.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; fetch http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;community&#x2F;x86_64&#x2F;APKINDEX.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; v3.8.1-38-g898a0bb28a [http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;main]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; v3.8.1-35-ga062ffc9e8 [http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;community]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; OK: 9539 distinct packages available
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; fetch http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;main&#x2F;x86_64&#x2F;APKINDEX.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; fetch http:&#x2F;&#x2F;dl-cdn.alpinelinux.org&#x2F;alpine&#x2F;v3.8&#x2F;community&#x2F;x86_64&#x2F;APKINDEX.tar.gz
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; (1&#x2F;33) Installing libbz2 (1.0.6-r6)
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; (2&#x2F;33) Installing expat (2.2.5-r0)
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; (3&#x2F;33) Installing libffi (3.2.1-r4)
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; (4&#x2F;33) Installing gdbm (1.13-r1)
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; (5&#x2F;33) Installing ncurses-terminfo-base (6.1_p20180818-r1)
&lt;&#x2F;span&gt;&lt;span&gt;[SNIP]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Executing subversion-1.10.0-r0.pre-install
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Executing busybox-1.28.4-r1.trigger
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Executing ca-certificates-20171114-r3.trigger
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; OK: 123 MiB in 46 packages
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container 49fd4fa48969
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; e8a15877e355
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 14&#x2F;16 : ENV GO_ENV=production
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in ac91ac08096a
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container ac91ac08096a
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 096f14de684e
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 15&#x2F;16 : EXPOSE 3000
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in eb60ce775db6
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container eb60ce775db6
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 0075a1cdb3c5
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Step 16&#x2F;16 : CMD [&amp;quot;athens-proxy&amp;quot;, &amp;quot;-config_file=&#x2F;config&#x2F;config.toml&amp;quot;]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; Running in 33e543286967
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Removing intermediate container 33e543286967
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;  ---&amp;gt; 211b19e9d4a4
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Successfully built 211b19e9d4a4
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Successfully tagged build:RWio8EYjdNbuK5HXd31rKHAp_1540411816
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; ▲ Assembling image
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; ▲ Storing image (64.0M)
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Build completed
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Verifying instantiation in sfo1
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; [0] buffalo: Unless you set SESSION_SECRET env variable, your session storage is not protected!
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; [0] time=&amp;quot;2018-10-24T20:12:29Z&amp;quot; level=info msg=&amp;quot;Exporter not specified. Traces won&amp;#39;t be exported&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; [0] buffalo: Starting application at :3000
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; ✔ Scaled 1 instance in sfo1 [14s]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; Success! Deployment ready
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;That&#x27;s using a &lt;a href=&quot;https:&#x2F;&#x2F;docs.docker.com&#x2F;develop&#x2F;develop-images&#x2F;multistage-build&#x2F;&quot;&gt;multi stage build&lt;&#x2F;a&gt; to build Athens inside a &lt;code&gt;Dockerfile&lt;&#x2F;code&gt;, on Zeit&#x27;s infrastructure. And I inception-ed it because I used a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;issues&#x2F;772&quot;&gt;hosted Athens module proxy&lt;&#x2F;a&gt; to build Athens itself inside the build, on Zeit&#x27;s infrastructure.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;athens-zeit&#x2F;diagram.png&quot; &#x2F;&gt;
&lt;p&gt;Here&#x27;s what it looks like after it&#x27;s deployed:&lt;&#x2F;p&gt;
&lt;pre style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot;&gt;&lt;code&gt;&lt;span&gt;➜  athens git:(zeit-now) now list
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; 5 total deployments found under arschles [314ms]
&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt; To list more deployments for an app run `now ls [app]`
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  app       url                         inst #    type      state    age
&lt;&#x2F;span&gt;&lt;span&gt;  athens    athens-ukiespgiyz.now.sh         -    DOCKER    READY    4m
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;There&#x27;s a UI for all this on the zeit.co site too. They even stream build logs to the site too #AMAZE&lt;&#x2F;p&gt;
&lt;h1 id=&quot;trying-it-out&quot;&gt;Trying it Out&lt;&#x2F;h1&gt;
&lt;p&gt;So basically I ran a few commands and got a ton of output on the CLI. Machines are doing my bidding; success!&lt;&#x2F;p&gt;
&lt;p&gt;Now that it&#x27;s running, I wanted to see how it performed. I started small with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;bld&quot;&gt;bld&lt;&#x2F;a&gt; (&lt;a href=&quot;https:&#x2F;&#x2F;arschles.com&#x2F;blog&#x2F;az-acr-build-or-docker-build&#x2F;&quot;&gt;blog post&lt;&#x2F;a&gt; for background on that tool if you&#x27;re interested):&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;➜  bld git:(master) export GOPROXY=https:&#x2F;&#x2F;athens-ukiespgiyz.now.sh
&lt;&#x2F;span&gt;&lt;span&gt;➜  bld git:(master) sudo rm -r $GOPATH&#x2F;pkg&#x2F;mod
&lt;&#x2F;span&gt;&lt;span&gt;➜  bld git:(master) time go build
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;magefile&#x2F;mage v1.4.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;tdewolff&#x2F;parse v2.3.3+incompatible
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;cobra v0.0.3
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;mitchellh&#x2F;go-homedir v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;mattn&#x2F;go-isatty v0.0.3
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;viper v1.2.1
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;dlclark&#x2F;regexp2 v1.1.6
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;tdewolff&#x2F;minify v2.3.5+incompatible
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;pflag v1.0.3
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;magiconair&#x2F;properties v1.8.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding gopkg.in&#x2F;yaml.v2 v2.2.1
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;fsnotify&#x2F;fsnotify v1.4.7
&lt;&#x2F;span&gt;&lt;span&gt;go: finding golang.org&#x2F;x&#x2F;text v0.3.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding golang.org&#x2F;x&#x2F;sys v0.0.0-20180906133057-8cf3aee42992
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;pelletier&#x2F;go-toml v1.2.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;jwalterweatherman v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;mitchellh&#x2F;mapstructure v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;pflag v1.0.2
&lt;&#x2F;span&gt;&lt;span&gt;go: finding gopkg.in&#x2F;check.v1 v0.0.0-20161208181325-20d25e280405
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;afero v1.1.2
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;spf13&#x2F;cast v1.2.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;hashicorp&#x2F;hcl v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: finding github.com&#x2F;davecgh&#x2F;go-spew v1.1.1
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;viper v1.2.1
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;cobra v0.0.3
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;mitchellh&#x2F;go-homedir v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;magefile&#x2F;mage v1.4.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;mitchellh&#x2F;mapstructure v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;hashicorp&#x2F;hcl v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;pelletier&#x2F;go-toml v1.2.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading gopkg.in&#x2F;yaml.v2 v2.2.1
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;jwalterweatherman v1.0.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;magiconair&#x2F;properties v1.8.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;fsnotify&#x2F;fsnotify v1.4.7
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;pflag v1.0.3
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;cast v1.2.0
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading github.com&#x2F;spf13&#x2F;afero v1.1.2
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading golang.org&#x2F;x&#x2F;sys v0.0.0-20180906133057-8cf3aee42992
&lt;&#x2F;span&gt;&lt;span&gt;go: downloading golang.org&#x2F;x&#x2F;text v0.3.0
&lt;&#x2F;span&gt;&lt;span&gt;go build  4.97s user 2.06s system 7% cpu 1:32.16 total
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So, that kinda took a while. Building Athens itself took &lt;em&gt;forever&lt;&#x2F;em&gt; as well (about 6:30), but there are a few reasons for that, and ways to get big ole speedups from experience hosting Athens:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Run this with more than one instance&lt;&#x2F;li&gt;
&lt;li&gt;Hook this up to external storage
&lt;ul&gt;
&lt;li&gt;You&#x27;d need to do this for #1 to work anyway&lt;&#x2F;li&gt;
&lt;li&gt;This is using local disk and I have no idea what or how fast that is on Zeit&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Pre-seed the Athens cache with things
&lt;ul&gt;
&lt;li&gt;Everything I showed was from a cold local (&lt;code&gt;$GOPATH&#x2F;pkg&#x2F;mod&lt;&#x2F;code&gt;) and hosted (on the Zeit Athens server) cache&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;All in all, this is a pretty rad platform. I give it pretty rad. Not mega rad - that&#x27;s reserved for &lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Office_Assistant&quot;&gt;clippy&lt;&#x2F;a&gt; level things, but wayyy higher than semi-rad.&lt;&#x2F;p&gt;
&lt;img src=&quot;&#x2F;images&#x2F;athens-zeit&#x2F;clippy.jpg&quot; height=&quot;500px&quot; &#x2F;&gt;
&lt;div style=&quot;text-align:center;font-size:70%;&quot;&gt;Zeit: not quite clippy level, but pretty darn close!&lt;&#x2F;div&gt;
&lt;p&gt;Keep on rockin&#x27; everybody!&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Absolutely Everybody is Welcome</title>
          <pubDate>Tue, 23 Oct 2018 20:53:21 +0000</pubDate>
          <author>Unknown</author>
          <link>/blog/absolutely-everybody/</link>
          <guid>/blog/absolutely-everybody/</guid>
          <description>&lt;p&gt;I started the &lt;a href=&quot;https:&#x2F;&#x2F;gophersource.com&#x2F;study-group&quot;&gt;Go Study Group&lt;&#x2F;a&gt; and &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&quot;&gt;Athens&lt;&#x2F;a&gt; both about 6 months ago. I did each for super different reasons, but I wanted both to have strong communities. The people are what keeps these communities alive. Not me, not the technology, nothing else. It&#x27;s the people. And that&#x27;s why I&#x27;m so insistent on strong communities.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been part of lots of OSS communities - as a leader and as a member - and each one has either lifted me up or ground me down. I&#x27;m at a point in my career now where I can tell pretty quickly which type of community it&#x27;s gonna be for me. Like after reading a few issues or doing a PR or two.&lt;&#x2F;p&gt;
&lt;p&gt;So with that experience, here&#x27;s what a strong community is to me:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;We&#x27;re all nice to each other&lt;&#x2F;li&gt;
&lt;li&gt;We welcome everybody who wants to join us&lt;&#x2F;li&gt;
&lt;li&gt;Nobody dictates&lt;&#x2F;li&gt;
&lt;li&gt;We respect each others&#x27; time and ideas&lt;&#x2F;li&gt;
&lt;li&gt;We make it easy to get started and easy to keep going&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;I went into detail on a lot of this in the Athens project with a &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;gomods&#x2F;athens&#x2F;blob&#x2F;master&#x2F;PHILOSOPHY.md&quot;&gt;&amp;quot;philosophy document&amp;quot;&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;inclusion&quot;&gt;Inclusion&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;images&#x2F;avg-joes.png&quot; alt=&quot;Average joe&#x27;s&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;There&#x27;s no way I can sum up all those things in that list above, but I want to be able to say something to newcomers to whatever thing to basically say: &amp;quot;you&#x27;re welcome here, and we&#x27;ll do everything we can to make it nice here.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;So I say this: &lt;em&gt;&amp;quot;absolutely everybody is welcome&amp;quot;&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;p&gt;I don&#x27;t have some grand story of how I came up with this catch phrase or anything, I just feel like it sums up that list above (by the way I know it doesn&#x27;t technically come close :P).&lt;&#x2F;p&gt;
&lt;p&gt;I use this all the time to say: &amp;quot;Hey folks! You&#x27;re welcome to join us. We&#x27;re inclusive, but we&#x27;re more too. Someone is gonna be there to greet you and help you. You&#x27;re not gonna be judged. You&#x27;re not gonna have to run some huge gauntlet just to get started or prove yourself. We&#x27;re happy you joined and you&#x27;re not different than us. We have work to do and we want you to do it with us.&amp;quot;&lt;&#x2F;p&gt;
&lt;p&gt;If you want to join either&#x2F;both of these communities, here&#x27;s how:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Go Study Group: we&#x27;re a weekly group of Gophers who get together and chat about Go. Meeting invite is here: &lt;a href=&quot;https:&#x2F;&#x2F;gophersource.com&#x2F;study-group&quot;&gt;https:&#x2F;&#x2F;gophersource.com&#x2F;study-group&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Athens: A module proxy for Go&#x27;s dependency management. More information on getting involved is here: &lt;a href=&quot;https:&#x2F;&#x2F;docs.gomods.io&#x2F;contributing&#x2F;community&#x2F;participating&#x2F;&quot;&gt;https:&#x2F;&#x2F;docs.gomods.io&#x2F;contributing&#x2F;community&#x2F;participating&#x2F;&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
</description>
      </item>
      <item>
          <title>az acr build or docker build?</title>
          <pubDate>Mon, 08 Oct 2018 16:19:36 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/bld/</link>
          <guid>/blog/bld/</guid>
          <description>&lt;p&gt;I had heard about &lt;a href=&quot;https:&#x2F;&#x2F;cda.ms&#x2F;HB&quot;&gt;&lt;code&gt;acr build&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; when it came out and didn&#x27;t pay much attention to it. I was like &amp;quot;I don&#x27;t wanna send my whole build context over the internet that&#x27;s crazy.&amp;quot; That was kinda a facepalm in retrospect because it&#x27;s kinda sorta my job to keep up on Azure things that are container related.&lt;&#x2F;p&gt;
&lt;p&gt;Anyway, now that I actually &lt;em&gt;looked&lt;&#x2F;em&gt; at this thing, I have some feels and codes!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;how-i-un-facepalmed-myself&quot;&gt;How I Un-Facepalmed Myself&lt;&#x2F;h1&gt;
&lt;p&gt;I was looking at &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bketelsen&quot;&gt;Brian&lt;&#x2F;a&gt;&#x27;s &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;bketelsen&#x2F;acrtasks&quot;&gt;acrtasks repo&lt;&#x2F;a&gt; the other day and &lt;em&gt;really&lt;&#x2F;em&gt; learned what Azure Container Registry tasks are about. You run &lt;code&gt;acr build&lt;&#x2F;code&gt; and the CLI zips up your local build context, sends it to the server, and that does the &lt;code&gt;docker build&lt;&#x2F;code&gt; for you. Not that exciting, but as Brian said, it&#x27;ll automatically re-build when base images change. Rad!&lt;&#x2F;p&gt;
&lt;p&gt;Here&#x27;s some more rad things that I like even better than that:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;I don&#x27;t need a local Docker daemon!&lt;&#x2F;li&gt;
&lt;li&gt;I don&#x27;t have to push image layers up to a registry&lt;&#x2F;li&gt;
&lt;li&gt;I don&#x27;t have to do registry auth - just auth to Azure&lt;&#x2F;li&gt;
&lt;li&gt;I don&#x27;t need a local Docker daemon&lt;&#x2F;li&gt;
&lt;li&gt;I don&#x27;t need a local Docker daemon :P&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Basically, I can just write my &lt;code&gt;Dockerfile&lt;&#x2F;code&gt; and run &lt;code&gt;acr build&lt;&#x2F;code&gt; without setting up the daemon. I&#x27;d want to on my dev machine, but I don&#x27;t have to figure it out in other places:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;CI&#x2F;CD systems like &lt;a href=&quot;https:&#x2F;&#x2F;buildkite.com&#x2F;&quot;&gt;Buildkite&lt;&#x2F;a&gt; or &lt;a href=&quot;https:&#x2F;&#x2F;travis-ci.org&#x2F;&quot;&gt;Travis&lt;&#x2F;a&gt;. Travis has Docker support but you have to spin up a whole VM. You can run &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;helm&#x2F;charts&#x2F;tree&#x2F;master&#x2F;stable&#x2F;buildkite&quot;&gt;Buildkite agents on Kubernetes&lt;&#x2F;a&gt;, but talking to the Docker daemon from inside a pod is crazytown (I&#x27;ll die on that hill, btw :P) &lt;&#x2F;li&gt;
&lt;li&gt;I repeat: don&#x27;t let pods talk to the external Docker daemon. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;jpetazzo&#x2F;dind&quot;&gt;DinD&lt;&#x2F;a&gt; isn&#x27;t much better on Kubernetes&lt;&#x2F;li&gt;
&lt;li&gt;Underpowered systems - Ever try building &lt;a href=&quot;https:&#x2F;&#x2F;gobuffalo.io&quot;&gt;Buffalo&lt;&#x2F;a&gt; on a Raspberry Pi? Not fast, kinda painful&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Basically, I just feel like not having to run a Docker daemon sometimes is one less thing to worry about. And it&#x27;s a big thing.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;enter-bld&quot;&gt;Enter &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;bld&quot;&gt;&lt;code&gt;bld&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Getting real again, I basically wanted to do &lt;code&gt;docker build&lt;&#x2F;code&gt;s on my dev machines and &lt;code&gt;az acr build&lt;&#x2F;code&gt;s playing with Buildkite and stuff. I pretty quickly got tired of writing the same bash script to decide which one to use. So I wrote this little, super basic tool that just does the &lt;code&gt;if&lt;&#x2F;code&gt; for me.&lt;&#x2F;p&gt;
&lt;p&gt;Basically you do this:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;console&quot; style=&quot;background-color:#f5f5f5;color:#1f1f1f;&quot; class=&quot;language-console &quot;&gt;&lt;code class=&quot;language-console&quot; data-lang=&quot;console&quot;&gt;&lt;span&gt;bld -t my&#x2F;image .
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;It does &lt;code&gt;docker build&lt;&#x2F;code&gt; if the Docker CLI is on the system, otherwise does &lt;code&gt;az acr build&lt;&#x2F;code&gt;. It&#x27;s &lt;em&gt;suuuper&lt;&#x2F;em&gt; basic and that&#x27;s kinda the point (at least right now). You decide whether you want &lt;code&gt;docker build&lt;&#x2F;code&gt; or &lt;code&gt;az acr build&lt;&#x2F;code&gt; based on whether you have &lt;code&gt;docker&lt;&#x2F;code&gt; (the CLI) installed.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;other-stuff-already-exists&quot;&gt;Other Stuff Already Exists&lt;&#x2F;h2&gt;
&lt;p&gt;Yup, and honestly lots of it is better and I might steal it :P&lt;&#x2F;p&gt;
&lt;p&gt;The coolest thing I know of is part of &lt;a href=&quot;https:&#x2F;&#x2F;draft.sh&quot;&gt;&lt;code&gt;draft&lt;&#x2F;code&gt;&lt;&#x2F;a&gt;. The rad &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bacongobbler&quot;&gt;Matt&lt;&#x2F;a&gt; saw this and DM&#x27;d me with &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Azure&#x2F;draft&#x2F;tree&#x2F;master&#x2F;pkg&#x2F;builder&quot;&gt;this code&lt;&#x2F;a&gt; in the project. The basic difference between &lt;code&gt;bld&lt;&#x2F;code&gt; and &lt;code&gt;draft&lt;&#x2F;code&gt; is that the former is just for building Docker images. The latter is for everything from coding to deploying to Kubernetes. By the way, Draft is rad and you should check it out :)&lt;&#x2F;p&gt;
&lt;p&gt;I want to keep this thing simple and I&#x27;m not sure whether to &lt;del&gt;shamelessly steal&lt;&#x2F;del&gt; use that code or just keep relying on &lt;code&gt;az&lt;&#x2F;code&gt;. &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;arschles&#x2F;bld&#x2F;issues&#x2F;1&quot;&gt;To be determined&lt;&#x2F;a&gt; I guess!&lt;&#x2F;p&gt;
&lt;p&gt;Keep on rockin&#x27;&lt;&#x2F;p&gt;
</description>
      </item>
      <item>
          <title>Emerging Languages</title>
          <pubDate>Fri, 05 Oct 2018 12:01:16 -0700</pubDate>
          <author>Unknown</author>
          <link>/blog/emerging-languages/</link>
          <guid>/blog/emerging-languages/</guid>
          <description>&lt;p&gt;My history with programming languages and frameworks is approximately this:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;I started my career writing PHP 3&lt;&#x2F;li&gt;
&lt;li&gt;Then I wrote C&#x2F;C++ (&lt;a href=&quot;https:&#x2F;&#x2F;www.boost.org&#x2F;&quot;&gt;Boost&lt;&#x2F;a&gt; forever!)&lt;&#x2F;li&gt;
&lt;li&gt;Then I discovered Java, Scala, Objective-C, Perl, Python, Clojure and Haskell all at once&lt;&#x2F;li&gt;
&lt;li&gt;Then I ditched it all for Go&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Oh, and throw some javascript in there.&lt;&#x2F;p&gt;
&lt;p&gt;I&#x27;ve been pretty focused on Go for a while and was missing learning new languages. &lt;a href=&quot;https:&#x2F;&#x2F;twitter.com&#x2F;bketelsen&quot;&gt;Brian Ketelsen&lt;&#x2F;a&gt; introduced me to &lt;a href=&quot;https:&#x2F;&#x2F;nim-lang.org&#x2F;&quot;&gt;Nim&lt;&#x2F;a&gt; and then I wanted more. So between my curiousity and his endless list of cool new tech, I&#x27;ve been checking out a few languages. &lt;&#x2F;p&gt;
&lt;p&gt;Here they are, along with my summaries of them:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;elixir-lang.org&#x2F;&quot;&gt;Elixir&lt;&#x2F;a&gt; - Ruby with less monkey patching but more macros&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Rust_(programming_language)&quot;&gt;Rust&lt;&#x2F;a&gt; - C++ with more bullets to shoot yourself in the foot with, but less foot&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;crystal-lang.org&#x2F;&quot;&gt;Crystal&lt;&#x2F;a&gt; - Compile Ruby to machine code. And now you can use all the cores!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;ballerina.io&#x2F;&quot;&gt;Ballerina&lt;&#x2F;a&gt; - Kinda like &lt;a href=&quot;https:&#x2F;&#x2F;metaparticle.io&#x2F;&quot;&gt;Metaparticle&lt;&#x2F;a&gt;, except it&#x27;s a language&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a href=&quot;https:&#x2F;&#x2F;swift.org&#x2F;&quot;&gt;Swift&lt;&#x2F;a&gt; - Objective C and Scala had a baby, and you can compile it on the LLVM&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Ok, I know it seems like I&#x27;m pretty sarcastic about all these languages, but I&#x27;m legitimately excited about all of these! I&#x27;m gonna play around with them for a while until I find something real to build.&lt;&#x2F;p&gt;
</description>
      </item>
    </channel>
</rss>
