<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Posts on @jdx</title><link>https://jdx.dev/posts/</link><description>Recent content in Posts on @jdx</description><generator>Hugo</generator><language>en</language><lastBuildDate>Mon, 14 Sep 2026 10:33:00 -0500</lastBuildDate><atom:link href="https://jdx.dev/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Dotfiles That Save Themselves</title><link>https://jdx.dev/posts/2026-09-07-dotfiles-that-save-themselves/</link><pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2026-09-07-dotfiles-that-save-themselves/</guid><description>&lt;p&gt;&lt;code&gt;mise bootstrap&lt;/code&gt; already supported two common ways of managing dotfiles: keep them in a repository and symlink them into place, as &lt;a href="https://www.gnu.org/software/stow/" class="external-link" target="_blank" rel="noopener"&gt;GNU Stow&lt;/a&gt; does, or generate the live files from stored sources and templates, as &lt;a href="https://www.chezmoi.io/user-guide/frequently-asked-questions/design/#why-doesnt-chezmoi-use-symlinks-like-gnu-stow" class="external-link" target="_blank" rel="noopener"&gt;chezmoi&lt;/a&gt; normally does.&lt;/p&gt;&#10;&lt;p&gt;I wanted to add a third model: &lt;strong&gt;automatic bidirectional syncing of the files I already edit, without symlinks&lt;/strong&gt;. Change &lt;code&gt;.zshrc&lt;/code&gt; on my laptop and mise saves it, shares it, and applies it on my desktop. Edit it on the desktop and the change flows back. The live files stay where they are, and I don&amp;rsquo;t have to remember to copy edits back into a source directory or run a sync command after each change.&lt;/p&gt;</description></item><item><title>Introducing mr-boxington: fix target/</title><link>https://jdx.dev/posts/2026-09-05-introducing-mr-boxington/</link><pubDate>Sat, 05 Sep 2026 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2026-09-05-introducing-mr-boxington/</guid><description>&lt;p&gt;I&amp;rsquo;m introducing &lt;a href="https://mr-boxington.jdx.dev" class="external-link" target="_blank" rel="noopener"&gt;mr-boxington&lt;/a&gt;, a build cache for Rust that shares compilations across your machine and cleans up after itself. The command is &lt;code&gt;mbx&lt;/code&gt;. Set it up once and keep running &lt;code&gt;cargo build&lt;/code&gt;, &lt;code&gt;cargo test&lt;/code&gt;, and &lt;code&gt;cargo clippy&lt;/code&gt; as usual.&lt;/p&gt;&#10;&lt;p&gt;The pitch is &lt;strong&gt;fix &lt;code&gt;target/&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;&#10;&lt;p&gt;If you work on several Rust projects, you probably have a lot of disk space tied up in build outputs. Add git worktrees and you get another copy of those outputs for every branch you&amp;rsquo;re working on. Delete them to reclaim space, and the next build spends time recreating much of the same work.&lt;/p&gt;</description></item><item><title>Introducing packslip</title><link>https://jdx.dev/posts/2026-09-05-introducing-packslip/</link><pubDate>Sat, 05 Sep 2026 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2026-09-05-introducing-packslip/</guid><description>&lt;p&gt;I&amp;rsquo;m introducing &lt;a href="https://packslip.dev" class="external-link" target="_blank" rel="noopener"&gt;packslip&lt;/a&gt;, a signed manifest vendors publish alongside their releases. It tells package managers which binaries to download, how to verify and install them, and where to find matching shell completions and agent skills.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://mise.jdx.dev" class="external-link" target="_blank" rel="noopener"&gt;mise&lt;/a&gt; supports packslip as a tier 1 backend, preferred over the github and aqua backends for new tools. Completions follow the tool version active in your project, and you can opt in to making that version&amp;rsquo;s skills available to your coding agent.&lt;/p&gt;</description></item><item><title>Going Full Time on Open Source</title><link>https://jdx.dev/posts/2026-04-17-going-full-time-on-open-source/</link><pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2026-04-17-going-full-time-on-open-source/</guid><description>&lt;p&gt;For the last several years, I&amp;rsquo;ve been building and maintaining developer tools in my spare time, chiefly &lt;a href="https://mise.jdx.dev" class="external-link" target="_blank" rel="noopener"&gt;mise&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;What started as a simple rewrite of &lt;a href="https://asdf-vm.com/" class="external-link" target="_blank" rel="noopener"&gt;asdf&lt;/a&gt; in Rust has become an incredibly successful local dev manager. mise now has &lt;a href="https://github.com/jdx/mise" class="external-link" target="_blank" rel="noopener"&gt;27k+ stars on GitHub&lt;/a&gt; and is the &lt;a href="https://formulae.brew.sh/analytics/install-on-request/30d/" class="external-link" target="_blank" rel="noopener"&gt;10th most downloaded Homebrew formula&lt;/a&gt;. In fact, roughly 1% of users typing &lt;code&gt;brew install&lt;/code&gt; are running &lt;code&gt;brew install mise&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve also watched mise show up in places I never expected, including &lt;a href="https://github.com/openai/codex-universal/blob/54b8f0434886aeff0817cc4d5a7d76bdb0d834d5/Dockerfile#L86-L100" class="external-link" target="_blank" rel="noopener"&gt;OpenAI Codex Universal&lt;/a&gt; and &lt;a href="https://github.com/NVIDIA/OpenShell/blob/e4d6f92d9b803719928eca5187b428a7ca55c1a5/mise.toml" class="external-link" target="_blank" rel="noopener"&gt;NVIDIA OpenShell&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Top 10 Features in Mise You're Not Using</title><link>https://jdx.dev/posts/2026-03-02-10-mise-features/</link><pubDate>Mon, 02 Mar 2026 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2026-03-02-10-mise-features/</guid><description>&lt;h2 id="1-task-sources-and-outputs"&gt;1. Task &lt;code&gt;sources&lt;/code&gt; and &lt;code&gt;outputs&lt;/code&gt; &lt;a class="heading-anchor" href="#1-task-sources-and-outputs" aria-label="Link to 1. Task sources and outputs"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;Any task can declare input/output globs. If all outputs are newer than all sources, the task is skipped entirely:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[tasks.build]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;run = &lt;span style="color:#f1fa8c"&gt;&amp;#34;cargo build&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sources = [&lt;span style="color:#f1fa8c"&gt;&amp;#34;Cargo.toml&amp;#34;&lt;/span&gt;, &lt;span style="color:#f1fa8c"&gt;&amp;#34;src/**/*.rs&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;outputs = [&lt;span style="color:#f1fa8c"&gt;&amp;#34;target/debug/myapp&amp;#34;&lt;/span&gt;]&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don&amp;rsquo;t want to enumerate outputs? Set &lt;code&gt;outputs = { auto = true }&lt;/code&gt; (which is actually the default when &lt;code&gt;sources&lt;/code&gt; is defined) — mise tracks a hash internally and skips the task if sources haven&amp;rsquo;t changed.&lt;/p&gt;</description></item><item><title>Shims: How they work in mise-en-place</title><link>https://jdx.dev/posts/2024-04-13-shims-how-they-work-in-mise-en-place/</link><pubDate>Sat, 13 Apr 2024 01:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2024-04-13-shims-how-they-work-in-mise-en-place/</guid><description>&lt;p&gt;Assuming you&amp;rsquo;ve setup &lt;a href="https://mise.jdx.dev" class="external-link" target="_blank" rel="noopener"&gt;mise&lt;/a&gt; and have it configured in a couple of&#10;projects to switch between different versions of node, that will happen automatically&#10;when entering the project directories:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ &lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; ~/src/proj1&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ node -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;20.0.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ &lt;span style="color:#8be9fd;font-style:italic"&gt;cd&lt;/span&gt; ~/src/proj2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ node -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;18.0.0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The way that mise is able to switch this version depends on whether you&amp;rsquo;ve activated&#10;mise with shims or not. In this article, I&amp;rsquo;ll explain how these methods work under the&#10;hood and the pros and cons of using them.&lt;/p&gt;</description></item><item><title>Shims: Build a version manager</title><link>https://jdx.dev/posts/2024-04-13-shims-build-a-version-manager/</link><pubDate>Sat, 13 Apr 2024 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2024-04-13-shims-build-a-version-manager/</guid><description>&lt;p&gt;I&amp;rsquo;ve spent the last decade of my career in the developer productivity space. In that time&#10;I&amp;rsquo;ve helped countless developers setup their dev environments. Something I see time and&#10;time again are developers struggling with version managers for their programming languages.&lt;/p&gt;&#10;&lt;p&gt;These tools are generally fine, but developers often don&amp;rsquo;t understand how they work which&#10;leads to frustration trying to get it to do what they want. In this article, we&amp;rsquo;ll build&#10;our own mini-version manager from scratch and along the way you&amp;rsquo;ll get an idea of how they&#10;work under the hood.&lt;/p&gt;</description></item><item><title>10 features in rtx you may have missed</title><link>https://jdx.dev/posts/2023-04-08-10-rtx-features/</link><pubDate>Sat, 08 Apr 2023 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2023-04-08-10-rtx-features/</guid><description>&lt;p&gt;Goal #1 with &lt;a href="https://rtx.pub" class="external-link" target="_blank" rel="noopener"&gt;rtx&lt;/a&gt; was to be a drop-in replacement for &lt;a href="https://asdf-vm.com" class="external-link" target="_blank" rel="noopener"&gt;asdf&lt;/a&gt;.&#10;We quickly&#10;achieved parity with asdf and now thousands of people have moved to rtx and are loving how much&#10;faster and easier to use it is.&#10;Since then, though, we haven&amp;rsquo;t stopped and a lot more features have been added you might not&#10;have seen.&lt;/p&gt;&#10;&lt;h2 id="1-arbitrary-env-vars"&gt;1. Arbitrary env vars &lt;a class="heading-anchor" href="#1-arbitrary-env-vars" aria-label="Link to 1. Arbitrary env vars"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;You can now set arbitrary environment variables in your &lt;code&gt;.rtx.toml&lt;/code&gt; files so now you can use rtx&#10;as a replacement for dotenv and common uses of direnv:&lt;/p&gt;</description></item><item><title>Beginner's Guide to rtx</title><link>https://jdx.dev/posts/2023-03-04-beginners-guide-to-rtx/</link><pubDate>Sat, 04 Mar 2023 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2023-03-04-beginners-guide-to-rtx/</guid><description>&lt;p&gt;&lt;a href="https://dev.to/jdxcode/beginners-guide-to-rtx-ac4" class="external-link" target="_blank" rel="noopener"&gt;https://dev.to/jdxcode/beginners-guide-to-rtx-ac4&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Introducing chim</title><link>https://jdx.dev/posts/2022-09-04-introducing-chim/</link><pubDate>Mon, 05 Sep 2022 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2022-09-04-introducing-chim/</guid><description>&lt;p&gt;&lt;em&gt;I had a 3-day weekend and decided to learn Rust while also putting together an idea I&amp;rsquo;ve&#10;had for a few years now: &lt;a href="https://chim.sh" class="external-link" target="_blank" rel="noopener"&gt;chim&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&#10;&lt;h1 id="background"&gt;Background &lt;a class="heading-anchor" href="#background" aria-label="Link to Background"&gt;#&lt;/a&gt;&lt;/h1&gt;&#10;&lt;p&gt;The problem I&amp;rsquo;ve wanted to solve is bootstrapping projects for local dev and CI/CD.&#10;Essentially, how tool versions are defined and installed—tools being things like programming&#10;language runtimes, package managers, and linters. For example, a project might require node-v18.8.0, jq-v1.6, and shellcheck-v0.8.0. Other versions of these tools may not be compatible.&lt;/p&gt;</description></item><item><title>10 Tips for Ramping Up as a Senior Engineer</title><link>https://jdx.dev/posts/2020-09-16-10-tips-ramping-up-as-a-senior-engineer/</link><pubDate>Wed, 16 Sep 2020 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2020-09-16-10-tips-ramping-up-as-a-senior-engineer/</guid><description>&lt;p&gt;I&amp;rsquo;ve learned the hard way that starting a job on a large engineering team as a senior engineer is more than simply writing good code. You have to drive initiatives at a social level and not simply because you know more about the codebase.&lt;/p&gt;&#10;&lt;p&gt;For a new hire this is especially a challenge. &lt;strong&gt;You need to learn the system that your colleagues already understand.&lt;/strong&gt; You might be a higher level but you&amp;rsquo;ll be a worse engineer for the first few months while you ramp up.&lt;/p&gt;</description></item><item><title>Async JavaScript Patterns for 2020</title><link>https://jdx.dev/posts/2020-01-19-async-javascript-patterns-for-2020/</link><pubDate>Sun, 19 Jan 2020 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2020-01-19-async-javascript-patterns-for-2020/</guid><description>&lt;p&gt;I&amp;rsquo;ve got a ton of different patterns I use when working with async code in JS. This article is a collection of them.&lt;/p&gt;&#10;&lt;p&gt;I&amp;rsquo;ve split them into 3 parts: &lt;a href="#promises" &gt;Promises&lt;/a&gt;, &lt;a href="#asyncawait" &gt;Async/Await&lt;/a&gt; and &lt;a href="#async-iterables" &gt;Async Iterables&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I see this as more of a ctrl-f reference for tasks like &lt;a href="#promises-timing-out-a-promise" &gt;&amp;ldquo;how do I timeout a promise&amp;rdquo;&lt;/a&gt;. Though I&amp;rsquo;ve also tried to keep it in rough order from beginner-advanced so if you want a comprehensive read of understanding async it should work well for that too.&lt;/p&gt;</description></item><item><title>Practical Guide to Closures</title><link>https://jdx.dev/posts/2020-01-12-closures/</link><pubDate>Sun, 12 Jan 2020 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2020-01-12-closures/</guid><description>&lt;blockquote&gt;&lt;p&gt;Closures are fairly common in JS but are also sometimes used in Go and Python and many other languages. This post is going to use JS but the concept is the same in all languages supporting closures.&lt;/p&gt;&#10;&lt;/blockquote&gt;&lt;p&gt;I found closures incredibly confusing for a long time—well after college and me working in the industry for years in fact. I found the concept confusing even when I was writing them all the time.&lt;/p&gt;</description></item><item><title>Pithy Guide to Kubernetes Part 2: Golang</title><link>https://jdx.dev/posts/2019-12-07-pithy-guide-to-kubernetes-part-2-golang/</link><pubDate>Sat, 07 Dec 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-12-07-pithy-guide-to-kubernetes-part-2-golang/</guid><description>&lt;p&gt;If you haven&amp;rsquo;t done &lt;a href="https://jdx.dev/posts/2019-12-06-pithy-guide-to-kubernetes-part-1" &gt;Part 1&lt;/a&gt; you&amp;rsquo;ll need to start there.&lt;/p&gt;&#10;&lt;p&gt;In Part 1 we setup Minikube and added an Ingress, Service, and Deployment to the k8s cluster. Now&#10;we&amp;rsquo;ll finish that work by giving the Deployment an image to load.&lt;/p&gt;&#10;&lt;p&gt;The full example code is available on &lt;a href="https://github.com/jdx/pithy-go" class="external-link" target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="build-the-go-api"&gt;Build the Go API &lt;a class="heading-anchor" href="#build-the-go-api" aria-label="Link to Build the Go API"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;First initialize this directory as a Go module: [optional, but recommended]&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod init&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now create a simple Go JSON API:&lt;/p&gt;</description></item><item><title>Pithy Guide to Kubernetes Part 2: Node.JS</title><link>https://jdx.dev/posts/2019-12-07-pithy-guide-to-kubernetes-part-2-nodejs/</link><pubDate>Sat, 07 Dec 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-12-07-pithy-guide-to-kubernetes-part-2-nodejs/</guid><description>&lt;p&gt;If you haven&amp;rsquo;t done &lt;a href="https://jdx.dev/posts/2019-12-06-pithy-guide-to-kubernetes-part-1" &gt;Part 1&lt;/a&gt; you&amp;rsquo;ll need to start there.&lt;/p&gt;&#10;&lt;p&gt;In Part 1 we setup Minikube and added an Ingress, Service, and Deployment to the k8s cluster. Now&#10;we&amp;rsquo;ll finish that work by giving the Deployment an image to load.&lt;/p&gt;&#10;&lt;p&gt;The full example code is available on &lt;a href="https://github.com/jdx/pithy-nodejs" class="external-link" target="_blank" rel="noopener"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;&#10;&lt;h2 id="build-the-node-api"&gt;Build the Node API &lt;a class="heading-anchor" href="#build-the-node-api" aria-label="Link to Build the Node API"&gt;#&lt;/a&gt;&lt;/h2&gt;&#10;&lt;p&gt;First create a &lt;code&gt;package.json&lt;/code&gt; file for our dependencies and add express to the app:&lt;/p&gt;</description></item><item><title>Pithy Guide to Kubernetes Part 1</title><link>https://jdx.dev/posts/2019-12-06-pithy-guide-to-kubernetes-part-1/</link><pubDate>Fri, 06 Dec 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-12-06-pithy-guide-to-kubernetes-part-1/</guid><description>&lt;p&gt;This is a concise, opinionated guide to Kubernetes. I&amp;rsquo;m assuming you already know how to build&#10;software and are just looking to get started with Kubernetes. This is a 2-parter: the first is&#10;language-agnostic and the second is for a particular language. I have Go and Node right now.&lt;/p&gt;&#10;&lt;p&gt;Assume we want to build a JSON web server managed by Kubernetes. At the end of this guide, we will&#10;be able to do this:&lt;/p&gt;</description></item><item><title>[video] oclifconf: The Future of oclif</title><link>https://jdx.dev/posts/2019-08-22-the-future-of-oclif/</link><pubDate>Thu, 22 Aug 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-08-22-the-future-of-oclif/</guid><description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=1TKh2YBxRMY" class="external-link" target="_blank" rel="noopener"&gt;https://www.youtube.com/watch?v=1TKh2YBxRMY&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Golang Function Driven Tests</title><link>https://jdx.dev/posts/2019-05-09-go-function-driven-tests/</link><pubDate>Thu, 09 May 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-05-09-go-function-driven-tests/</guid><description>&lt;p&gt;&lt;a href="https://dave.cheney.net/2019/05/07/prefer-table-driven-tests" class="external-link" target="_blank" rel="noopener"&gt;Dave Cheney had a fantastic post on improving test structure in Go code with table-driven tests.&lt;/a&gt; I want to expand on it just a tiny bit to show how table-driven tests can be made more concise with functions.&lt;/p&gt;&#10;&lt;p&gt;If you haven&amp;rsquo;t read his article, read that before continuing.&lt;/p&gt;&#10;&lt;p&gt;The end result of Dave&amp;rsquo;s post is the following example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#8be9fd;font-style:italic"&gt;func&lt;/span&gt; &lt;span style="color:#50fa7b"&gt;TestSplit&lt;/span&gt;(t &lt;span style="color:#ff79c6"&gt;*&lt;/span&gt;testing.T) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tests &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; &lt;span style="color:#8be9fd;font-style:italic"&gt;map&lt;/span&gt;[&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;]&lt;span style="color:#8be9fd;font-style:italic"&gt;struct&lt;/span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; input &lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; sep &lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; want []&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;simple&amp;#34;&lt;/span&gt;: {input: &lt;span style="color:#f1fa8c"&gt;&amp;#34;a/b/c&amp;#34;&lt;/span&gt;, sep: &lt;span style="color:#f1fa8c"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, want: []&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;{&lt;span style="color:#f1fa8c"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;, &lt;span style="color:#f1fa8c"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;, &lt;span style="color:#f1fa8c"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;wrong sep&amp;#34;&lt;/span&gt;: {input: &lt;span style="color:#f1fa8c"&gt;&amp;#34;a/b/c&amp;#34;&lt;/span&gt;, sep: &lt;span style="color:#f1fa8c"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;, want: []&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;{&lt;span style="color:#f1fa8c"&gt;&amp;#34;a/b/c&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;no sep&amp;#34;&lt;/span&gt;: {input: &lt;span style="color:#f1fa8c"&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;, sep: &lt;span style="color:#f1fa8c"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, want: []&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;{&lt;span style="color:#f1fa8c"&gt;&amp;#34;abc&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f1fa8c"&gt;&amp;#34;trailing sep&amp;#34;&lt;/span&gt;: {input: &lt;span style="color:#f1fa8c"&gt;&amp;#34;a/b/c/&amp;#34;&lt;/span&gt;, sep: &lt;span style="color:#f1fa8c"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;, want: []&lt;span style="color:#8be9fd"&gt;string&lt;/span&gt;{&lt;span style="color:#f1fa8c"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;, &lt;span style="color:#f1fa8c"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;, &lt;span style="color:#f1fa8c"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;}},&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;for&lt;/span&gt; name, tc &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; &lt;span style="color:#ff79c6"&gt;range&lt;/span&gt; tests {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; got &lt;span style="color:#ff79c6"&gt;:=&lt;/span&gt; &lt;span style="color:#50fa7b"&gt;Split&lt;/span&gt;(tc.input, tc.sep)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ff79c6"&gt;if&lt;/span&gt; !reflect.&lt;span style="color:#50fa7b"&gt;DeepEqual&lt;/span&gt;(tc.want, got) {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; t.&lt;span style="color:#50fa7b"&gt;Fatalf&lt;/span&gt;(&lt;span style="color:#f1fa8c"&gt;&amp;#34;%s: expected: %v, got: %v&amp;#34;&lt;/span&gt;, name, tc.want, got)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a nice way to reduce code duplication, but closing over a new function we can go one step further:&lt;/p&gt;</description></item><item><title>[podcast] codeish: oclif: An Open Source CLI Framework</title><link>https://jdx.dev/posts/2019-05-07-oclif-an-open-source-cli-framework/</link><pubDate>Tue, 07 May 2019 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2019-05-07-oclif-an-open-source-cli-framework/</guid><description>&lt;p&gt;&lt;a href="https://www.heroku.com/podcasts/codeish/13-oclif-an-open-source-cli-framework" class="external-link" target="_blank" rel="noopener"&gt;https://www.heroku.com/podcasts/codeish/13-oclif-an-open-source-cli-framework&lt;/a&gt;&lt;/p&gt;</description></item><item><title>[video] NodeConf: Building Great CLI Experiences in Node.js</title><link>https://jdx.dev/posts/2018-10-17-nodeconf-building-great-cli-experiences-in-node/</link><pubDate>Thu, 18 Oct 2018 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2018-10-17-nodeconf-building-great-cli-experiences-in-node/</guid><description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Izx3-KSuaM8" class="external-link" target="_blank" rel="noopener"&gt;https://www.youtube.com/watch?v=Izx3-KSuaM8&lt;/a&gt;&lt;/p&gt;</description></item><item><title>12 Factor CLI Apps</title><link>https://jdx.dev/posts/2018-10-08-12-factor-cli-apps/</link><pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2018-10-08-12-factor-cli-apps/</guid><description>&lt;p&gt;&lt;a href="https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46" class="external-link" target="_blank" rel="noopener"&gt;https://medium.com/@jdxcode/12-factor-cli-apps-dd3c227a0e46&lt;/a&gt;&lt;/p&gt;</description></item><item><title>[video] DevRelCon: Designing a delightful command line interface</title><link>https://jdx.dev/posts/2018-07-16-devrelcon-designing-a-delightful-command-line-interface/</link><pubDate>Mon, 16 Jul 2018 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2018-07-16-devrelcon-designing-a-delightful-command-line-interface/</guid><description>&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=Izx3-KSuaM8" class="external-link" target="_blank" rel="noopener"&gt;https://www.youtube.com/watch?v=Izx3-KSuaM8&lt;/a&gt;&lt;/p&gt;</description></item><item><title>For the love of god, don’t use .npmignore</title><link>https://jdx.dev/posts/2018-05-25-for-the-love-of-god-dont-use-npmignore/</link><pubDate>Fri, 25 May 2018 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2018-05-25-for-the-love-of-god-dont-use-npmignore/</guid><description>&lt;p&gt;&lt;a href="https://medium.com/@jdxcode/for-the-love-of-god-dont-use-npmignore-f93c08909d8d" class="external-link" target="_blank" rel="noopener"&gt;https://medium.com/@jdxcode/for-the-love-of-god-dont-use-npmignore-f93c08909d8d&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Salesforce Blog: Open Sourcing oclif, the CLI Framework that Powers Our CLIs</title><link>https://jdx.dev/posts/2018-03-20-open-sourcing-oclif-the-cli-framework-that-powers-our-clis/</link><pubDate>Tue, 20 Mar 2018 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2018-03-20-open-sourcing-oclif-the-cli-framework-that-powers-our-clis/</guid><description>&lt;p&gt;&lt;a href="https://engineering.salesforce.com/open-sourcing-oclif-the-cli-framework-that-powers-our-clis-21fbda99d33a" class="external-link" target="_blank" rel="noopener"&gt;https://engineering.salesforce.com/open-sourcing-oclif-the-cli-framework-that-powers-our-clis-21fbda99d33a&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Heroku Blog: Evolution of the Heroku CLI: 2008-2017</title><link>https://jdx.dev/posts/2017-08-15-evolution-of-the-heroku-cli/</link><pubDate>Tue, 15 Aug 2017 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2017-08-15-evolution-of-the-heroku-cli/</guid><description>&lt;p&gt;&lt;a href="https://blog.heroku.com/evolution-of-heroku-cli-2008-2017" class="external-link" target="_blank" rel="noopener"&gt;https://blog.heroku.com/evolution-of-heroku-cli-2008-2017&lt;/a&gt;&lt;/p&gt;</description></item><item><title>How to Pair Program</title><link>https://jdx.dev/posts/2015-02-03-how-to-pair-program/</link><pubDate>Tue, 03 Feb 2015 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2015-02-03-how-to-pair-program/</guid><description>&lt;p&gt;&lt;a href="https://medium.com/@jdxcode/how-to-pair-program-d6741077e513" class="external-link" target="_blank" rel="noopener"&gt;https://medium.com/@jdxcode/how-to-pair-program-d6741077e513&lt;/a&gt;&lt;/p&gt;</description></item><item><title>[book] Write Modern Web Apps with the MEAN Stack</title><link>https://jdx.dev/posts/2014-10-05-write-modern-webapps-with-the-mean-stack/</link><pubDate>Sun, 05 Oct 2014 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2014-10-05-write-modern-webapps-with-the-mean-stack/</guid><description>&lt;p&gt;&lt;a href="https://www.amazon.com/Write-Modern-Apps-MEAN-Stack/dp/0133930157" class="external-link" target="_blank" rel="noopener"&gt;https://www.amazon.com/Write-Modern-Apps-MEAN-Stack/dp/0133930157&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Best Practices for Building Angular.JS Apps</title><link>https://jdx.dev/posts/2014-07-09-best-practices-for-building-angular-apps/</link><pubDate>Wed, 09 Jul 2014 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2014-07-09-best-practices-for-building-angular-apps/</guid><description>&lt;p&gt;&lt;a href="https://medium.com/@jdxcode/best-practices-for-building-angular-js-apps-266c1a4a6917" class="external-link" target="_blank" rel="noopener"&gt;https://medium.com/@jdxcode/best-practices-for-building-angular-js-apps-266c1a4a6917&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Older Posts</title><link>https://jdx.dev/posts/2014-01-01-older_posts/</link><pubDate>Wed, 01 Jan 2014 00:00:00 +0000</pubDate><guid>https://jdx.dev/posts/2014-01-01-older_posts/</guid><description>&lt;p&gt;You can view my older posts on medium: &lt;a href="https://medium.com/@jdxcode" class="external-link" target="_blank" rel="noopener"&gt;https://medium.com/@jdxcode&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>