<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>Kaushik Gopal's Website</title><link>https://kau.sh/</link><description>Recent content - Kaushik Gopal's Website</description><language>en-us</language><lastBuildDate>Fri, 20 Feb 2026 21:04:46 GMT</lastBuildDate><image><url>https://kau.sh///images/kg.jpeg</url><title>Kaushik Gopal's Website</title><link>https://kau.sh/</link></image><atom:link href="https://kau.sh/feed.xml" rel="self" type="application/rss+xml"/><item><title>
Agentic Fluidity - OpenCode is OpenClaw for coding</title><link>https://kau.sh/blog/opencode-openclaw/</link><description>
&lt;p&gt;One of the reasons &lt;a href="https://openclaw.ai/"&gt;OpenClaw&lt;/a&gt; got so popular was how
fluidly you can chat with and operate your agents. Pull up your phone, send a
quick message on WhatsApp, and you&amp;rsquo;re in business.&lt;/p&gt;
&lt;p&gt;As we focus more on agent orchestration&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; in 2026, I think an important
aspect will be &lt;strong&gt;access fluidity&lt;/strong&gt;. How do you hop into your agent&amp;rsquo;s context
from any device, terminal, or IDE and just start coding?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://code.claude.com/docs/en/claude-code-on-the-web#moving-tasks-between-web-and-terminal"&gt;Claude Code&lt;/a&gt;
supports this in a limited way, while others like
&lt;a href="https://cursor.com/blog/agent-web"&gt;Cursor&lt;/a&gt; and Codex take a cloud-based
approach.&lt;/p&gt;
&lt;p&gt;The best option I&amp;rsquo;ve found for this &amp;ldquo;on-the-go&amp;rdquo; agentic coding is an open-source
one — OpenCode.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://opencode.ai/"&gt;OpenCode&lt;/a&gt; - your best &amp;ldquo;on-the-go&amp;rdquo; option for agentic
coding.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="server-client-architecture"&gt;
Server-client architecture
&lt;a class="heading-anchor" href="#server-client-architecture" aria-label="Link to Server-client architecture"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;OpenCode uses a native server-client architecture. You can simply spin it up in
a regular terminal tab, just like &lt;code&gt;claude&lt;/code&gt; or &lt;code&gt;codex&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But the power move is running it as a
&lt;a href="https://opencode.ai/docs/server/#how-it-works"&gt;server&lt;/a&gt; and connecting multiple
clients.&lt;/p&gt;
&lt;p&gt;A client can be your terminal tab, a mobile device, or a desktop computer. Each
terminal tab becomes a new, isolated CLI session that connects to the server.&lt;/p&gt;
&lt;p&gt;Couple this with &lt;a href="https://tailscale.com/"&gt;Tailscale&lt;/a&gt;, and you can securely
connect to a &lt;a href="https://kau.sh/blog/mac-mini-tailscale-benefits-tips-vpn-vps/"&gt;dev machine&lt;/a&gt;
running an OpenCode server from anywhere.&lt;/p&gt;
&lt;h2 id="getting-started"&gt;
Getting started
&lt;a class="heading-anchor" href="#getting-started" aria-label="Link to Getting started"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d start by using &lt;code&gt;opencode&lt;/code&gt; like a regular CLI tool. Once it feels familiar,
switch to server/web mode.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# advertises server as opencode.local&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opencode web --mdns
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# attach to a session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# equivalent to starting a new claude code session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;opencode attach http://opencode.local:4096 --dir /path/to/project
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The beauty is you can open that &lt;code&gt;opencode.local&lt;/code&gt; URL in any browser, and it&amp;rsquo;s
fully synced.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Credit to my &lt;a href="https://fragmentedpodcast.com/about/#iury-souza"&gt;co-host&lt;/a&gt;
&lt;a href="https://x.com/IurySza/status/2018362469035483318"&gt;Iury&lt;/a&gt; for tooting the
OpenCode horn early, and my Instacart colleague
&lt;a href="https://x.com/spencerschack"&gt;Spencer&lt;/a&gt; for questioning my luddite tmux
ways.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll write a future post singing OpenCode&amp;rsquo;s other praises. For now, if you&amp;rsquo;re
exploring the bleeding edge of agent access fluidity, don&amp;rsquo;t sleep on it.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;See my post on &lt;a href="https://kau.sh/blog/ai-programming/"&gt;AI paradigms&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I noticed some
&lt;a href="https://github.com/anomalyco/opencode/issues/11225"&gt;memory leaks&lt;/a&gt; when
using tmux sessions with OpenCode, and Spencer asked me: why not lean on the
server-client model more and use regular Ghostty tabs and splits.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/opencode-openclaw/</guid><pubDate>Fri, 20 Feb 2026 21:04:46 GMT</pubDate></item><item><title>
AI model choices 2026-01</title><link>https://kau.sh/blog/ai-model-choices/</link><description>
&lt;p&gt;Which AI model do I use?&lt;/p&gt;
&lt;p&gt;This is a common question I get asked, but models evolve so rapidly that I never
felt like I could give an answer that would stay relevant for more than a month
or two.&lt;/p&gt;
&lt;p&gt;This year, I finally feel like I have a stable set of model choices that
consistently give me good results. I&amp;rsquo;m jotting it down here to share more
broadly and to trace how my own choices evolve over time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GPT 5.2&lt;/strong&gt; (High) for planning and writing, including
&lt;a href="https://kau.sh/blog/exec-plans/"&gt;plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Opus 4.5&lt;/strong&gt; for anything coding, task automation, and tool calling&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini&lt;/strong&gt;&amp;rsquo;s range of models for everything else:
&lt;ul&gt;
&lt;li&gt;Gemini 3 (Thinking) for learning and understanding concepts (underrated)&lt;/li&gt;
&lt;li&gt;Gemini 3 (Flash) for quick fire questions&lt;/li&gt;
&lt;li&gt;Nano Banana (obv) for image generation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;NVIDIA&amp;rsquo;s &lt;strong&gt;Parakeet&lt;/strong&gt; for voice transcription&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/ai-model-choices/</guid><pubDate>Tue, 13 Jan 2026 19:39:48 GMT</pubDate></item><item><title>
Forking subagents in an AI coding session with tmux</title><link>https://kau.sh/blog/agent-forking/</link><description>
&lt;p&gt;With agentic coding becoming the
&lt;a href="https://kau.sh/blog/ai-programming/#iii--agentic-coding"&gt;primary paradigm&lt;/a&gt; for coding, many
have tried to come up with a smooth subagent workflow.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Many of these solutions are reasonable, but none match the simplicity of what I
actually want:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Spin up another agent instance with the exact same context I&amp;rsquo;ve painstakingly
built.&lt;/li&gt;
&lt;li&gt;Pursue a tangential thought &lt;em&gt;interactively&lt;/em&gt; in a separate session.&lt;/li&gt;
&lt;li&gt;Sometimes that&amp;rsquo;s exploratory (understand a subsystem, ask follow-ups).
Sometimes it&amp;rsquo;s parallel work (write tests while context is fresh, draft docs,
spike an alternative).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it. Open a new tab, resume the session, take a different path.&lt;/p&gt;
&lt;p&gt;Sounds straightforward, but tack on a few more requirements and it becomes hard
to find a satisfying solution. I’ve been using a thin shell script for this and
it&amp;rsquo;s worked well.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;You can find the
&lt;a href="https://gist.github.com/kaushikgopal/3a67f71052cf10276315162012dcac1c"&gt;source&lt;/a&gt;
here.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="agent-forking.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h1 id="my-requirements"&gt;
My requirements
&lt;a class="heading-anchor" href="#my-requirements" aria-label="Link to My requirements"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="super-thin-glue-layer"&gt;
Super thin glue layer
&lt;a class="heading-anchor" href="#super-thin-glue-layer" aria-label="Link to Super thin glue layer"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m deliberately &lt;strong&gt;not trying to build on top of existing agents&lt;/strong&gt;. I use claude
code, codex cli &amp;amp; gemini daily, and they change fast enough that anything with a
thick layer (like a UI) will lag behind on features.&lt;/p&gt;
&lt;p&gt;So: a Bash script and tmux. That&amp;rsquo;s it. Available on virtually any computer.&lt;/p&gt;
&lt;h2 id="tool-agnostic-forks"&gt;
Tool-agnostic forks
&lt;a class="heading-anchor" href="#tool-agnostic-forks" aria-label="Link to Tool-agnostic forks"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I want to start the main session in one tool and fork into another, keeping the
same context.&lt;/p&gt;
&lt;p&gt;So I might start a planning session with codex. After I have a decent plan, I
might want to fork into claude code (with all the context I&amp;rsquo;ve built) and start
a coding session. I might want to fork another subagent from gemini and, using
something like the nanobanana MCP, build a before/after flow diagram.&lt;/p&gt;
&lt;h2 id="interactive-not-one-shot"&gt;
Interactive, not one-shot
&lt;a class="heading-anchor" href="#interactive-not-one-shot" aria-label="Link to Interactive, not one-shot"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;When I fork a subagent, I want a real session I can keep interacting with. It&amp;rsquo;s
rare that I can one-shot a request and get exactly what I want. Based on the
initial response, I might want to go down the rabbit hole and explore more.&lt;/p&gt;
&lt;p&gt;Many existing solutions are headless or try to merge results back automatically.
In practice, I just copy-paste what I need from the fork back into the main
session — tmux makes this trivial.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;An early version of my script had something similar: the ability to manually
“pull” in results from the subagents. In practice, I rarely used it that way. I
typically copy-paste relevant lines from the agent response and paste them back
into the main session. With tmux, if I highlight text with my mouse, it&amp;rsquo;s
automatically copied to my clipboard. Removing this feature made the script
simpler and more robust.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="no-context-bloat"&gt;
No context bloat
&lt;a class="heading-anchor" href="#no-context-bloat" aria-label="Link to No context bloat"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The whole point of forking is keeping tangents out of the main session. I want
the main session to stay focused on the “primary” change, while side quests
happen elsewhere.&lt;/p&gt;
&lt;p&gt;But I also want the context going into the forked subagent to be controlled. To
that end, the script pulls in the text from the original window (courtesy of
tmux) and checks the size. If the transcript is long, I summarize it before
feeding it into the subagent.&lt;/p&gt;
&lt;p&gt;In this way, the script keeps tangential context out of the main session while
still seeding useful context to the subagent.&lt;/p&gt;
&lt;h2 id="no-personas"&gt;
No personas
&lt;a class="heading-anchor" href="#no-personas" aria-label="Link to No personas"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Claude and other tools frame subagents around personas. I don&amp;rsquo;t find them as
useful.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get me wrong, personas in prompts are powerful and can give good results.
But I don&amp;rsquo;t need a subagent container for that. I find myself crafting specific
agent skills and seeding those into the prompt. This gives me equally good
results.&lt;/p&gt;
&lt;h2 id="label-the-subagents"&gt;
Label the subagents!
&lt;a class="heading-anchor" href="#label-the-subagents" aria-label="Link to Label the subagents!"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Perhaps the trickiest part of having multiple agents working in parallel is
keeping track of what each agent is doing.&lt;/p&gt;
&lt;p&gt;My current solution to this is rather trivial. Every time an agent is spawned, I
label the tmux window with something that will help me remember. It works rather
well!&lt;/p&gt;
&lt;h1 id="the-solution"&gt;
The solution
&lt;a class="heading-anchor" href="#the-solution" aria-label="Link to The solution"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="agent-forking.webm" type="video/webm"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;Feel free to take a look at the Bash
&lt;a href="https://gist.github.com/kaushikgopal/3a67f71052cf10276315162012dcac1c"&gt;script here&lt;/a&gt;.
You can also see my
&lt;a href="https://gist.github.com/kaushikgopal/2327b09ae1a194704d62da48d135dd35"&gt;tmux.conf&lt;/a&gt; but
there&amp;rsquo;s nothing special in there, and the subagent script should work with most
configurations.&lt;/p&gt;
&lt;p&gt;The script is conceptually pretty simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Start a coding session in tmux (as you regularly do).&lt;/li&gt;
&lt;li&gt;Treat the current tmux pane as the main agent or session.&lt;/li&gt;
&lt;li&gt;When you want to spawn a subagent, capture the transcript lines using tmux
(configurable).&lt;/li&gt;
&lt;li&gt;Prompt for the task you want to perform.&lt;/li&gt;
&lt;li&gt;Spawn a new tmux window in the background with the chosen agent command, then
paste in a payload:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;context&amp;gt; ... &amp;lt;/context&amp;gt;&lt;/code&gt; (raw transcript, or an optional summary created
with possibly a different agent)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;task&amp;gt; ... &amp;lt;/task&amp;gt;&lt;/code&gt; (the prompt you typed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="caveats"&gt;
Caveats
&lt;a class="heading-anchor" href="#caveats" aria-label="Link to Caveats"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;ve been tweaking this script for my own setup (Ghostty terminal + fish
shell + tmux). I haven&amp;rsquo;t polished or tested it for any setup other than mine,
but it should be straightforward to adapt.&lt;/li&gt;
&lt;li&gt;It’s easy to spawn too many forks. The marginal value drops quickly once
you’re context-switching more than you’re building.&lt;/li&gt;
&lt;li&gt;Summarization is optional, but if you use it, you’re trusting a model to
compress your transcript. If accuracy matters, keep the threshold low enough
that the raw context fits.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Here are some of the most compelling ones I&amp;rsquo;ve found:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://block.github.io/goose/docs/guides/subagents/"&gt;Goose&lt;/a&gt; has a way of
doing it, but Goose itself is a new wrapper tool, around the underlying
tools that we actually use (claude code, codex cli, gemini-cli). Which
means it will always lag behind on features.&lt;/li&gt;
&lt;li&gt;Claude itself has &lt;a href="https://code.claude.com/docs/en/sub-agents"&gt;subagents&lt;/a&gt;
but it&amp;rsquo;s a very different take on subagents, focusing more on the
multi-persona approach.&lt;/li&gt;
&lt;li&gt;Amazon has a sophisticated solution called
&lt;a href="https://github.com/awslabs/agents-for-amazon-bedrock-blueprints"&gt;CAO&lt;/a&gt;
(CLI Agent Orchestrator) which feels like the closest but it is far more
complex than necessary, with MCPs, etc.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.conductor.build/"&gt;Conductor&lt;/a&gt; is a free tool that also looks
really nice, but like Goose it sits on top of the agents and focuses on
independent workspaces.&lt;/li&gt;
&lt;/ul&gt;
&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I&amp;rsquo;ve heavily tweaked this script and it works great on my current Ghostty
terminal + fish shell + tmux setup. I haven&amp;rsquo;t tried to polish it for other
shells, and I don&amp;rsquo;t intend to, so feel free to copy, fork, and modify it to
your heart&amp;rsquo;s content!&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/agent-forking/</guid><pubDate>Mon, 29 Dec 2025 08:00:00 GMT</pubDate></item><item><title>
Wi-Fi sharing is a killer Android feature</title><link>https://kau.sh/blog/wifi-sharing-android/</link><description>
&lt;p&gt;Ubiquiti announced a new
&lt;a href="https://blog.ui.com/article/travel-in-style-unifi-style-unifi-travel-router"&gt;travel router&lt;/a&gt;.
Much of the internet is excited. So am I.&lt;/p&gt;
&lt;p&gt;Then I tried to remember the last time I actually &lt;em&gt;needed&lt;/em&gt; a travel router. You
see, Android has supported a feature I&amp;rsquo;ll call Wi-Fi sharing for
years.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; Your phone connects to an existing Wi-Fi network and
re-shares it as a hotspot.&lt;/p&gt;
&lt;p&gt;This might sound like a regular hotspot feature that most phones (including the
iPhone) come with. But it&amp;rsquo;s not. iPhones can share mobile data. They can&amp;rsquo;t
re-share a Wi-Fi connection as a hotspot.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Wi-Fi sharing
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Your phone connects to Wi-Fi, and then re-shares that same Wi-Fi as a hotspot.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;This is different from typical hotspot functionality where the phone shares
its mobile data connection (vs Wi-Fi).&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Neat trick, but why bother? Can&amp;rsquo;t you just connect each device to Wi-Fi?&lt;/p&gt;
&lt;h2 id="avoid-signing-every-device-into-a-captive-wi-fi-portal"&gt;
Avoid signing every device into a captive Wi-Fi portal
&lt;a class="heading-anchor" href="#avoid-signing-every-device-into-a-captive-wi-fi-portal" aria-label="Link to Avoid signing every device into a captive Wi-Fi portal"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Captive portals are annoying when you&amp;rsquo;re carrying multiple devices. I typically
travel with 3-4 devices that want internet. Signing each one in, every time,
gets old fast.&lt;/p&gt;
&lt;p&gt;Some devices are worse: Chromecast and Fire TV sticks are particularly painful
to get past captive portals. If everything connects to your hotspot, you only
deal with the portal once.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="work-around-one-device-at-a-time-wi-fi-plans"&gt;
Work around &amp;ldquo;one device at a time&amp;rdquo; Wi-Fi plans
&lt;a class="heading-anchor" href="#work-around-one-device-at-a-time-wi-fi-plans" aria-label="Link to Work around “one device at a time” Wi-Fi plans"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;On a plane, I sometimes want both my laptop and phone online. Some paid Wi-Fi
plans only allow one device at a time. Unless you&amp;rsquo;re ok paying twice, Wi-Fi
sharing is simpler.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Hotels and conference centers do the same: sign-in plus device limits. Wi-Fi
sharing works around it.&lt;/p&gt;
&lt;h2 id="fix-devices-cant-see-each-other-networking"&gt;
Fix &amp;ldquo;devices can&amp;rsquo;t see each other&amp;rdquo; networking
&lt;a class="heading-anchor" href="#fix-devices-cant-see-each-other-networking" aria-label="Link to Fix “devices can’t see each other” networking"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This one is less obvious, but common in hotels and conference Wi-Fi: your
devices have internet, but they can&amp;rsquo;t see each other locally. Chromecast (or
printers) won&amp;rsquo;t show up as a cast target because it doesn&amp;rsquo;t appear on the
network.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s usually client/AP isolation.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; Put your devices on your phone&amp;rsquo;s
hotspot, and local discovery usually works again.&lt;/p&gt;
&lt;h2 id="secure-networking-with-a-tailscale-setup"&gt;
Secure networking with a Tailscale setup
&lt;a class="heading-anchor" href="#secure-networking-with-a-tailscale-setup" aria-label="Link to Secure networking with a Tailscale setup"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is slightly advanced. With a Tailscale setup and an explicit exit node, you
basically have a private VPN.&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; On phones where hotspot traffic
routes through that VPN, you only have to set it up on your Android phone, and
every device that connects to your phone gets the same &amp;ldquo;safe&amp;rdquo; path out.&lt;/p&gt;
&lt;p&gt;If I have to log in to bank accounts when roaming or connecting to &amp;ldquo;free&amp;rdquo; Wi-Fi,
this helps me feel safer knowing the local network can&amp;rsquo;t see or tamper with the
contents of my traffic.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="how-to-enable-wi-fi-sharing"&gt;
How to enable Wi-Fi sharing
&lt;a class="heading-anchor" href="#how-to-enable-wi-fi-sharing" aria-label="Link to How to enable Wi-Fi sharing"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;Connect your Android phone to the Wi-Fi network you want to share. If it&amp;rsquo;s
behind a captive portal, sign in as needed.&lt;/li&gt;
&lt;li&gt;Go to Settings → Hotspot &amp;amp; tethering → Wi-Fi hotspot (wording varies) and
turn it on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Typically, if your phone does not support Wi-Fi sharing, it will disable Wi-Fi.
Some OEMs show a separate toggle to enable Wi-Fi sharing. On Pixel phones, it&amp;rsquo;s
automatic.&lt;/p&gt;
&lt;h1 id="almost-every-android-phone"&gt;
&lt;em&gt;Almost&lt;/em&gt; every Android phone
&lt;a class="heading-anchor" href="#almost-every-android-phone" aria-label="Link to Almost every Android phone"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I should pause my gloating over iPhones for a second: a &lt;em&gt;few&lt;/em&gt; Android devices
may not support this feature.&lt;/p&gt;
&lt;p&gt;The Android OS has Wi-Fi sharing baked in, but it still requires hardware +
driver support. Notable exceptions include the Pixel 7a, the Pixel 8a, and yes
the (first generation) Pixel Fold.&lt;/p&gt;
&lt;h2 id="technical-details"&gt;
Technical Details
&lt;a class="heading-anchor" href="#technical-details" aria-label="Link to Technical Details"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Wi-Fi sharing requires Wi-Fi hardware (chipset + drivers) that can run as both a
client and an access point at the same time (STA + AP).&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt; Chipsets can
implement this in a few ways (DBS, SBS, MCC, SCC).&lt;sup id="fnref:8"&gt;&lt;a href="#fn:8" class="footnote-ref" role="doc-noteref"&gt;8&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Android doesn&amp;rsquo;t mandate one mode; it depends on the Wi-Fi chipset. DBS/SBS use
multiple radios, so the phone can keep the upstream connection and hotspot truly
simultaneous (for example, 5 GHz upstream and a 2.4 GHz hotspot). MCC/SCC share
a radio, so the hotspot either stays on the same channel (SCC) or the radio hops
channels (MCC).&lt;/p&gt;
&lt;p&gt;If a phone can&amp;rsquo;t do STA + AP concurrency well (or at all), OEMs disable Wi-Fi
sharing (which is why some phones and many older devices don&amp;rsquo;t support it).&lt;/p&gt;
&lt;p&gt;Travel routers still have their place: Ethernet ports, better radios, and an
always-on box you can run a VPN on. But if you&amp;rsquo;re on Android and your phone
supports Wi-Fi sharing, you already have the core trick.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Android doesn&amp;rsquo;t call it this in Settings, but it&amp;rsquo;s the best term I have for
&amp;ldquo;connect to Wi-Fi, then share that Wi-Fi as a hotspot&amp;rdquo;. In strict networking
terms, this isn&amp;rsquo;t L2 bridging; it&amp;rsquo;s typically tethering (routing/NAT) with a
Wi-Fi upstream.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;This works because the captive portal only sees your phone; everything else
is NATed behind it.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Thank you Delta for being one of the few US domestic airlines that don&amp;rsquo;t
place this restriction. Looking at you United.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Hotel and conference Wi-Fi often blocks device-to-device traffic on purpose
(&amp;ldquo;client isolation&amp;rdquo;) so guests can&amp;rsquo;t discover, scan, or connect to each
other&amp;rsquo;s devices. Your phone&amp;rsquo;s hotspot creates a separate little LAN, so your
devices can talk to each other again.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I have a post in the making about this: &amp;ldquo;With Tailscale you don&amp;rsquo;t need to
pay for a VPN&amp;rdquo;.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;HTTPS encrypts the bank session, but open Wi-Fi is still untrusted: a
malicious access point can tamper with DNS and try to steer you into
phishing. A VPN (or Tailscale exit node) reduces the surface area by
encrypting your traffic to a trusted endpoint.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;Modern devices support AP (Access Point) + STA (Station) Mode, letting them
act as both a client to one network and a hotspot for others, allowing Wi-Fi
extension or tethering.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;Definitions from Android’s Wi-Fi vendor HAL (&lt;code&gt;WifiNative.java&lt;/code&gt;):
&lt;a href="https://android.googlesource.com/platform/packages/modules/Wifi/&amp;#43;/refs/heads/main/service/java/com/android/server/wifi/WifiNative.java#3454"&gt;DBS&lt;/a&gt;
(Dual Band Simultaneous),
&lt;a href="https://android.googlesource.com/platform/packages/modules/Wifi/&amp;#43;/refs/heads/main/service/java/com/android/server/wifi/WifiNative.java#3445"&gt;SBS&lt;/a&gt;
(Single Band Simultaneous),
&lt;a href="https://android.googlesource.com/platform/packages/modules/Wifi/&amp;#43;/refs/heads/main/service/java/com/android/server/wifi/WifiNative.java#3427"&gt;MCC&lt;/a&gt;
(Multi Channel Concurrency),
&lt;a href="https://android.googlesource.com/platform/packages/modules/Wifi/&amp;#43;/refs/heads/main/service/java/com/android/server/wifi/WifiNative.java#3436"&gt;SCC&lt;/a&gt;
(Single Channel Concurrency).&amp;#160;&lt;a href="#fnref:8" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/wifi-sharing-android/</guid><pubDate>Fri, 26 Dec 2025 20:30:06 GMT</pubDate></item><item><title>
AI is a motorbike for the mind</title><link>https://kau.sh/blog/motorbike-for-the-mind/</link><description>
&lt;p&gt;Steve Jobs famously called the computer a
&lt;a href="https://youtu.be/NwccnuHz5fM?si=LZjHwYanUIhi9JWd"&gt;bicycle for the mind&lt;/a&gt;.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="quote" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;We humans are tool builders&amp;hellip; We can fashion tools that amplify these
inherent abilities to spectacular magnitudes. So for me, a computer has always
been the bicycle of the mind. Something that takes us far beyond our inherent
abilities.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;That sentiment feels prescient again today but the vehicle has changed.&lt;/p&gt;
&lt;p&gt;If the personal computer was a bicycle, &lt;strong&gt;AI is a motorbike for the mind&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I choose this distinction carefully.&lt;/p&gt;
&lt;p&gt;To ride a bicycle well, you must build muscle. The motorbike is different. You
cover vast distances without any tax on your body. Ride it long enough, and the
muscles you once relied on atrophy. It&amp;rsquo;s seductive to ship words or code while
skipping the slow, painful work. But wrestling with edge cases is how you learn
what &amp;ldquo;good&amp;rdquo; looks like: a skill you&amp;rsquo;ll need to sharpen even more with AI doing
the typing.&lt;/p&gt;
&lt;p&gt;Speed also changes the nature of failure. A fall at 10 mph is a bruise: a bug
you can trace and learn from. A fall at 60 mph is fatal: a bug hidden in code
you never wrote, its damage everywhere before you notice. Keep vibe-coding your
entire system&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; without understanding every line, every trade-off;
you&amp;rsquo;ll eventually crash.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the thing about motorbikes: everyone can twist the throttle. What
separates riders who arrive from those who crash is knowing when to brake – when
to stop, look, and understand before you ship. In an age where anyone can go
fast, the true skill is no longer the throttle. It&amp;rsquo;s the brake.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/motorbike-mind.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;as &lt;a href="https://kau.sh/blog/vibe-eng"&gt;opposed to engineering with AI&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/motorbike-for-the-mind/</guid><pubDate>Tue, 23 Dec 2025 06:01:28 GMT</pubDate></item><item><title>
Combating AI coding atrophy with Rust</title><link>https://kau.sh/blog/learn-rust-ai-atrophy/</link><description>
&lt;p&gt;It&amp;rsquo;s no secret that I&amp;rsquo;ve fully &lt;a href="https://kau.sh/tags/ai/"&gt;embraced&lt;/a&gt; AI for my coding. A valid
concern (&lt;em&gt;and one I&amp;rsquo;ve been thinking about deeply&lt;/em&gt;) is the atrophying of the
part of my brain that helps me code.&lt;/p&gt;
&lt;p&gt;To push back on that, I&amp;rsquo;ve been learning &lt;a href="https://rust-lang.org/"&gt;Rust&lt;/a&gt; on the
side for the last few months. I am absolutely loving it.&lt;/p&gt;
&lt;h2 id="why-rust"&gt;
Why Rust?
&lt;a class="heading-anchor" href="#why-rust" aria-label="Link to Why Rust?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="systems-level-language"&gt;
Systems level language
&lt;a class="heading-anchor" href="#systems-level-language" aria-label="Link to Systems level language"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Kotlin remains my go-to language. It&amp;rsquo;s the language I know like the back of my
hand. If someone sends me a swath of Kotlin code, whether handwritten or AI
generated, I can quickly grok it and form a strong opinion on how to improve it.&lt;/p&gt;
&lt;p&gt;But Kotlin is a high-level language that runs on a JVM. There are structural
limits to the performance you can eke out of it, and for most of my career&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;
I&amp;rsquo;ve worked with garbage-collected languages.&lt;/p&gt;
&lt;p&gt;For a change, I wanted a systems-level language, one without the training wheels
of a garbage collector.&lt;/p&gt;
&lt;h3 id="new-paradigms"&gt;
New paradigms
&lt;a class="heading-anchor" href="#new-paradigms" aria-label="Link to New paradigms"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I also wanted a language with a different core philosophy, something that would
force me to think in new ways.&lt;/p&gt;
&lt;p&gt;I picked up Go casually but it didn&amp;rsquo;t feel like a big enough departure from the
languages I already knew.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="quote" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;It just felt more useful to ask AI to generate Go code than to learn it
myself.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;With Rust, I could get code translated, but then I&amp;rsquo;d stare at the generated code
and realize I was missing some core concepts and fundamentals. I loved that!&lt;/p&gt;
&lt;p&gt;The first time I hit a lifetime error, I had no mental model for it. That
confusion was exactly what I was looking for.&lt;/p&gt;
&lt;p&gt;Coming from a GC world, memory management is an afterthought — if it requires
any thought at all. Rust really pushes you to think through the ownership and
lifespan of your data, every step of the way.&lt;/p&gt;
&lt;p&gt;In a bizarre way, AI made this gap obvious. It showed me where I didn&amp;rsquo;t
understand things and pointed me toward something worth learning.&lt;/p&gt;
&lt;h3 id="built-with-rust"&gt;
Built with Rust
&lt;a class="heading-anchor" href="#built-with-rust" aria-label="Link to Built with Rust"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s some software that&amp;rsquo;s either built entirely in Rust or uses it in
fundamental ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sharkdp/fd"&gt;fd&lt;/a&gt; (my tool of choice for finding files)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BurntSushi/ripgrep"&gt;ripgrep&lt;/a&gt; (my tool of choice for
searching files)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fishshell.com/"&gt;Fish shell&lt;/a&gt; (my shell of choice, recently rewrote in
Rust)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zed.dev/"&gt;Zed&lt;/a&gt; (my text/code editor of choice)&lt;/li&gt;
&lt;li&gt;Firefox (&lt;a href="https://kau.sh/blog/how-to-firefox/"&gt;my browser&lt;/a&gt; of choice)&lt;/li&gt;
&lt;li&gt;Android?! That&amp;rsquo;s right: Rust now powers some of the
&lt;a href="https://security.googleblog.com/2025/11/rust-in-android-move-fast-fix-things.html"&gt;internals&lt;/a&gt;
of the OS, including the
&lt;a href="https://security.googleblog.com/2025/11/android-quick-share-support-for-airdrop-security.html"&gt;recent&lt;/a&gt;
Quick Share feature.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Many of the most important tools I use daily are built with Rust. Can&amp;rsquo;t hurt to
know the language they&amp;rsquo;re written in.&lt;/p&gt;
&lt;h3 id="kotlin-developers-will-feel-at-home"&gt;
Kotlin developers will feel at home
&lt;a class="heading-anchor" href="#kotlin-developers-will-feel-at-home" aria-label="Link to Kotlin developers will feel at home"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Rust is quite similar to Kotlin in many ways. Both use strict static typing with
advanced type inference. Both support null safety and provide compile-time
guarantees.&lt;/p&gt;
&lt;p&gt;The compile-time strictness and higher-level constructs made it fairly easy for
me to pick up the basics. Syntactically, it feels very familiar.&lt;/p&gt;
&lt;h2 id="how-im-learning-rust"&gt;
How I&amp;rsquo;m learning Rust
&lt;a class="heading-anchor" href="#how-im-learning-rust" aria-label="Link to How I’m learning Rust"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I started by rewriting a couple of small CLI tools I used to keep in Bash or Go.
Even in these tiny programs, the borrow checker forced me to be clear about who
owns what and when data goes away. It can be quite the mental workout at times,
which is perfect for keeping that atrophy from setting in.&lt;/p&gt;
&lt;p&gt;After that, I started to graduate to slightly larger programs and small
services.&lt;/p&gt;
&lt;h3 id="main-resources"&gt;
Main Resources
&lt;a class="heading-anchor" href="#main-resources" aria-label="Link to Main Resources"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;There are two main resources I keep coming back to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fondly referred to as
&amp;ldquo;&lt;a href="https://doc.rust-lang.org/book/ch01-02-hello-world.html"&gt;The Book&lt;/a&gt;&amp;rdquo;.
There&amp;rsquo;s also a convenient
&lt;a href="https://www.youtube.com/playlist?list=PLai5B987bZ9CoVR-QEIN9foz4QCJ0H2Y8"&gt;YouTube series following the book&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Google&amp;rsquo;s &lt;a href="https://google.github.io/comprehensive-rust/"&gt;Comprehensive Rust&lt;/a&gt;
course, presumably created to ramp up their Android team. It even has a
dedicated
&lt;a href="https://google.github.io/comprehensive-rust/android.html"&gt;Android chapter&lt;/a&gt;.
This worked beautifully for me.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="using-ai-as-a-rust-tutor"&gt;
Using AI as a Rust tutor
&lt;a class="heading-anchor" href="#using-ai-as-a-rust-tutor" aria-label="Link to Using AI as a Rust tutor"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;There are times when the book or course mentions a concept and I want to go
deeper. Typically, I&amp;rsquo;d spend time googling, searching Stack Overflow, finding
references, diving into code snippets, and trying to clear up small nuances.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s changed dramatically with AI. One of my early aha moments with AI was
how easy it made ramping up on code. The same is true for learning a new
language like Rust.&lt;/p&gt;
&lt;p&gt;For example, what&amp;rsquo;s the difference&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; between these two:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-rust" data-lang="rust"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;let&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;mut&lt;/span&gt; x_coord: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;let&lt;/span&gt; x_coord: &lt;span style="color:#66d9ef"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;mut&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;i32&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another thing I loved doing is asking AI: what are some idiomatic ways people
use these concepts? Here&amp;rsquo;s a prompt I gave Gemini while learning:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;i&amp;#39;m learning Rust; specifically focusing on the concepts of mutability, shared
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;and exclusive references etc. This is from my notes:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt; &lt;/span&gt;&lt;span style="font-style:italic"&gt;[!tip] There are individual concepts:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;&lt;/span&gt;&lt;span style="font-style:italic"&gt;&amp;gt; 1. Mutability and immutability (through `mut`) keyword
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt; &lt;/span&gt;&lt;span style="font-style:italic"&gt;2. There is References and borrowing (enabled through `&amp;amp;` and `*`)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt; &lt;/span&gt;&lt;span style="font-style:italic"&gt; - when you see `&amp;amp;` - immediately think reading/&amp;#34;borrowing&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt; &lt;/span&gt;&lt;span style="font-style:italic"&gt;3. Things get interesting when you combine `&amp;amp;mut` which is exclusive
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;&amp;gt; &lt;/span&gt;&lt;span style="font-style:italic"&gt; references
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-style:italic"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;what are some usecases in rust where we use some combination of the above. i
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;want to understand if i become a daily rust programmer, what are some idiomatic
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;commonly used patterns I should get familiar with?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s an abbreviated response (the full response was &lt;strong&gt;incredibly&lt;/strong&gt; useful):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Great question! Let me break down the most common idiomatic patterns you&amp;#39;ll
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;encounter daily in Rust with these concepts:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Key Daily Patterns to Remember:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;1.&lt;/span&gt; Default to &amp;amp;T - Use immutable references unless you need mutation
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;2.&lt;/span&gt; Use &amp;amp;mut T for in-place operations - Sorting, filtering, modifying
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;3.&lt;/span&gt; Clone only when necessary - Prefer borrowing over cloning
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;4.&lt;/span&gt; Method receivers tell a story - &amp;amp;self reads, &amp;amp;mut self modifies, self
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; consumes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;5.&lt;/span&gt; Iterators are everywhere - Master .iter(), .iter_mut(), and .into_iter()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;The beauty of Rust is that the compiler guides you - if you try the wrong
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;borrowing pattern, it&amp;#39;ll suggest the right one. After a few weeks, these
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;patterns become second nature!
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s easy to be doom and gloom about AI in coding — the &amp;ldquo;we&amp;rsquo;ll all forget how to
program&amp;rdquo; anxiety is real. But I hope this offers a more hopeful perspective.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re an experienced developer worried about skill atrophy, learn a language
that forces you to think differently. AI can help you cross that gap faster. Use
it as a tutor, not just a code generator.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I did a little C/C++ in high school, but nowhere close to proficiency.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Think mutable var to a &amp;ldquo;shared reference&amp;rdquo; vs. immutable var to an &amp;ldquo;exclusive
reference&amp;rdquo;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/learn-rust-ai-atrophy/</guid><pubDate>Fri, 05 Dec 2025 08:00:00 GMT</pubDate></item><item><title>
AI-assisted coding → Vibe Engineering ← Vibe Coding</title><link>https://kau.sh/blog/vibe-eng/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/ai-coding.webp"
alt="We should go from AI assisted coding to Vibe Engineering not Vibe Coding"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;We&amp;rsquo;re still wrestling with how much AI belongs in our day to day coding. Three
phrases keep getting mixed together:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AI-assisted coding&lt;/li&gt;
&lt;li&gt;Vibe coding&lt;/li&gt;
&lt;li&gt;Vibe engineering&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are not the same.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;AI Assisted&lt;/th&gt;
&lt;th&gt;Vibe coding&lt;/th&gt;
&lt;th&gt;Vibe engineering&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Generates the code&lt;/td&gt;
&lt;td&gt;👨‍💻 (+ 🤖)&lt;/td&gt;
&lt;td&gt;🤖&lt;/td&gt;
&lt;td&gt;🤖&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owns the design&lt;/td&gt;
&lt;td&gt;👨‍💻&lt;/td&gt;
&lt;td&gt;︎🤖&lt;/td&gt;
&lt;td&gt;👨‍💻 ︎&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;They differ along two dimensions: &lt;em&gt;who generates most of the code&lt;/em&gt; and &lt;em&gt;who owns
the design and quality bar&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="1-ai-assisted-coding"&gt;
1. AI-assisted coding
&lt;a class="heading-anchor" href="#1-ai-assisted-coding" aria-label="Link to 1. AI-assisted coding"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is where we started.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;You, the programmer, write most of the code.&lt;/li&gt;
&lt;li&gt;AI acts as super-powered autocomplete.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You type most of the code yourself. AI handles completions, boilerplate, pieces
of the logic and maybe small refactors. Your eyes stay on every line, and the AI
rarely acts without you explicitly placing or accepting a suggestion. This is
still &amp;ldquo;you drive, AI rides shotgun&amp;rdquo;.&lt;/p&gt;
&lt;div class="aside text-ht-lighter italic"&gt;&lt;p&gt;GitHub Copilot was the early poster child. Here&amp;rsquo;s an
&lt;a href="https://www.youtube.com/watch?v=Tr2YBmecdw4"&gt;early video&lt;/a&gt; of me dabbling with
Copilot and scripts. Today, &lt;a href="https://cursor.com/docs/tab/overview"&gt;Cursor Tab&lt;/a&gt;
has taken the crown: fast, inline help without giving up control of the
codebase.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;I particularly like how accurate Cursor is at predicting the next point of code
entry or edit. When it works well, it disappears into the background and
silently increases your productivity. Get it wrong, and you&amp;rsquo;re immediately
frustrated with AI.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id="2-vibe-coding"&gt;
2. Vibe coding
&lt;a class="heading-anchor" href="#2-vibe-coding" aria-label="Link to 2. Vibe coding"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This took the world by storm.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;AI generates most if not all of the code.&lt;/li&gt;
&lt;li&gt;You, the programmer, react with feedback in English and are not as in tune
with what the actual code shapes up to be.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You give up intentional design. In practice, you’re letting the model make the
architectural calls: file structure, abstractions, naming, everything. As your
codebase grows and needs ongoing maintenance, vibe coding breaks down. Your code
turns into mystery‑meat: it runs, but nobody understands clearly how or why.&lt;/p&gt;
&lt;p&gt;This is why experienced greybeards raise alarms and complain about AI bots on
their lawns.&lt;/p&gt;
&lt;p&gt;But Vibe coding does shine in a few scenarios:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Non‑programmers who need something working without caring about internals&lt;/li&gt;
&lt;li&gt;Fast proofs of concept and one‑off scripts&lt;/li&gt;
&lt;li&gt;Exploring an unfamiliar platform or stack where you don&amp;rsquo;t know what &amp;ldquo;good&amp;rdquo;
looks like&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="aside text-ht-lighter italic"&gt;&lt;p&gt;Andrej Karpathy, the &lt;a href="https://www.youtube.com/c/AndrejKarpathy"&gt;godfather&lt;/a&gt; of
LLMs, &lt;a href="https://x.com/karpathy/status/1886192184808149383"&gt;coined&lt;/a&gt; the term. Some
say it with affection, others with disdain. For better or worse, the term has
stuck.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;I vibe‑coded a &lt;a href="https://kau.sh/blog/usbi/"&gt;script to check if your USB‑C cable is any good&lt;/a&gt;
and can attest to some of this. It started as a bash shell script and, for a
more cross‑platform version, I asked the AI to convert it to Go. That took a few
minutes; I actually know very little Go (but I know enough to know that the
output code quality was not great). I still haven&amp;rsquo;t gone through every line of
that script, even after that post became somewhat popular.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s the whole point of vibe coding: you get something that mostly works,
very quickly. It serves the purpose, even if the code isn&amp;rsquo;t something you&amp;rsquo;d
proudly own long term.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id="3-vibe-engineering"&gt;
3. Vibe engineering
&lt;a class="heading-anchor" href="#3-vibe-engineering" aria-label="Link to 3. Vibe engineering"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is where I think software engineering with AI is headed.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;AI generates most of the code, but within your explicit constraints.&lt;/li&gt;
&lt;li&gt;You design the architecture, set boundaries, and review structure &amp;amp;
behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Vibe coding and vibe engineering both have AI generating most of the code. The
difference is who owns the design: in vibe coding, the model effectively owns
it; in vibe engineering, you stay the architect.&lt;/p&gt;
&lt;p&gt;Simon Willison, wizard of
&lt;a href="https://simonwillison.net/2025/May/27/llm-tools/"&gt;LLM CLIs&lt;/a&gt; and skilled
pelicans, is trying to popularize the term
&lt;a href="https://simonwillison.net/2025/Oct/7/vibe-engineering/"&gt;vibe engineering&lt;/a&gt;. He
uses it to describe seasoned engineers who use LLMs and coding agents to move
faster, but stay accountable for design, quality, and long‑term maintainability.
I know the term &amp;ldquo;vibe&amp;rdquo; annoys folks, but I don&amp;rsquo;t mind it tbh.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The difference from AI‑assisted coding is autonomy. With vibe engineering, you
let the AI act in bigger chunks: run commands, edit files, refactor modules; all
while you still stay on the hook for overall design and behavior, not every
keystroke. You&amp;rsquo;re still doing real engineering: researching approaches, making
architectural decisions, defining success criteria, and spending time on code
review and QA.&lt;/p&gt;
&lt;p&gt;It amplifies classic senior‑engineer habits: tests, planning, documentation,
version control, and ruthless code review.&lt;/p&gt;
&lt;div class="aside text-ht-lighter italic"&gt;&lt;p&gt;I &lt;a href="https://kau.sh/blog/container-traffic-control/"&gt;vibe‑engineered&lt;/a&gt; a fairly complex Firefox
add‑on (very proud of this one; it&amp;rsquo;s one of the few Firefox extensions with
tests). That experience sold me on this approach. I wasn&amp;rsquo;t vibe coding: I
decided the architecture, wrote the spec, set constraints, and then used LLMs
and agents to explore implementations, generate code, and iterate based on test
results and reviews.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;In practice, you shape the playground for the AI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write a quick design doc: modules, responsibilities, key interfaces&lt;/li&gt;
&lt;li&gt;Provide strong instructions (tech choices, dependencies to avoid, safety
constraints)&lt;/li&gt;
&lt;li&gt;Ask agents to implement one slice at a time (a feature, a module, a refactor),
using &lt;a href="https://kau.sh/blog/exec-plans/"&gt;exec-plans&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Review diffs for structure and behavior: are the boundaries clean, tests
meaningful, logs sane?&lt;/li&gt;
&lt;li&gt;If the shape is wrong, steer; if the shape is right, let the details go&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;p&gt;As I explored in &lt;a href="https://kau.sh/blog/ai-programming/"&gt;programming paradigms&lt;/a&gt;, we&amp;rsquo;re fully in
the agentic coding era. That shift demands vibe engineering discipline. Agentic
vibe-coding feels like a recipe for scaling chaos.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;An alternative name to this could be agent-driven coding but
vibe-engineering has a better ring to it? Vibe Coding; AI Assisted Coding;
Agent Driven Coding. hmm&amp;hellip;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/vibe-eng/</guid><pubDate>Tue, 18 Nov 2025 00:00:00 GMT</pubDate></item><item><title>
Go with monthly AI subscriptions friends</title><link>https://kau.sh/blog/ai-monthly-subs/</link><description>
&lt;p&gt;Go with monthly AI subscriptions friends&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t remember where I read this tip, but given how fast the AI lab models
move, it&amp;rsquo;s smarter to stick with a monthly plan instead of locking into an
annual one, even if the annual price looks more attractive.&lt;/p&gt;
&lt;p&gt;I hit a &lt;a href="https://developer.android.com/training/dependency-injection"&gt;DI issue&lt;/a&gt;
on Android and was too lazy to debug it myself, so I pointed two models at it.
GPT Codex gave me the cleanest, correct fix. Claude Sonnet 4.5 found a fix, but
it wasn&amp;rsquo;t idiomatic and was pretty aggressive with the changes.&lt;/p&gt;
&lt;p&gt;A month ago, I wouldn&amp;rsquo;t have bothered with anything other than the Claude models
for coding. Today, Codex clearly feels ahead. Google is
&lt;a href="https://x.com/sundarpichai/status/1989481514393121239"&gt;about&lt;/a&gt; to ship its next
Gemini model and, from what I&amp;rsquo;m hearing, it&amp;rsquo;s going to be absurdly good.&lt;/p&gt;
&lt;p&gt;In these wonderfully unstable times, monthly subscriptions are the way to go.&lt;/p&gt;</description><guid>https://kau.sh/blog/ai-monthly-subs/</guid><pubDate>Sun, 16 Nov 2025 18:46:05 GMT</pubDate></item><item><title>
Firefox + UbO is still better than Brave, Edge or any Chromium-based solution</title><link>https://kau.sh/blog/firefox-ubo-vs-brave-chromium/</link><description>
&lt;p&gt;I often find myself replying to claims that Brave, Edge, or other Chromium
browsers effectively achieve the same privacy standards as Firefox + uBlock
Origin (uBO).&lt;/p&gt;
&lt;p&gt;This is simply not true.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Brave and other Chromium browsers are constrained by Google&amp;rsquo;s Manifest V3.
Brave works around this by patching Chromium and self-hosting some MV2
extensions, but it is still swimming upstream against the underlying engine.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;Firefox does not have these MV3 constraints, so uBlock Origin on Firefox
retains more powerful, user-controllable blocking than MV3-constrained setups
like Brave + uBO Lite.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Brave is an excellent product and what I used for a long time. But the
comparison often ignores structural realities. There are important nuances that
make Firefox the more future-proof platform for privacy-conscious users.&lt;/p&gt;
&lt;h1 id="manifest-v3-permanently-nerfs-chromium-based-browsers"&gt;
Manifest V3 permanently nerfs Chromium-based browsers
&lt;a class="heading-anchor" href="#manifest-v3-permanently-nerfs-chromium-based-browsers" aria-label="Link to Manifest V3 permanently nerfs Chromium-based browsers"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The core issue is Manifest V3 (MV3). This is Google&amp;rsquo;s new extension architecture
for Chromium (what Chrome, Brave, and Edge are built on).&lt;/p&gt;
&lt;p&gt;Under Manifest V2, blockers like uBO used the blocking version of the
&lt;code&gt;webRequest&lt;/code&gt; API (&lt;code&gt;webRequest&lt;/code&gt; + &lt;code&gt;webRequestBlocking&lt;/code&gt;) to run their own code on
each network request and decide whether to cancel, redirect, or modify it.&lt;/p&gt;
&lt;p&gt;MV3
&lt;a href="https://developer.chrome.com/docs/extensions/develop/migrate/what-is-mv3"&gt;deprecates that blocking path&lt;/a&gt;
for normal extensions and replaces it with the &lt;code&gt;declarativeNetRequest&lt;/code&gt; (DNR)
API: extensions must declare a capped set of static rules in advance, and the
browser enforces those rules without running extension code per request. This
preserves basic blocking but, as uBO&amp;rsquo;s developer documents,
&lt;a href="https://github.com/uBlockOrigin/uBOL-home/wiki/Frequently-asked-questions-%28FAQ%29#filtering-capabilities-which-cant-be-ported-to-mv3"&gt;removes whole classes of filtering capabilities&lt;/a&gt;
uBO relies on.&lt;/p&gt;
&lt;p&gt;And Google is forcing this change by
&lt;a href="https://developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline"&gt;deprecating MV2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Yeah, shitty.&lt;/p&gt;
&lt;h1 id="braves-workaround-for-manifest-v3"&gt;
Brave’s workaround for Manifest V3
&lt;a class="heading-anchor" href="#braves-workaround-for-manifest-v3" aria-label="Link to Brave’s workaround for Manifest V3"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;To get around the problem, Brave is effectively swimming upstream against its
own engine. It does this in two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Native patching:&lt;/strong&gt; It implements ad-blocking (Shields) natively in
C++/Rust within the browser core to bypass extension limitations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manual extension hosting:&lt;/strong&gt; Brave now has to
&lt;a href="https://brave.com/blog/brave-shields-manifest-v3/"&gt;manually host and update&lt;/a&gt;
specific Manifest V2 extensions (like uBO and AdGuard) on its own servers to
keep them alive as Google purges them from the store.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;They wrote a great &lt;a href="https://brave.com/blog/brave-shields-manifest-v3/"&gt;post&lt;/a&gt;
about this too.&lt;/p&gt;
&lt;p&gt;Brave is doing a great job, but it is operating with a sword of Damocles hanging
over it. The team must manually patch a hostile underlying engine to maintain
functionality that Firefox simply provides out of the box.&lt;/p&gt;
&lt;h1 id="ublock-origin-lite--ublock-origin"&gt;
uBlock Origin Lite != uBlock Origin
&lt;a class="heading-anchor" href="#ublock-origin-lite--ublock-origin" aria-label="Link to uBlock Origin Lite != uBlock Origin"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;A lot of people also say, wait, we now have &amp;ldquo;uBlock Origin Lite&amp;rdquo; that does the
same thing and is even more lightweight!&lt;/p&gt;
&lt;p&gt;Nope.&lt;/p&gt;
&lt;p&gt;It is &amp;ldquo;lite&amp;rdquo; for a reason. You are not getting the same blocking safeguards. uBO
Lite is a stripped-down version necessitated by Google&amp;rsquo;s API restrictions.&lt;/p&gt;
&lt;p&gt;As detailed in the
&lt;a href="https://github.com/uBlockOrigin/uBOL-home/wiki/Frequently-asked-questions-%28FAQ%29"&gt;uBlock Origin FAQ&lt;/a&gt;,
the &amp;ldquo;Lite&amp;rdquo; version lacks in the following ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No on-demand list updates:&lt;/strong&gt; uBO Lite compiles filter lists into the
extension package. The resulting declarative rulesets are refreshed only when
the extension itself updates, so you cannot trigger an immediate filter-list
or malware-list update from within the extension.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No &amp;ldquo;Strict Blocking&amp;rdquo;:&lt;/strong&gt; uBO Lite does not support uBlock Origin&amp;rsquo;s strict
blocking modes or its per-site dynamic matrix. With full uBO on Firefox, my
&lt;a href="https://kau.sh/blog/how-to-firefox/#block-outgoing-network-traffic-to-facebook"&gt;setup&lt;/a&gt;
defines and exposes a custom, per-site rule set that ensures Facebook never
sees my activity on other sites. uBO Lite does not let me express or maintain
that kind of custom policy; I have to rely entirely on whatever blocking logic
ships with the extension.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No dynamic filtering:&lt;/strong&gt; You lose the advanced matrix to block specific
scripts or frames per site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited element picker:&lt;/strong&gt; &amp;ldquo;Pointing and zapping&amp;rdquo; items requires specific,
permission-gated steps rather than being seamless.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No custom filters:&lt;/strong&gt; You cannot write your own custom rules to block nearly
anything, from annoying widgets to entire domains.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="firefox-offers-the-best-experience-with-ublock-origin"&gt;
Firefox offers the best experience with uBlock Origin
&lt;a class="heading-anchor" href="#firefox-offers-the-best-experience-with-ublock-origin" aria-label="Link to Firefox offers the best experience with uBlock Origin"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;uBlock Origin is widely accepted as the most effective content blocker
available. Its creator, gorhill, has explicitly stated that
&lt;a href="https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox"&gt;uBlock Origin works best on Firefox&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So while using a browser like Brave is better than using Chrome or other
browsers that lack a comprehensive blocker, it is not equivalent to Firefox +
uBlock Origin.&lt;/p&gt;
&lt;p&gt;Brave gives you strong, mostly automatic blocking on a Chromium base that is
ultimately constrained by Google&amp;rsquo;s MV3 decisions. Firefox + uBlock Origin gives
you a full-featured, user-controllable blocker on an engine that is not tied to
MV3, which matters if you care about long-term, maximum control over what loads
and who sees your traffic.&lt;/p&gt;</description><guid>https://kau.sh/blog/firefox-ubo-vs-brave-chromium/</guid><pubDate>Fri, 14 Nov 2025 04:38:50 GMT</pubDate></item><item><title>
Cognitive Burden</title><link>https://kau.sh/blog/cognitive-burden/</link><description>
&lt;p&gt;A common argument I hear against AI tools: &amp;ldquo;It doesn&amp;rsquo;t do the job better or
faster than me, so why am I using this again?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Simple answer: cognitive burden.&lt;/p&gt;
&lt;p&gt;My biggest unlock with AI was realizing I could get more done, not because I was
&lt;em&gt;faster&lt;/em&gt;, but because I wasn&amp;rsquo;t wringing my brain with needless tedium. Even if
it took longer or needed more iterations, I&amp;rsquo;d finish less exhausted. That was
the aha moment that sold me.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h1 id="on-writing"&gt;
On Writing
&lt;a class="heading-anchor" href="#on-writing" aria-label="Link to On Writing"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Simple example: when writing a technical&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; post, I start with bullet
points. Sometimes there&amp;rsquo;s a turn of phrase or a bit of humor I enjoy, and I&amp;rsquo;ll
throw those in too. Then a custom agent trained on my writing generates a draft
in my voice.&lt;/p&gt;
&lt;p&gt;After it drafts, I still review every single word.&lt;/p&gt;
&lt;p&gt;A naysayer might ask: &amp;ldquo;Well, if you&amp;rsquo;re reviewing every single word anyway, at
that point, why not just write the post from scratch?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Because it&amp;rsquo;s &lt;em&gt;dramatically&lt;/em&gt; easier and more enjoyable not to grind through and
string together a bunch of prepositions to draft the whole post. I&amp;rsquo;ve captured
the main points and added my creative touch; the AI handles the rest. With far
less &lt;em&gt;effort&lt;/em&gt;, I can publish more quickly — not due to raw speed, but because
it&amp;rsquo;s low‑touch and I focus only on what makes it uniquely me.&lt;/p&gt;
&lt;p&gt;Cognitive burden ↓.&lt;/p&gt;
&lt;h1 id="on-coding"&gt;
On Coding
&lt;a class="heading-anchor" href="#on-coding" aria-label="Link to On Coding"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;About two years ago I pushed back on our CEO in a staff meeting: &amp;ldquo;Most of the
time we engineers waste isn&amp;rsquo;t in writing the code. It&amp;rsquo;s the meetings, design
discussions, working with PMs, fleshing out requirements — that&amp;rsquo;s where we
should focus our AI efforts first.&amp;rdquo;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I missed the same point. Yes, I enjoy crafting every line of code and I&amp;rsquo;m not
bogged down by that process per se, but there&amp;rsquo;s a cognitive tax to pay. I&amp;rsquo;d even
say I could still build a feature faster than some LLMs today (accounting for
quality and iterations) before needing to take a break and recharge. Now I
typically have 3–4 features in flight (with requisite docs, tests, and multiple
variants to boot).&lt;/p&gt;
&lt;p&gt;Yes, I&amp;rsquo;m more productive. And sure, I&amp;rsquo;m probably shipping faster. But that&amp;rsquo;s
correlation, not causation. Speed is a byproduct. The real driver is less
cognitive burden, which lets me carry more.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s invigorated me further as a product engineer is that I&amp;rsquo;m spending a lot
more time on actually &lt;strong&gt;building a good product&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not that I don&amp;rsquo;t know how to write every &lt;code&gt;if&lt;/code&gt; statement; it&amp;rsquo;s just&amp;hellip; no
longer interesting.
&lt;a href="https://world.hey.com/dhh/coding-should-be-a-vibe-50908f49"&gt;Others&lt;/a&gt; feel
differently. Great! To each their own.&lt;/p&gt;
&lt;p&gt;For me, that was the aha moment that sold me on AI. Reducing cognitive burden
made me more effective; everything else followed.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I still craft the smaller personal posts from scratch. I do this mostly
because it helps evolve my thinking as I write each word down — a sort of
muscle memory formed over the years of writing here.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;In hindsight, maybe not one of my finest arguments especially given my
&lt;a href="https://kau.sh/tags/ai-coding-agent/"&gt;recent fervor&lt;/a&gt;. To be fair, while I concede my
pushback was wrong, I don&amp;rsquo;t think leaders then had the correct reasoning
fully synthesized.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/cognitive-burden/</guid><pubDate>Tue, 04 Nov 2025 22:24:00 GMT</pubDate></item><item><title>
Standardize with ⌘ O ⌘ P to reduce cognitive load</title><link>https://kau.sh/blog/cmd-o-cmd-p-cognitive-load/</link><description>
&lt;p&gt;There are a few apps on macOS in the text manipulation category that I end up spending a lot of time on. For example: &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt; (for notes), &lt;a href="https://zed.dev/"&gt;Zed&lt;/a&gt; (text editor + IDE lite), Android Studio &amp;amp; Intellij (IDE++), Cursor (IDE + AI), etc.&lt;/p&gt;
&lt;p&gt;All these apps have two types of commands that I frequently use:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a specific file or note&lt;/li&gt;
&lt;li&gt;Open the command palette (or find any action menu)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But by default, these apps use ever so slightly different shortcuts. One might use ⌘ P, another might use ⌘ ⇧ P, etc.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve found it incredibly helpful to take a few minutes and make these specific keyboard shortcuts the same everywhere. So now I use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;⌘ O&lt;/strong&gt; – Open a file/note&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⌘ P&lt;/strong&gt; – Open the command palette (or equivalent action menu)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This small change has reduced cognitive load significantly. I no longer have to think about which app I&amp;rsquo;m in, and what the shortcut is for that specific app. Muscle memory takes over, and I can just get things done faster. Highly recommended!&lt;/p&gt;</description><guid>https://kau.sh/blog/cmd-o-cmd-p-cognitive-load/</guid><pubDate>Sat, 01 Nov 2025 00:38:50 GMT</pubDate></item><item><title>
Product vs Model Innovation</title><link>https://kau.sh/blog/product-vs-model-innovation/</link><description>
&lt;p&gt;Cursor is known to be the fastest growing SaaS &lt;a href="https://sacra.com/research/cursor-at-100m-arr/"&gt;company in history&lt;/a&gt;. Yet, because they don&amp;rsquo;t control the underlying model, I&amp;rsquo;ve always felt the writing was on the wall. They are an AI tool, not an AI product, and tool companies risk disruption from model providers who can build competing tools on their own platforms.&lt;/p&gt;
&lt;p&gt;But Cursor has consistently shown they can deliver the highest quality product. The paradigms they introduce set the standard for how AI integrates into developer workflows. When Claude Code and other TUIs emerged with smoother agentic editing, I wondered how Cursor would respond.&lt;/p&gt;
&lt;p&gt;Sure enough, Cursor 2.0 proves they can stay ahead by introducing some of the best UI paradigms for an agentic IDE.&lt;/p&gt;
&lt;p&gt;What keeps Cursor going is &lt;strong&gt;product innovation&lt;/strong&gt;, not model innovation. They aren&amp;rsquo;t trying to win the model game.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; And while many worry the writing is on the wall, they continue to set the pace for how IDEs adopt AI.&lt;/p&gt;
&lt;h2 id="addendum"&gt;
Addendum
&lt;a class="heading-anchor" href="#addendum" aria-label="Link to Addendum"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The more I read their subsequent post on &lt;a href="https://cursor.com/blog/kernels"&gt;kernels&lt;/a&gt; and how they went about training Composer (their new custom model), I&amp;rsquo;m starting to change my thinking about Cursor and ever so slightly more bullish on the writing not being on the wall for them after all. Their first mover advantage (with a fantastic product) allowed them to collect data that&amp;rsquo;s unique to the coding IDE use case. This has now given them the ability to train a highly effective custom model that could potentially give the Sonnet 4.5s and GPT codexes of the world a run for their money.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Yes, I&amp;rsquo;m aware they have custom models and &amp;ldquo;Cursor Tab&amp;rdquo; is an example of them doing &lt;a href="https://cursor.com/blog/composer"&gt;amazing&lt;/a&gt; work in that area. But their &amp;ldquo;product&amp;rdquo; still relies heavily on the primary model. If Cursor didn&amp;rsquo;t support Sonnet 4.5, for example, it would be a non-starter for most users.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/product-vs-model-innovation/</guid><pubDate>Wed, 29 Oct 2025 19:32:07 GMT</pubDate></item><item><title>
Agent Skills: How is it different from command and other tools?</title><link>https://kau.sh/blog/claude-skills/</link><description>
&lt;p&gt;Anthropic announced &lt;a href="https://www.anthropic.com/news/skills"&gt;Claude Skills&lt;/a&gt; and
my first reaction was: &amp;ldquo;So what?&amp;rdquo; We already have &lt;code&gt;AGENTS.md&lt;/code&gt;, slash commands,
nested instructions, or even MCPs. What&amp;rsquo;s new here?&lt;/p&gt;
&lt;p&gt;But if
&lt;a href="https://simonwillison.net/2025/Oct/16/claude-skills/"&gt;Simon W thinks this is a big deal&lt;/a&gt;,
then pelicans be damned; I must be missing something. So I dissected every word
of Anthropic&amp;rsquo;s
&lt;a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills"&gt;eng. blog post&lt;/a&gt;
to find what I missed.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t think the innovation is &lt;em&gt;what&lt;/em&gt; Skills does or achieves, but rather &lt;em&gt;how&lt;/em&gt;
it does it that&amp;rsquo;s super interesting. This continues their push on
&lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents"&gt;context engineering&lt;/a&gt;
as the next frontier.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Addendum
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Skills has been out for sometime and Anthropic has even made it an
&lt;a href="https://claude.com/blog/organization-skills-and-directory"&gt;open standard&lt;/a&gt;.
Make sure to read the addedum at the end of this post, where I share some
newer realizations I&amp;rsquo;ve had since first writing it.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id="skills-and-how-it-works"&gt;
Skills and how it works
&lt;a class="heading-anchor" href="#skills-and-how-it-works" aria-label="Link to Skills and how it works"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Skills are simple markdown files with YAML frontmatter. But what makes them
different is the idea of &lt;strong&gt;progressive disclosure&lt;/strong&gt;:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="quote" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Progressive disclosure is the core design principle that makes Agent
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Skills flexible and scalable. Like a well-organized manual that starts with a
table of contents, then specific chapters, and finally a detailed appendix,
skills let Claude load information only as needed:&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;So here&amp;rsquo;s how it works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Scan at startup&lt;/strong&gt;: Claude scans available Skills and reads only their YAML
descriptions (name, summary, when to use)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build lightweight index&lt;/strong&gt;: This creates a catalog of capabilities (with
minimal token cost); so think dozens of tokens per skill&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Load on demand&lt;/strong&gt;: The full content of a Skill only gets injected into
context when Claude&amp;rsquo;s reasoning determines it&amp;rsquo;s relevant to the current task&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This dynamic context loading mechanism is very &lt;strong&gt;token efficient&lt;/strong&gt;; that&amp;rsquo;s the
interesting development here. In this token-starved AI economy, that&amp;rsquo;s 🤑. Other
solutions aren&amp;rsquo;t as good in this specific way.&lt;/p&gt;
&lt;h1 id="why-the-alternatives-arent-as-good"&gt;
Why the alternatives aren&amp;rsquo;t as good
&lt;a class="heading-anchor" href="#why-the-alternatives-arent-as-good" aria-label="Link to Why the alternatives aren’t as good"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="agentsmd-monolithic"&gt;
AGENTS.md (monolithic)
&lt;a class="heading-anchor" href="#agentsmd-monolithic" aria-label="Link to AGENTS.md (monolithic)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✓ Auto-discovered and loaded&lt;/li&gt;
&lt;li&gt;✗ Static: all context loaded upfront (bloats context window at scale)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why not throw everything into &lt;code&gt;AGENTS.md&lt;/code&gt;? You could add all the info directly
and agents would load it at session start. The problem: loading everything fills
up your context window fast, and your model starts outputting garbage unless you
adopt other strategies. Not scalable.&lt;/p&gt;
&lt;h2 id="nested-agentsmd"&gt;
Nested AGENTS.md
&lt;a class="heading-anchor" href="#nested-agentsmd" aria-label="Link to Nested AGENTS.md"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✓ Scoped to directories&lt;/li&gt;
&lt;li&gt;✗ Not portable across folders; overrides behavior, not composition&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Place an AGENTS.md in each subfolder and agents read the nearest file in the
tree. This splits context across folders and solves token bloat. But it&amp;rsquo;s not
portable across directories and creates an override behavior instead of true
composition.&lt;/p&gt;
&lt;h2 id="referenced-instruction-files"&gt;
Referenced instruction files
&lt;a class="heading-anchor" href="#referenced-instruction-files" aria-label="Link to Referenced instruction files"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✓ Organized and modular&lt;/li&gt;
&lt;li&gt;✗ Still requires static loading when referenced&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Place instructions in separate files and reference them in AGENTS.md. This fixes
the portability problem vs the nested approach. But when referenced, the full
content still loads statically. Feels closest to Skills, but lacks the JIT
loading mechanism.&lt;/p&gt;
&lt;h2 id="slash-commands"&gt;
Slash commands
&lt;a class="heading-anchor" href="#slash-commands" aria-label="Link to Slash commands"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✓ Powerful and procedural&lt;/li&gt;
&lt;li&gt;✗ Manual invocation breaks agent autonomy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Slash commands (or &lt;code&gt;/prompts&lt;/code&gt; in Codex) let you provide organized,
hyper-specific instructions to the LLM. You can even script sequences of
actions, just like Skills. The problem: these aren&amp;rsquo;t auto-discovered. You must
manually invoke them, which breaks agent autonomy.&lt;/p&gt;
&lt;h2 id="mcps-model-context-protocol"&gt;
MCPs (Model Context Protocol)
&lt;a class="heading-anchor" href="#mcps-model-context-protocol" aria-label="Link to MCPs (Model Context Protocol)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✓ Access to external data sources&lt;/li&gt;
&lt;li&gt;✗ Heavyweight; vendor lock-in; overkill for procedural knowledge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Skills handle 80% of MCP use cases with 10% of the complexity. You don&amp;rsquo;t need a
network protocol if you can drop a markdown file that says &amp;ldquo;to access GitHub
API, use &lt;code&gt;curl api.github.com&lt;/code&gt; with &lt;code&gt;$GITHUB_API_KEY&lt;/code&gt;.&amp;rdquo; To be quite honest, I&amp;rsquo;ve
never been a big fan of MCPs. I think they make a lot of sense for the
inter-service communication but more often than not they&amp;rsquo;re overkill.&lt;/p&gt;
&lt;h1 id="the-verdict"&gt;
The Verdict
&lt;a class="heading-anchor" href="#the-verdict" aria-label="Link to The Verdict"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;Token-efficient context loading is the innovation. Everything else you can
already do with existing tools.&lt;/li&gt;
&lt;li&gt;If this gets adoption, it could replace slash commands and simplify MCP use
cases.&lt;/li&gt;
&lt;li&gt;I keep forgetting, this is for the Claude product generally (not just Claude
Code) which is cool.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Skills is starting to solve the larger problem: &amp;ldquo;How do I give my agent deep
expertise without paying the full context cost upfront?&amp;rdquo; That&amp;rsquo;s an architectural
improvement definitely worth solving and Skills looks like a good attempt.&lt;/p&gt;
&lt;h1 id="addendum"&gt;
Addendum
&lt;a class="heading-anchor" href="#addendum" aria-label="Link to Addendum"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;Dec 25, 2025&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Skills has been out for sometime now. Here are some of the realizations I&amp;rsquo;ve
since had:&lt;/p&gt;
&lt;h3 id="skills-are-an-open-standard"&gt;
Skills are an open standard
&lt;a class="heading-anchor" href="#skills-are-an-open-standard" aria-label="Link to Skills are an open standard"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Anthropic &lt;a href="https://claude.com/blog/organization-skills-and-directory"&gt;announced&lt;/a&gt;
that they&amp;rsquo;ve made Agent Skills an &lt;a href="https://agentskills.io/home"&gt;open standard&lt;/a&gt;
now. It&amp;rsquo;s telling how quickly the various AI labs &amp;amp; companies have adopted Agent
Skills. I think the implication here is that agent skills will probably subsume
other equivalent mechanisms and tools. Codex for example has a more
&lt;a href="https://x.com/kaushikgopal/status/2004662229158035736?s=20"&gt;flushed out version&lt;/a&gt;
of skills than slash commands (even though slash commands was a feature that predates skills).&lt;/p&gt;
&lt;h3 id="skills-is-universal"&gt;
Skills is universal
&lt;a class="heading-anchor" href="#skills-is-universal" aria-label="Link to Skills is universal"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I missed this the first time around, but you can use custom Skills outside of
developer environments. For example claude.ai supports
&lt;a href="https://platform.claude.com/docs/en/agents-and-tools/agent-skills/overview#claude-ai"&gt;uploading zip files as custom skills&lt;/a&gt;.
This can be pretty powerful for non-developers. If I build a python script that
generates a custom pdf from three uploads and some custom text, package it up as
a custom skill and upload to claude.ai, mom and dad can leverage this without
having to write a lick of code.&lt;/p&gt;</description><guid>https://kau.sh/blog/claude-skills/</guid><pubDate>Mon, 20 Oct 2025 11:45:11 GMT</pubDate></item><item><title>
Cargo Culting</title><link>https://kau.sh/blog/cargo-culting/</link><description>
&lt;p&gt;If you&amp;rsquo;re a software engineer long enough, you will meet some gray beards that throw out-of-left-field phrases to convey software wisdom.&lt;/p&gt;
&lt;p&gt;For example, you should know if you&amp;rsquo;re &lt;a href="https://kau.sh/blog/yak-shaving-bike-shedding/"&gt;yak-shaving or bike-shedding&lt;/a&gt;, and when that&amp;rsquo;s even a good thing.&lt;/p&gt;
&lt;p&gt;A recent &lt;a href="https://news.ycombinator.com/item?id=45618350"&gt;HN article&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; reminded me of another nugget – Cargo Culting (or Cargo Cult Programming).&lt;/p&gt;
&lt;h3 id="definition"&gt;
Definition
&lt;a class="heading-anchor" href="#definition" aria-label="Link to Definition"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Cargo Culting&lt;/strong&gt;: ritualizing a process without understanding it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the context of programming:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;practice of applying a design pattern or coding style blindly without understanding the reasons behind it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="example"&gt;
Example
&lt;a class="heading-anchor" href="#example" aria-label="Link to Example"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m going to take this opportunity to air one of my personal cargo-culting pet peeves, sure to kick up another storm: &lt;strong&gt;Making everything small&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When I get PR feedback saying &amp;ldquo;this class is too long, split this!&amp;rdquo;, I get ready to launch into a tirade: you&amp;rsquo;re confusing &lt;em&gt;small&lt;/em&gt; with &lt;em&gt;logically small&lt;/em&gt; – ritualizing line count without understanding cohesion. You can make code small by being terse: removing whitespace, cramming logic into one-liners, using clever shorthand.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; But you&amp;rsquo;ve just made it harder to read. A function that does one cohesive thing beats multiple smaller functions scattered across files.&lt;/p&gt;
&lt;h3 id="etymology"&gt;
Etymology
&lt;a class="heading-anchor" href="#etymology" aria-label="Link to Etymology"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;As the &lt;a href="https://en.wikipedia.org/wiki/Cargo_cult_programming"&gt;parable&lt;/a&gt; goes, after the end of the Second World War, indigenous tribes believed that air delivery of cargo would resume if they carried out the proper rituals, such as building runways, lighting fires next to them, and wearing headphones carved from wood while sitting in fabricated control towers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;While on the surface amusing, there&amp;rsquo;s sadness if you dig into the history and contributing factors (value dominance, language &amp;amp; security barriers). I don&amp;rsquo;t think that&amp;rsquo;s reason to avoid the term altogether. We as humans sometimes have to embrace our dark history, acknowledge our wrongs and build kindness in our hearts. We cannot change our past, but we can change our present and future.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The next time someone on your team ritualizes a pattern without understanding it, you&amp;rsquo;ll know what to call it.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Jargon_File"&gt;Who&lt;/a&gt; comes up with these terms anyway?&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Now that you&amp;rsquo;re aware of the term, you&amp;rsquo;ll realize that the original article&amp;rsquo;s use of the term cargo-cult is weak at best. In HN style, the comments were quick to call this out.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;You know exactly what I&amp;rsquo;m thinking of, fellow Kotlin programmers.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/cargo-culting/</guid><pubDate>Sat, 18 Oct 2025 16:37:23 GMT</pubDate></item><item><title>
ExecPlans – How to get your coding agent to run for hours</title><link>https://kau.sh/blog/exec-plans/</link><description>
&lt;p&gt;I&amp;rsquo;ve long maintained that the biggest unlock with AI coding agents is the planning step. In my &lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-1-plan-with-the-agent"&gt;previous post&lt;/a&gt;, I describe how I use a &lt;code&gt;.ai/plans&lt;/code&gt; directory and ask the agent to diligently write down its tasks before and during execution.&lt;/p&gt;
&lt;p&gt;Most coding agents now include this as a feature. Cursor, for example, introduced it as an &lt;a href="https://cursor.com/blog/plan-mode"&gt;explicit feature&lt;/a&gt; recently.&lt;/p&gt;
&lt;p&gt;While that all felt validating, on a plane ride home I watched OpenAI&amp;rsquo;s DevDay. One of the most valuable sessions was &lt;a href="https://youtu.be/Gr41tYOzE20?si=ZqfoPD7mnKeXtN4-&amp;amp;t=756"&gt;Shipping with Codex&lt;/a&gt;. Aaron Friel — credited with record-long sessions and token output — walked through his process and the idea of &amp;ldquo;ExecPlans.&amp;rdquo; It felt similar at first, but I quickly realized this was some god-level planning.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;He said OpenAI would release his PLANS.md soon, but I couldn&amp;rsquo;t wait. On that flight, with janky wifi, I rebuilt what I could from the talk and grew my baby plan into something more mature — and I was already seeing &lt;em&gt;better&lt;/em&gt; results. I pinged Aaron on BlueSky for the full doc, and he very kindly &lt;a href="https://bsky.app/profile/aaronfriel.bsky.social/post/3m2sqosxldc25"&gt;shared&lt;/a&gt; the &lt;a href="https://github.com/openai/openai-cookbook/pull/2185/"&gt;PR&lt;/a&gt; that&amp;rsquo;s about to get merged with detailed information.&lt;/p&gt;
&lt;p&gt;My god, this thing is a work of art. Aaron clearly spent a lot of time honing it. I&amp;rsquo;ve tried it on two PRs so far, and it&amp;rsquo;s working fantastically. I still need to put it through its paces on some larger work projects, but I feel comfortable preemptively calling it the gold standard for planning.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve made a few small tactical tweaks to how I use it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I instruct the agent to write plans to &lt;code&gt;.ai/plans&lt;/code&gt; (works across coding agents)&lt;/li&gt;
&lt;li&gt;In my &lt;a href="https://kau.sh/blog/agents-md"&gt;AGENTS.md&lt;/a&gt; I tell agents to put temporary plans in &lt;code&gt;.ai/plans/tmp&lt;/code&gt; (which I&amp;rsquo;ve gitignored)&lt;/li&gt;
&lt;li&gt;I keep the master &lt;code&gt;PLANS.md&lt;/code&gt; Aaron shared at &lt;code&gt;@.ai/plans/PLANS.md&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is &lt;strong&gt;really&lt;/strong&gt; a big unlock, folks. Try it now.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;The latest PLANS.md can be found in &lt;a href="https://github.com/openai/openai-cookbook/pull/2185"&gt;Aaron&amp;rsquo;s PR&lt;/a&gt;. Use it as a template in your &lt;code&gt;.ai/plans&lt;/code&gt; folder. Then instruct your agent via AGENTS.md to always write an ExecPlan when working on complex tasks.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;I highly recommend you go watch Aaron&amp;rsquo;s part of the talk &lt;a href="https://youtu.be/Gr41tYOzE20?si=ZqfoPD7mnKeXtN4-&amp;amp;t=756"&gt;Shipping with Codex&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ll update this post once it&amp;rsquo;s merged or if anything changes.&lt;/p&gt;
&lt;p&gt;Update: I&amp;rsquo;ve been using this for the last few days (~8 PRs so far) and on an average I&amp;rsquo;ve definitely gotten my agents to run for much longer successfully (longest was about ~1 hour but frequently &amp;gt;30 mts). This is the way.&lt;/p&gt;</description><guid>https://kau.sh/blog/exec-plans/</guid><pubDate>Mon, 13 Oct 2025 01:01:48 GMT</pubDate></item><item><title>
We are becoming software Conductors</title><link>https://kau.sh/blog/software-conductors/</link><description>
&lt;p&gt;Engineers won&amp;rsquo;t be replaced by tools that do their tasks better; they&amp;rsquo;ll be replaced by systems that make those tasks nonessential.&lt;/p&gt;
&lt;p&gt;Sangeet Paul Choudary wrote an &lt;a href="https://www.ai-supremacy.com/i/172078340/why-ai-experts-get-their-predictions-on-jobs-so-wrong"&gt;insightful piece&lt;/a&gt; on AI-driven job displacement and a more transformative way to think about it:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;To truly understand how AI affects jobs, we must look beyond individual tasks to comprehend AI’s impact on our workflows and organizations.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;The task-centric view sees AI as a tool that improves how individual tasks are performed. Work remains structurally unchanged. AI is simply layered on top to improve speed or lower costs.
&amp;hellip;In this framing, the main risk is that a smarter tool might replace the person doing the task.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;The system-centric view, on the other hand, looks at how AI reshapes the organization of work itself. It focuses on how tasks fit into broader workflows and how their value is determined by the logic of the overall system. In this view, even if tasks persist, the rationale for grouping them into a particular job, or even performing them within the company, may no longer hold once AI changes the system’s structure.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;If we adopt a system-centric view, how does the role of a software engineer evolve?&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; I&amp;rsquo;ve had a notion for some time — the role will transform into a software &amp;ldquo;conductor&amp;rdquo;.&lt;/p&gt;
&lt;h1 id="software-conductors"&gt;
Software conductors
&lt;a class="heading-anchor" href="#software-conductors" aria-label="Link to Software conductors"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
music conductors
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;&lt;em&gt;conducting is the art of directing the simultaneous performance of several players or singers by the use of gesture&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The tasks a software conductor must master differ from those of today&amp;rsquo;s software engineer. Here are some of the shifts I can think of:&lt;/p&gt;
&lt;h2 id="task-orchestration-mastery"&gt;
Task Orchestration Mastery
&lt;a class="heading-anchor" href="#task-orchestration-mastery" aria-label="Link to Task Orchestration Mastery"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The craft is knowing exactly how much detail to provide in prompts: too little and models thrash; too much and they overfit or hallucinate constraints. You&amp;rsquo;ll need to write spec-grade prompts that define interfaces, acceptance criteria, and boundaries — chunking work into units atomic enough for clear execution yet large enough to preserve context. Equally critical: recognizing when to interrupt and redirect — catching drift early and steering with surgical edits rather than expensive reruns or loops.&lt;/p&gt;
&lt;h2 id="ai-compatible-system-design"&gt;
AI-Compatible System Design
&lt;a class="heading-anchor" href="#ai-compatible-system-design" aria-label="Link to AI-Compatible System Design"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll need to design systems that AI can both navigate and extend elegantly. This means clear module boundaries with explicit interfaces, descriptive naming that models can infer purpose from, and tests that double as executable specs. The goal: systems where AI agents can make surgical changes quickly and efficiently without cascading tech debt.&lt;/p&gt;
&lt;h2 id="parallel-experimentation"&gt;
Parallel Experimentation
&lt;a class="heading-anchor" href="#parallel-experimentation" aria-label="Link to Parallel Experimentation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;We&amp;rsquo;re moving from building one solution to exploring many simultaneously. This unlocks three levels of experimentation:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Feature variants&lt;/strong&gt; — Build competing product approaches in parallel. One agent implements phone-only authentication while another builds traditional email/password. Both ship behind feature flags. Let users decide which wins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Implementation variants&lt;/strong&gt; — Build the same feature with different architectures. Redis caching on path A, SQLite on path B. Run offline benchmarks and online canaries to measure which performs better under real load.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Personalized variants&lt;/strong&gt; — Stop looking for a single winner. The most radical shift: each user might get their own variant. Not just enterprise vs consumer, but individual-level personalization where the system learns what works for you specifically. Power users get keyboard shortcuts and dense information; casual users get guided flows with progressive disclosure. Users who convert on social proof see testimonials; analytical users see feature comparisons. AI makes the economics work — what was prohibitively expensive (maintaining thousands of personalized codepaths manually) becomes viable when AI generates, tests, and synchronizes variants automatically.&lt;/p&gt;
&lt;p&gt;The skill: running rigorous evals, measuring trade-offs with metrics, and orchestrating the complexity of multiple live variants.&lt;/p&gt;
&lt;h2 id="real-time-cost-performance-arbitrage"&gt;
Real-time Cost-Performance Arbitrage
&lt;a class="heading-anchor" href="#real-time-cost-performance-arbitrage" aria-label="Link to Real-time Cost-Performance Arbitrage"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Every API call has a price, a latency budget, and quality trade-offs. You&amp;rsquo;ll need to master arbitrage between expensive reasoning models and cheaper models, knowing when to leverage MCPs, local tools, or cloud APIs. Learn how models approach refactors differently from new features or bug fixes, then tune prompts, context windows, and routing strategies accordingly.&lt;/p&gt;
&lt;h2 id="observability--evals-as-a-discipline"&gt;
Observability &amp;amp; Evals as a Discipline
&lt;a class="heading-anchor" href="#observability--evals-as-a-discipline" aria-label="Link to Observability &amp;amp; Evals as a Discipline"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;You&amp;rsquo;ll need to build golden test sets, trace model runs, classify failure modes, and treat evals like unit tests. Evaluation frameworks with baseline datasets, regression suites, and automated canaries that catch quality drift before production become non-negotiable. Without observability, you can&amp;rsquo;t iterate safely or validate that changes actually improve outcomes.&lt;/p&gt;
&lt;h2 id="generalist-thinking-over-specialist-skills"&gt;
Generalist Thinking Over Specialist Skills
&lt;a class="heading-anchor" href="#generalist-thinking-over-specialist-skills" aria-label="Link to Generalist Thinking Over Specialist Skills"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Framework fluency loses value when AI handles syntax. What matters is depth in three areas:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Core computer science fundamentals&lt;/strong&gt; — Not because AI doesn&amp;rsquo;t know them, but because you need to verify AI made the right trade-offs for your specific constraints. AI might use quicksort when your dataset is always 10 items. It might optimize a function that runs once a day while missing the N+1 query in your hot path — where you loop through 1000 users making a database call for each instead of batching. Your value is code review with context: catching when AI optimizes for the wrong thing, knowing when simple beats clever, and spotting performance cliffs before they ship.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Product judgment&lt;/strong&gt; — Knowing which problem to solve, not just how to solve it. AI can build any feature you describe, but it can&amp;rsquo;t tell you whether that feature matters. Understanding user needs, prioritizing ruthlessly, and recognizing when you&amp;rsquo;re overbuilding becomes the bottleneck.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Domain expertise&lt;/strong&gt; — Deep knowledge of your problem space — whether it&amp;rsquo;s payments, healthcare, logistics, or graphics. AI can write generic code, but it struggles with domain-specific edge cases, regulations, and the unwritten rules experts know. The more niche your expertise, the harder you are to replace.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;These are the skills that matter for the next three years. But I don&amp;rsquo;t have a crystal ball beyond that. At the pace AI is evolving, even conductors might become a role that AI plays better. The orchestration itself could be automated, leaving us asking the same questions about the next evolution.&lt;/p&gt;
&lt;p&gt;For now, learning to conduct is how we stay relevant.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Companies will change how they ship too; but the nearer shift is the individual’s role, so that&amp;rsquo;s my focus for this post.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/software-conductors/</guid><pubDate>Sat, 11 Oct 2025 18:59:38 GMT</pubDate></item><item><title>
Sorting Prompts - LLMs are not wrong you just caught them mid thought</title><link>https://kau.sh/blog/sorting-prompt/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="quote" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Good sensemaking processes iterate. We develop initial theories, note some alternative ones. We then take those theories that we’ve seen and stack up the evidence for one against the other (or others). Even while doing that we keep an eye out for other possible explanations to test. When new explanations stop appearing and we feel that the evidence pattern increasingly favors one idea significantly over another we call it a day.&lt;/p&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;LLMs are no different. What often is deemed a &amp;ldquo;wrong&amp;rdquo; response is often4 merely a first pass at describing the beliefs out there. And the solution is the same: iterate the process.&lt;/p&gt;
&lt;p&gt;&amp;hellip;&lt;/p&gt;
&lt;p&gt;What I&amp;rsquo;ve found specifically is that pushing it to do a second pass without putting a thumb on the scale almost always leads to a better result. To do this I use what I call &amp;ldquo;sorting statements&amp;rdquo; that try to do a variety of things&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Mike Caulfield is someone who cares about the veracity of information. The entire post is fascinating and has painted LLM search results in a new way for me.&lt;/p&gt;
&lt;p&gt;I now have a Raycast Snippet &lt;code&gt;aiprompt;&lt;/code&gt; which expands to this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;What is the evidence for and against the claim/guidance just stated?
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Already I&amp;rsquo;m seeing much better results.&lt;/p&gt;</description><guid>https://kau.sh/blog/sorting-prompt/</guid><pubDate>Fri, 10 Oct 2025 16:19:26 GMT</pubDate></item><item><title>
Build your own /init command like Claude Code</title><link>https://kau.sh/blog/build-ai-init-command/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Build your own &lt;code&gt;/init&lt;/code&gt; command
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Claude&amp;rsquo;s &lt;code&gt;/init&lt;/code&gt; makes it easy to add clear repo instructions.
Build your own and use it with any agent to add or improve on an existing AGENTS.md
&lt;a href="https://gist.github.com/kaushikgopal/92e3a2fe89d602f6cdd94969c771989e"&gt;Here&amp;rsquo;s the one I came up with&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Claude Code really nailed the onboarding experience for agentic coding. Open it, type &lt;code&gt;/init&lt;/code&gt;, and you get a &lt;code&gt;CLAUDE.md&lt;/code&gt; that delivers better results than a repo without proper system instructions (or an &lt;code&gt;AGENTS.md&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a clever way to ramp a repo fast. As I wrote last time, it hits one of the &lt;a href="https://kau.sh/blog/three-req-ai-coding/"&gt;three levers for successful AI coding&lt;/a&gt; - seeding the right context. Even &lt;a href="https://openai.com/index/codex-now-generally-available/"&gt;Codex CLI&lt;/a&gt; now comes with a built-in init prompt.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no secret&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; sauce: &lt;code&gt;/init&lt;/code&gt; is just a strong prompt that writes (or improves) an instructions file.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the prompt, &lt;a href="https://www.reddit.com/r/ClaudeAI/comments/1loa1c1/comment/n0lqqla/"&gt;per&lt;/a&gt; folks who&amp;rsquo;ve reverse‑engineered it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Please analyze this codebase and create a CLAUDE.md file, which will be given to future instances of Claude Code to operate in this repository.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;What to add:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;1.&lt;/span&gt; Commands that will be commonly used, such as how to build, lint, and run tests. Include the necessary commands to develop in this codebase, such as how to run a single test.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;2.&lt;/span&gt; High-level code architecture and structure so that future instances can be productive more quickly. Focus on the &amp;#34;big picture&amp;#34; architecture that requires reading multiple files to understand
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Usage notes:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; If there&amp;#39;s already a CLAUDE.md, suggest improvements to it.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; When you make the initial CLAUDE.md, do not repeat yourself and do not include obvious instructions like &amp;#34;Provide helpful error messages to users&amp;#34;, &amp;#34;Write unit tests for all new utilities&amp;#34;, &amp;#34;Never include sensitive information (API keys, tokens) in code or commits&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Avoid listing every component or file structure that can be easily discovered
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Don&amp;#39;t include generic development practices
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include the important parts.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; If there is a README.md, make sure to include the important parts.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Do not make up information such as &amp;#34;Common Development Tasks&amp;#34;, &amp;#34;Tips for Development&amp;#34;, &amp;#34;Support and Documentation&amp;#34; unless this is expressly included in other files that you read.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Be sure to prefix the file with the following text:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can write your own and get the same result. I use a &lt;a href="https://gist.github.com/kaushikgopal/92e3a2fe89d602f6cdd94969c771989e"&gt;custom &lt;code&gt;/initialize&lt;/code&gt;&lt;/a&gt; on new repos to get up and running fast.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; I tweaked it to work across different coding agents and sprinkled in a few tips I collected along the way.&lt;/p&gt;
&lt;p&gt;It should create a relevant &lt;code&gt;AGENTS.md&lt;/code&gt;; if one exists, it updates it. Save this prompt as a custom command and use it with any tool — Gemini CLI, Codex, Amp, Firebender, etc. You aren&amp;rsquo;t stuck with any single tool.&lt;/p&gt;
&lt;p&gt;One more tip: a reasoning model works best for these types of commands.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I must say: the more time I spend with these tools, the more &amp;ldquo;emperor‑has‑no‑clothes&amp;rdquo; moments I have.&lt;/em&gt; Some of the ways these things work are deceptively simple.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Claude does a few other things, like instructing its inner agent tools (BatchTool &amp;amp; GlobTool) to collect related files and existing instructions (&lt;code&gt;package*.json&lt;/code&gt;, &lt;code&gt;*.md&lt;/code&gt;, &lt;code&gt;.cursor/rules/**&lt;/code&gt;, &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;, etc.) as context for generating or updating &lt;code&gt;CLAUDE.md&lt;/code&gt;. But the prompt is the meat.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I used this prompt when I &lt;a href="https://kau.sh/blog/container-traffic-control/"&gt;vibe‑engineered a maintainable Firefox add‑on&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/build-ai-init-command/</guid><pubDate>Tue, 07 Oct 2025 15:58:06 GMT</pubDate></item><item><title>
Three important things to get right for successful AI Coding</title><link>https://kau.sh/blog/three-req-ai-coding/</link><description>
&lt;p&gt;I often hear AI coding feels inconsistent or underwhelming. I&amp;rsquo;m surprised by this because more often than not, I get good results.&lt;/p&gt;
&lt;p&gt;When working with any AI agent (&lt;em&gt;or any LLM tool&lt;/em&gt;), there are really just three things that drive your results:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;the context you provide&lt;/li&gt;
&lt;li&gt;the prompt you write&lt;/li&gt;
&lt;li&gt;executing in chunks&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This may sound discouragingly obvious, but being deliberate about these three (every time you send a request to Claude Code, ChatGPT etc.) makes a noticeable difference.&lt;/p&gt;
&lt;p&gt;&amp;hellip;and it&amp;rsquo;s straightforward to get 80% of this right.&lt;/p&gt;
&lt;h1 id="context"&gt;
Context
&lt;a class="heading-anchor" href="#context" aria-label="Link to Context"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;LLMs are pocket‑sized world knowledge machines. Every time you work on a task, you need to trim that machine to a surgical one that&amp;rsquo;s only focused on the task at hand. You do this by seeding context.&lt;/p&gt;
&lt;p&gt;The simplest way to do this, especially for AI Coding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;System rules &amp;amp; agent instructions&lt;/strong&gt;: This is basically your &lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/strong&gt; file where you briefly explain what the project is, the architecture, conventions used in the repository, and navigation the project.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tooling&lt;/strong&gt;: Lot of folks miss this, but in your AGENTS.md, explicitly point to the commands you use yourself to build, test and verify. I&amp;rsquo;m a big fan of maintaining a single &lt;strong&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/strong&gt; with the most important commands, that the assistant can invoke easily from the command line.&lt;/li&gt;
&lt;li&gt;Real‑time data (&lt;strong&gt;MCP&lt;/strong&gt;): when you need real-time data or connect to external tools, use MCPs. People love to go on about complex MCP setup but don&amp;rsquo;t over index on this. For e.g. instead of a github MCP just install the &lt;code&gt;gh&lt;/code&gt; cli command let the agent run these directly. You can burn tokens if you&amp;rsquo;re not careful with MCPs. But of course, for things like Figma/JIRA where there&amp;rsquo;s no other obvious connection path, use it liberally.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are many other ways, and engineering better context delivery is fast becoming the next frontier in AI development.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="prompt"&gt;
Prompt
&lt;a class="heading-anchor" href="#prompt" aria-label="Link to Prompt"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Think of prompts as specs, not search queries. For example: &amp;lsquo;Write me a unit test for this authentication class&amp;rsquo; 🙅‍♂️.&lt;/p&gt;
&lt;p&gt;Instead of that one‑liner, here&amp;rsquo;s how I would start that same prompt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Persona:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; You&amp;#39;re an expert Android developer, well‑versed in industry norms and testing conventions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; You only use Kotlin, JUnit 5, and Mockito
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Task:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Write the unit tests for @AuthService.kt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Context/Constraints:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Follow the existing testing patterns as demonstrated in @RuleEngineService.kt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Start by writing the tests for the three public methods first - &lt;span style="color:#e6db74"&gt;`login`&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`logout`&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`refreshToken`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Prefer fakes over mocks; if we don&amp;#39;t have a convenient fake class, add one
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Remember, never make real network or database calls in these tests
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Make sure to cover happy paths and error cases as well
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Output:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; AuthServiceTest.kt in folder &amp;lt;src/test/...&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Test names: methodName_condition_expectedResult
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Verify:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Use &lt;span style="color:#e6db74"&gt;`make test AuthService`&lt;/span&gt; to test just this class
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Don&amp;#39;t run lint while iterating; it will take a long time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; I need to hit a code coverage of at least 80%.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; You can check coverage for this class with &lt;span style="color:#e6db74"&gt;`make test-coverage AuthService`&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;First propose a plan before you start making changes or coding. Proceed only after I accept
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I use a text‑expansion snippet, &lt;code&gt;aiprompt;&lt;/code&gt;, almost every single time. It reminds me to structure any prompt:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Persona:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; {cursor}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Task:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Context/Details/Constraints:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Output:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Verify:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This structure forces you to think through the problem and gives the AI what it needs to make good decisions.&lt;/p&gt;
&lt;h3 id="custom-commands"&gt;
Custom commands
&lt;a class="heading-anchor" href="#custom-commands" aria-label="Link to Custom commands"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Writing detailed prompts every single time gets tedious. So you might want to create &amp;ldquo;&lt;a href="https://docs.anthropic.com/claude/docs/claude-code#slash-commands"&gt;command&lt;/a&gt;&amp;rdquo; templates. These are just markdown files that capture your detailed prompts.&lt;/p&gt;
&lt;p&gt;People don&amp;rsquo;t leverage this enough. If your team maintains a shared folder of commands that everyone iterates on, you end up with a powerful set of prompts you can quickly reuse for strong results. I have commands like &lt;code&gt;/write-unit-test.md&lt;/code&gt;, &lt;code&gt;/write-pr-desc.md&lt;/code&gt;, &lt;code&gt;/debug-ticket.md&lt;/code&gt;, &lt;code&gt;/understand-feature.md&lt;/code&gt; etc.&lt;/p&gt;
&lt;h1 id="chunking"&gt;
Chunking
&lt;a class="heading-anchor" href="#chunking" aria-label="Link to Chunking"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;AI agents hit limits: context windows fill up, attention drifts, hallucinations creep in, results suffer. Newer models can run hours‑long coding sessions, but until that&amp;rsquo;s common, the simpler fix is to break work into discrete chunks and plan before coding.&lt;/p&gt;
&lt;p&gt;Many developers miss this. I can&amp;rsquo;t stress how important it is, especially when you&amp;rsquo;re working on longer tasks. My &lt;a href="https://kau.sh/blog/agentic-coding-flow-state/"&gt;post&lt;/a&gt; covers this; it was the single biggest step‑function improvement in my own AI coding practice.&lt;/p&gt;
&lt;p&gt;Briefly, here&amp;rsquo;s how I go about it:&lt;/p&gt;
&lt;h3 id="session-1--plan-only"&gt;
Session 1 — Plan only
&lt;a class="heading-anchor" href="#session-1--plan-only" aria-label="Link to Session 1 — Plan only"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Share the high‑level goal and iterate with the agent&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t write code in this session; use it to tell the agent what it&amp;rsquo;s about to do.&lt;/li&gt;
&lt;li&gt;Once you&amp;rsquo;re convinced, ask the agent to write the plan in detailed markdown in your &lt;code&gt;.ai/plans/&lt;/code&gt; folder&lt;/li&gt;
&lt;li&gt;Reset context before you start executing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="session-2--execute-one-task-at-a-time"&gt;
Session 2+ — Execute one task at a time
&lt;a class="heading-anchor" href="#session-2--execute-one-task-at-a-time" aria-label="Link to Session 2&amp;#43; — Execute one task at a time"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Spawn a fresh agent, load &lt;code&gt;task-1.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Implement only that task, verify &amp;amp; commit.&lt;/li&gt;
&lt;li&gt;Reset or clear your session.&lt;/li&gt;
&lt;li&gt;Proceed to &lt;code&gt;task-2.md&lt;/code&gt; and repeat.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One‑shot requests force the agent to plan and execute simultaneously — which rarely produces great results. If you were to submit these as PRs to your colleagues for review, how would you break them up? You wouldn&amp;rsquo;t ship 10,000 lines, so don&amp;rsquo;t do that with your agents either.&lt;/p&gt;
&lt;p&gt;Plan → chunk → execute → verify.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So the next time you&amp;rsquo;re not getting good results, ask yourself these three things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Am I providing all the necessary context?&lt;/li&gt;
&lt;li&gt;Is my prompt a clear spec?&lt;/li&gt;
&lt;li&gt;Am I executing in small, verifiable chunks?&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I wrote a &lt;a href="https://kau.sh/blog/agents-md/"&gt;post&lt;/a&gt; about this btw, on consolidating these instructions for various agents and tools.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Anthropic&amp;rsquo;s recent &lt;a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents"&gt;post&lt;/a&gt; on &amp;ldquo;context engineering&amp;rdquo; is a good overview of techniques.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/three-req-ai-coding/</guid><pubDate>Sun, 05 Oct 2025 16:54:46 GMT</pubDate></item><item><title>
Vibe-engineering a Firefox add-on: Container Traffic Control</title><link>https://kau.sh/blog/container-traffic-control/</link><description>
&lt;p&gt;I wanted to test a simple claim: you can ship maintainable software by &lt;a href="https://kau.sh/blog/vibe-eng"&gt;vibe-engineering&lt;/a&gt; end to end. I set strict constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;New platform (I haven&amp;rsquo;t built a browser extension/add-on)&lt;/li&gt;
&lt;li&gt;Language I&amp;rsquo;m no longer proficient in (JavaScript)&lt;/li&gt;
&lt;li&gt;Zero manual code editing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In about a day&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; I had a working Firefox add-on I could submit for review. The code meets my bar for readability and long‑term change. Even the icon came from an image model.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Introducing &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ctc/"&gt;Container Traffic Control&lt;/a&gt;.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Install and source
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;• Install: &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ctc/"&gt;Firefox add-on listing&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;• Code: &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control"&gt;github.com/kaushikgopal/ff-container-traffic-control&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure class="rounded-2xl!"&gt;
&lt;div align="center"&gt;
&lt;img src="https://raw.githubusercontent.com/kaushikgopal/ff-container-traffic-control/refs/heads/master/icons/icon.png"
class="rounded-2xl!"
alt="Container Traffic Control Icon"
width="300"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id="on-the-vibe-engineering-process"&gt;
On the vibe-engineering process
&lt;a class="heading-anchor" href="#on-the-vibe-engineering-process" aria-label="Link to On the vibe-engineering process"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s in vogue to share horror stories of decimated vibe-coded repos.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; But I&amp;rsquo;m convinced that with the right fundamentals, you can vibe-engineer&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; a codebase you&amp;rsquo;d comfortably hand to another engineer.&lt;/p&gt;
&lt;p&gt;This was my experiment to vet my feelings on the subject. Granted, this was a small and arguably very simple repository, but I&amp;rsquo;ve also seen success with moderately larger codebases personally.&lt;/p&gt;
&lt;p&gt;It comes down to &lt;strong&gt;scrupulous pruning&lt;/strong&gt;: updating system instructions, diligent prompting, and code review. I plan to write much more about this later, but let&amp;rsquo;s talk about some of the mechanics of how it went:&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t write a single line of JavaScript by hand. When I needed changes, better structure, reusable patterns, small refactors — I asked the agent. The goal throughout was simple: keep the codebase readable and maintainable. It now has a lot of the things we consider important for a decent codebase:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tests (for the important parts)&lt;/li&gt;
&lt;li&gt;Well-organized code&lt;/li&gt;
&lt;li&gt;Clear, useful logging&lt;/li&gt;
&lt;li&gt;Code comments (uses a style called &lt;a href="https://kau.sh/blog/space-shuttle-style-programming/"&gt;space-shuttle style programming&lt;/a&gt;, which I think is increasingly valuable with vibe-engineering)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The best part: most of this came together over two days.&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; Some example pull requests from the repository with the exact prompt I used and the plan that was generated:&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the very first prompt I used to generate the guts of the code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Role/Persona:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; you are an expert javascript developer who is well versed with making firefox add-ons and extensions.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; you have a strong propensity to write clearly legible code with simple to understand code comments.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Task/Goal:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; write the functionality for the firefox add-on &amp;#34;Container Traffic Control&amp;#34; (CTC). I will provide you with the
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;requirements
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; work on first task which is adding the rules, running the validation check, then outputting clear debug logs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Details:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; the container takes in rules from the preferences panel; it does it similarly to how the extension (see
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;screenshot)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; you can find all the relevant code for the extension &amp;#34;conductor&amp;#34; that does something similar
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://github.com/mcortt/Conductor - this is where the screenshot is from
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; where CTC is different from conductor is that CTC takes in a rule with 4 possible fields (each rule will have 4
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fields)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; 1. container name (name of firefox container)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; 2. allow/block (drop down that says this rule should either &amp;#34;block&amp;#34; or &amp;#34;allow&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; 3. url pattern (regex pattern that will match the website url someone is about to navigate to)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; 4. high prioirty (is this rule a high prioirty rule)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; CTC will build a redirectRules object similar to Conductor based on the rules that a user inputs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[Image #1]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; on attempting to save a rule, CTC will always run a &amp;#34;validation check&amp;#34; on all rules:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; same url pattern cannot have more than one high priority container
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; same url pattern cannot have both allow and block
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Allow Only + &lt;span style="color:#e6db74"&gt;`*`&lt;/span&gt; - invalid rule
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;in a future task i will provide how to process these rules. but for now, i would like to achieve the following:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; allow a user to input rules
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; rule is saved if validated
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; on each save, print an output log showing the table or list of rules object
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;the code must be extremely legible, simple and clean
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; avoid overly nesting or including too many classes of fancy javascript
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Context:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; https://github.com/mcortt/Conductor/blob/6daa9ce23f2de0dc43f9e07378da03f141195fec/background.js is the main file
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;from conductor that you cna draw inspiration from
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; i also like the addon https://github.com/kemayo/firefox-sticky-containers/blob/master/background.js which has a
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;clean style of code commenting and details - use this for inspiration in terms of how to write the code, add
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;comments, and debug logs
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; container-redirect is an advanced add-on which provides almost the same set of complexity/functionality that CTC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;will eventually achieve - code can be found here
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://github.com/max-dw-i/container-redirect/blob/master/src/index.js ; you may use this to figure out how
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;containers in firefox work, differnet functionality that can be achieved etc.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;you are allowed to search the codebases of these plugins if necessary.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Output Format:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; i want you to go through the asks
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; if there are things that are unclear, raise them and ask me questions
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; propose a set of tasks or plan on how you intend to go about achieving this feature first.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;once i agree to the plan, i want you to write down a detailed checkpoint document in @.ai/plans/ctc-add-rule.md
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I captured my prompts but wasn&amp;rsquo;t diligent about surfacing them in pull requests; here are a few I did capture:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Here&amp;rsquo;s a &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control/pull/2"&gt;PR&lt;/a&gt; where midway I captured a major feature change: the original version of the add-on used a very different way of capturing the rules. It wasn&amp;rsquo;t as intuitive, so I decided to change it up.&lt;/li&gt;
&lt;li&gt;This was more a &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control/pull/1"&gt;fun one&lt;/a&gt; where I asked it to critique the code as an HN reader would. Some good suggestions came out of it, but the explicit persona callout didn&amp;rsquo;t generate anything helpful in this specific case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code is open source, so go ahead and &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control/"&gt;check it out&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="about-the-add-on-itself"&gt;
About the add-on itself
&lt;a class="heading-anchor" href="#about-the-add-on-itself" aria-label="Link to About the add-on itself"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In my last post, &lt;a href="https://kau.sh/blog/how-to-firefox/#privacy-power-up-containers"&gt;How to Firefox&lt;/a&gt;, I covered &amp;ldquo;Privacy power-up: Containers&amp;rdquo;.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; &amp;ldquo;Containers&amp;rdquo; let you log in to multiple Gmail accounts without separate browser profiles. Add &lt;a href="https://support.mozilla.org/en-US/kb/introducing-total-cookie-protection-standard-mode"&gt;Total Cookie Protection&lt;/a&gt; and you get strong isolation.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s great, but managing it automatically gets tedious fast. Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keep &lt;code&gt;google.com&lt;/code&gt; searches in one container, but open result links in my default container.&lt;/li&gt;
&lt;li&gt;From work Gmail, clicking a GitHub link: if it&amp;rsquo;s &lt;code&gt;github.com/instacart&lt;/code&gt;, open in Work; if it&amp;rsquo;s &lt;code&gt;github.com/kaushikgopal&lt;/code&gt;, open in Personal.&lt;/li&gt;
&lt;li&gt;In Google Docs (Personal), clicking a Sheets or Drive link should stay in Personal — even though my default for Sheets is Work.&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Added these test cases
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;I realized while writing this post I should probably have these exact use cases tested, so I &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control/pull/3"&gt;did just that&lt;/a&gt; right now&amp;hellip; as I continued to flush out this post.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can&amp;rsquo;t achieve this level of control with default containers unless you micromanage every case — and even then, some are impossible. I tried &lt;a href="https://github.com/kintesh/containerise"&gt;various&lt;/a&gt; &lt;a href="https://github.com/mcortt/Conductor/tree/main"&gt;add-ons&lt;/a&gt; but kept hitting cases that &lt;a href="https://github.com/kintesh/containerise/issues/24"&gt;just&lt;/a&gt; &lt;a href="https://github.com/kintesh/containerise/pull/167"&gt;wouldn&amp;rsquo;t&lt;/a&gt; &lt;a href="https://github.com/kintesh/containerise/issues/53"&gt;work&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I built my own. I also prefer how this add-on asks you to set up rules:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/ctc-1.webp"
alt="Rule setup"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/ctc-2.webp"
alt="How the rules work"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Overall, I enjoyed the experiment. I&amp;rsquo;ve been happily using my &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ctc/"&gt;add-on&lt;/a&gt;, and I feel confident that if I needed to make changes, I could do it in what I consider a maintainable codebase.&lt;/p&gt;
&lt;p&gt;Stay tuned for my tips on how you can use AI coding more constructively.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
vibe-coding vs vibe-engineering
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Simon Willison started using the term &lt;a href="https://simonwillison.net/2025/Oct/7/vibe-engineering/"&gt;vibe-engineering&lt;/a&gt; for precisely vibe-coding with this level of rigor. I&amp;rsquo;m trying to adopt this more. You can read more about this distinction in my &lt;a href="https://kau.sh/blog/vibe-eng"&gt;follow up post&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The bulk took a few hours; the rest was tweaks between other work.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://gemini.google/overview/image-generation/"&gt;Google&amp;rsquo;s new 🍌 model&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;which I don&amp;rsquo;t for a second deny exist.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;I intentionally am not using the term vibe-code here.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Honestly, the work put together was probably a few hours. I was issuing commands mostly on the side and going about my business, coming back later when I had time to tweak and re-instruct.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;I&amp;rsquo;ve since updated the post to point to my new Firefox add-on.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/container-traffic-control/</guid><pubDate>Mon, 29 Sep 2025 02:31:35 GMT</pubDate></item><item><title>
Terminal command that tells you which USB-C cable is bad</title><link>https://kau.sh/blog/usbi/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
now includes macOS Tahoe support
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Apple slightly altered the system command for Tahoe&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You have a drawer full of USB cables. Half are junk that barely charge your phone. The other half transfer data at full speed. But which is which?&lt;/p&gt;
&lt;p&gt;Android Studio solved this. Recent versions warn you when you connect a slow cable to your phone:&lt;/p&gt;
&lt;figure class="w-[80%] rounded-2xl!"&gt;
&lt;div align="center"&gt;
&lt;img src="https://developer.android.com/static/studio/images/usb-check.png"
class="w-[80%] rounded-2xl!"
alt="Android Studio Warning"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://developer.android.com/studio/releases/past-releases/as-koala-feature-drop-release-notes#usb-check"&gt;
developers.android.com
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I wanted this for the command line. So I &amp;ldquo;built&amp;rdquo;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;a href="https://gist.github.com/kaushikgopal/7e1555569cd5bb3138cb5ca67bb7a4ae"&gt;&lt;code&gt;usbi&lt;/code&gt;&lt;/a&gt;, a script to check your USB connections.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;The script parses macOS&amp;rsquo;s &lt;code&gt;system_profiler SPUSBHostDataType&lt;/code&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; command, which produces a dense, hard-to-scan raw output:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/usbi-0.webp"
alt="raw output form macos command"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;With a little bit of scripting, the output becomes much cleaner:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/usbi-1.webp"
alt="usbi --speed output sample"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;When I connect my Pixel&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/usbi-2.webp"
alt="usbi output with pixel phone attached"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h2 id="quick-notes-on-the-vibe-coding-experience"&gt;
Quick notes on the vibe-coding experience
&lt;a class="heading-anchor" href="#quick-notes-on-the-vibe-coding-experience" aria-label="Link to Quick notes on the vibe-coding experience"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The first version was a bash script I cobbled together with AI. It worked, but was a mess to maintain. Because I let AI take the wheel, even minor tweaks like changing output colors were difficult.&lt;/p&gt;
&lt;p&gt;Second time around, I decided to vibe-code again but asked AI to rewrite the entire thing in &lt;a href="https://go.dev/"&gt;Go&lt;/a&gt;. I chose Go because I felt I could structure the code more legibly and tweaks would be easier to follow. Go also has the unique ability to compile a cross-platform binary, which I can run on any machine.&lt;/p&gt;
&lt;p&gt;But perhaps the biggest reason is, it took me a grand total of 10 minutes to have AI rewrite the entire thing. I was punching through my email actively as Claude was chugging on the side.&lt;/p&gt;
&lt;p&gt;Two years ago, I wouldn&amp;rsquo;t have bothered with the rewrite, let alone creating the script in the first place. The friction was too high. Now, small utility scripts like this are almost free to build.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s the real story. Not the script, but how AI changes the calculus of what&amp;rsquo;s worth our time.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;yes, vibe coded. Shamelessly, I might add.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;prevoius versoins of macOS used &lt;code&gt;SPUSBDataType&lt;/code&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I had Claude pull specs for the most common Pixel phones. I&amp;rsquo;ll do the same for iPhones if I ever switch back.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/usbi/</guid><pubDate>Tue, 16 Sep 2025 03:50:45 GMT</pubDate></item><item><title>
Google still knows how to really swag</title><link>https://kau.sh/blog/google-swag-tpu/</link><description>
&lt;p&gt;Google still knows how to really swag&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/google-tpuv4-swag.webp"
alt="decommissioned TPU v4 that Google used from their 4th/5th gen AI accelerators"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;</description><guid>https://kau.sh/blog/google-swag-tpu/</guid><pubDate>Fri, 01 Aug 2025 23:38:42 GMT</pubDate></item><item><title>
reclaiming em-en dashing back from AI and lowercasing</title><link>https://kau.sh/blog/reclaim-dash-lowercase-from-ai/</link><description>
&lt;p&gt;AI is transforming our tools, our writing, and — apparently now — our sense of typographic originality. But there are two quirks of my writing that now get me side-eye from friends:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;em and en dashes&lt;/li&gt;
&lt;li&gt;predominant lowercasing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I know, i know. nobody likes the guy who says he liked the band before they got famous. but here we are.&lt;/p&gt;
&lt;p&gt;I was lowercasing and em-en dashing before AI and i&amp;rsquo;d like to claim this back please. my friends roll their eyes when i try. even the polite ones. so, since i can&amp;rsquo;t get a word in at dinner, i&amp;rsquo;ll do it here.&lt;/p&gt;
&lt;h2 id="but-first-receipts"&gt;
But first, receipts…
&lt;a class="heading-anchor" href="#but-first-receipts" aria-label="Link to But first, receipts…"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I need to build some credibility before I attempt to explain myself. one of the advantages of writing this blog for some time now: I can do a quick &lt;code&gt;git log -S&lt;/code&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; and show you some early mention of these &amp;ldquo;quirks&amp;rdquo;:&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://kau.sh/blog/rxjava1-rxjava2-migration-understanding-changes/"&gt;earliest post&lt;/a&gt; I have here using an em dash is from 2017.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/emdashing-before-ai.webp"
alt="a blog post from 2017 where i use an em dash 😎"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://kau.sh/blog/rxjava1-rxjava2-migration-understanding-changes/"&gt;
blog post from 2017 where i use an em dash 😎. click for proof
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;might I remind you, ChatGPT was introduced to this world in &lt;strong&gt;2022&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And now the more egregious one that Sam Altman is attempting to rob me of&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/lowercase-before-ai-cool.webp"
alt="git history of an old version of my blog 11 years ago showing my about page as saying i prefer using lowercase"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
from an old version of my blog from 11 years ago
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h1 id="reclaiming-my-quirks"&gt;
Reclaiming my quirks
&lt;a class="heading-anchor" href="#reclaiming-my-quirks" aria-label="Link to Reclaiming my quirks"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="em--en-dashes"&gt;
em &amp;amp; en dashes
&lt;a class="heading-anchor" href="#em--en-dashes" aria-label="Link to em &amp;amp; en dashes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;listen folks, — &amp;amp; – are beautiful characters that the english language provides us. Strunk &amp;amp; White, who wrote a once &lt;a href="https://amzn.to/3U0G81e"&gt;seminal guide&lt;/a&gt; have this to say:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;A dash is a mark of separation stonger than a comma, less formal than a colon, and more relaxed than parentheses…&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;we cannot let the AI overlords claim these characters theirs. I have a proposal:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Put spaces around your em and en dashes.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;you see when AI genereates text, it tends to do the typographically standard thing—not pad the dash with spaces, like i just demonstrated. but if you did pad it with spaces — it becomes a stylistic choice, and adds visual rhythm to boot.&lt;/p&gt;
&lt;p&gt;we can now use em and en dashes and prove that ChatGPT didn&amp;rsquo;t write this!&lt;/p&gt;
&lt;h2 id="about-the-lowercasing"&gt;
about the lowercasing
&lt;a class="heading-anchor" href="#about-the-lowercasing" aria-label="Link to about the lowercasing"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve mentioned my rsi before and even the &lt;a href="https://kau.sh/blog/karabiner-kt/"&gt;crazy keyboard hacks&lt;/a&gt; to help me with it. one important one is mapping caps-lock to escape. if i have to capitalize my characters often, that&amp;rsquo;s my pinky painfully stretching to one of the ⇧ (shift) keys while typing the character with the other hand. no bueno for me.&lt;/p&gt;
&lt;p&gt;i now implore you: let&amp;rsquo;s normalize not calling people typing in all lowercase as AI zealots proving their humanness.&lt;/p&gt;
&lt;p&gt;yeah, i know this one is a stretch. but my fingers will click-clack their way through the shame.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;very cool git trick to search your history, by the way. yes, I was desperate to find the evidence.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The eagle eye will notice that I &lt;em&gt;sometimes&lt;/em&gt; use proper punctuation. this is purely accidental or most likely my Mac auto-correcting my away.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/reclaim-dash-lowercase-from-ai/</guid><pubDate>Mon, 28 Jul 2025 03:18:36 GMT</pubDate></item><item><title>
Getting Into Flow State with Agentic Coding</title><link>https://kau.sh/blog/agentic-coding-flow-state/</link><description>
&lt;p&gt;I recently found myself in a deep state of flow while coding — the kind where time melts away and you gain real clarity about the software you&amp;rsquo;re building. The difference this time: I was using Claude Code primarily.&lt;/p&gt;
&lt;p&gt;If my &lt;a href="https://kau.sh/tags/ai/"&gt;recent&lt;/a&gt; posts are any indication, I&amp;rsquo;ve been experimenting a lot with AI coding — not just with toy side projects, but high-stakes production code for my day job.&lt;/p&gt;
&lt;p&gt;I have a flow that I think works pretty well. I&amp;rsquo;m documenting it here as a way to hone my own process and, hopefully, benefit others as well.&lt;/p&gt;
&lt;ol start="0"&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-0-set-the-stage"&gt;set the stage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-1-plan-with-the-agent"&gt;plan with the agent&lt;/a&gt; (no really 🤮)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-2-the-fun-part---release-the-agents"&gt;spawn your agents&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-3-verify-and-refactor"&gt;verify and refactor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/agentic-coding-flow-state/#step-4-the-final-review"&gt;the final review&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="w-[75%]"&gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/coding-flow-state.webp"
class="w-[75%]"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="note" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-pencil"&gt;&lt;path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"&gt;&lt;/path&gt;&lt;path d="m15 5 4 4"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Skeptics vs cynics
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Many of my friends and colleagues are understandably skeptical about AI&amp;rsquo;s role in development. That&amp;rsquo;s ok. That&amp;rsquo;s actually good. We &lt;strong&gt;should&lt;/strong&gt; be skeptical of anything that&amp;rsquo;s upending our field with such ferociousness. We just shouldn&amp;rsquo;t be cynical.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id="step-0-set-the-stage"&gt;
Step 0: Set the stage
&lt;a class="heading-anchor" href="#step-0-set-the-stage" aria-label="Link to Step 0: Set the stage"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;You know what&amp;rsquo;ll definitely get you &lt;em&gt;out&lt;/em&gt; of the flow? Having to constantly repeat basic instructions to your agent.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;This is an Android app that does X, using the Y architecture…&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;When styling the front end web client, only use tailwind css v4…&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;Use `make test` to run a single test; `make tests` to run the entire test suite…&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;To build the app without running lint use `make`…&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;…
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These are all very important instructions that you shouldn&amp;rsquo;t have to repeat to your agent every single time. Invest a little upfront in your master ai instructions file. It makes a big difference and gets you up and coding quickly with any agent.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/claude-init.webp"
alt="claude init instructions"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
trivial to get started with claude /init these days
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Of course, I also recommend &lt;a href="https://kau.sh/blog/agents-md/"&gt;consolidating your ai instructions to a single source of truth&lt;/a&gt;, so you&amp;rsquo;re not locked in with any single vendor.&lt;/p&gt;
&lt;h1 id="step-1-plan-with-the-agent"&gt;
Step 1: Plan with the Agent
&lt;a class="heading-anchor" href="#step-1-plan-with-the-agent" aria-label="Link to Step 1: Plan with the Agent"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Update:
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Checkout my post &lt;a href="https://kau.sh/blog/exec-plans"&gt;ExecPlans&lt;/a&gt;. I now use OpenAI&amp;rsquo;s Aaron Friel ExecPlans approach for this.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I won&amp;rsquo;t lie. The idea of this step did not exactly spark joy for me. There are times where I plan my code out in a neat list, but rarely. More often than not, I peruse the code, formulate the plan in my head and let the code guide me. Yeah, that didn&amp;rsquo;t work at all with the agents.&lt;/p&gt;
&lt;p&gt;Two phrases you&amp;rsquo;ll hear thrown around a lot: &amp;ldquo;Context is king&amp;rdquo; and &amp;ldquo;Garbage in, garbage out.&amp;rdquo; This planning phase is how you provide high-quality context to the agent and make sure it doesn&amp;rsquo;t spit garbage out.&lt;/p&gt;
&lt;p&gt;After I got over my unease and explicitly spent time on this step, I started seeing dramatically better results. Importantly, it also allows you to pause and have the agent pick up the work at any later point of your session, without missing a beat. You might be in the flow, but if your agent runs out of tokens, it will lose its flow.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my process:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All my plans live in an &lt;code&gt;.ai/plans/&lt;/code&gt; folder. I treat them as fungible; as soon as a task is done, the plan is deleted.&lt;/li&gt;
&lt;li&gt;Say I&amp;rsquo;m working on a ticket &lt;code&gt;JIRA-1234&lt;/code&gt; and intend to implement it with a stack&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; of 4 PRs.&lt;/li&gt;
&lt;li&gt;Each of those PRs should have a corresponding one plan file: &lt;code&gt;JIRA-1234-1.md&lt;/code&gt;, &lt;code&gt;JIRA-1234-2.md&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;The plan (and PR) should be discrete and implement one aspect of the feature or ticket.&lt;/li&gt;
&lt;li&gt;Of course, I make the agent write all the plans and store it in the folder. (&lt;em&gt;I came this far in my career without being so studious about jira tasks and stories; I wasn&amp;rsquo;t about to start now&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the exact prompt I use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;You are an Expert Task Decomposer. Your entire purpose is to break down complex goals, problems, or ideas into simple, clear, and actionable tasks. You will not answer my request directly; you will instead convert it into one or more execution plans.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;You &lt;span style="font-weight:bold"&gt;**MUST**&lt;/span&gt; follow this three-step interactive process without deviation:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Step 1: Propose a Task List.**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;First, analyze my request and generate a concise, numbered list of the task titles you propose to create. Your first response to me must ONLY be this list. Do NOT write the full plans yet.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;For example, if my request is &amp;#34;I want to create a simple blog,&amp;#34; your first response should be something like:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;#34;Understood. I propose the following tasks:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;1.&lt;/span&gt; Choose and Purchase a Domain Name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;2.&lt;/span&gt; Set Up Web Hosting
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;3.&lt;/span&gt; Install and Configure Content Management System (CMS)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;4.&lt;/span&gt; Design and Customize Blog Theme
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;5.&lt;/span&gt; Write and Publish First Three Blog Posts&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Step 2: Await User Approval.**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After presenting the list, &lt;span style="font-weight:bold"&gt;**STOP**&lt;/span&gt;. Wait for my explicit confirmation. I will respond with something like &amp;#34;Proceed,&amp;#34; &amp;#34;Yes,&amp;#34; or request modifications. Do not proceed until you receive my approval.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Step 3: Get Filename Format &amp;amp; Generate Plans.**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Once I approve the list, you will ask me for the desired filename format. For example: &amp;#34;What filename format would you like? (e.g., &lt;span style="color:#e6db74"&gt;`&amp;lt;YYYY-MM-DD&amp;gt;-&amp;lt;TaskName&amp;gt;.md`&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;`Task-&amp;lt;ID&amp;gt;.txt`&lt;/span&gt;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After I provide the format, you will generate the full, detailed execution plan for EACH approved task. Each plan must be created in a separate, single file, adhering strictly to the &lt;span style="color:#e6db74"&gt;`## OUTPUT FORMAT`&lt;/span&gt; specified below.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;The files should be created in a @.ai/plans folder. If this folder doesn&amp;#39;t exist, ask the user where they need to create it.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## OUTPUT FORMAT
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Use the following markdown template for every plan you generate:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# Task: [A concise name for this specific, atomic task]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Problem:**&lt;/span&gt; [Briefly explain what this specific task is solving or achieving.]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Dependencies:**&lt;/span&gt; [List any other tasks that must be completed first. Write &amp;#34;None&amp;#34; if there are no dependencies.]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Plan:**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;1.&lt;/span&gt; [Clear, explicit Step 1 of the plan]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;2.&lt;/span&gt; [Clear, explicit Step 2 of the plan]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;3.&lt;/span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="font-weight:bold"&gt;**Success Criteria:**&lt;/span&gt; [A simple checklist or a clear statement defining what &amp;#34;done&amp;#34; looks like for this specific task.]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## CRITICAL RULES
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Atomicity:**&lt;/span&gt; Each task must be a single, focused unit of work. If a step in a plan feels too large, it should likely be its own task. Err on the side of creating more, smaller tasks rather than fewer, complex ones.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Clarity:**&lt;/span&gt; Write instructions that are explicit, unambiguous, and can be executed by someone without needing any additional context.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Strict Adherence:**&lt;/span&gt; The interactive workflow is not optional. Always propose the task list first and await my approval before asking for the filename and generating the plans.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Begin now. My first request is:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I have this prompt saved as a &amp;ldquo;plan-tasks.md&amp;rdquo; command in my &lt;code&gt;.ai/commands&lt;/code&gt; folder so I don&amp;rsquo;t have to type this all the time.&lt;/p&gt;
&lt;p&gt;What follows now is a clean back-and-forth conversation with the agent that will culminate with the agent writing down the task plan. Sometimes I may not understand or agree with the task or sequencing, so I&amp;rsquo;ll ask for an explanation and adapt the plan. Other times I&amp;rsquo;m certain a task &lt;em&gt;isn&amp;rsquo;t&lt;/em&gt; needed and I&amp;rsquo;ll ask for it to be removed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is the most crucial step in the entire process&lt;/strong&gt;.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="note" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-pencil"&gt;&lt;path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"&gt;&lt;/path&gt;&lt;path d="m15 5 4 4"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
A note on software engineering experience
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;If the future of software engineering is going to be AI reliant, I believe this planning step is what will distinguish the senior engineers from the junior ones. This is where your experience shines: you can spot the necessary tasks, question the unnecessary ones, challenge the agent&amp;rsquo;s assumptions, and ultimately shape a plan that is both robust and efficient.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I intently prune these tasks and want them to look as close to the sequence I would actually follow myself. To reiterate: the key here is to ask for the plan to be written so that &lt;em&gt;another agent can execute it autonomously&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="step-2-the-fun-part---release-the-agents"&gt;
Step 2: The Fun Part - release the Agents!
&lt;a class="heading-anchor" href="#step-2-the-fun-part---release-the-agents" aria-label="Link to Step 2: The Fun Part - release the Agents!"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After committing all the plans (in your first PR), the real fun begins and we implement &lt;code&gt;JIRA-1234-1.md&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You would think my process now is to spawn multiple agents in parallel and ask them to go execute the other plans as well. Maybe one day, but I&amp;rsquo;m not there yet. Instead, I start to parallelize the work around a &lt;strong&gt;single&lt;/strong&gt; task.&lt;/p&gt;
&lt;p&gt;Taking &lt;code&gt;JIRA-1234-1.md&lt;/code&gt; as an example, I&amp;rsquo;ll spawn three agents simultaneously:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agent 1: The Implementer.&lt;/strong&gt; Executes the core logic laid out in the plan.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent 2: The Tester.&lt;/strong&gt; Writes meaningful tests for the functionality, assuming the implementation will be completed correctly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agent 3: The Documenter.&lt;/strong&gt; Updates my project documentation (typically in &lt;code&gt;.ai/docs&lt;/code&gt;) with the task being executed.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;I want you to execute the plan I provide by spawning multiple agents.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;But before you start the work:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Go through the plan, analyze it and see how it applies to the project.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Come up with your execution steps
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; Confirm the strategy with me
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;-&lt;/span&gt; After i confirm, start execution.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Three agents need to do the work.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;1.&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Agent 1: The Implementer.**&lt;/span&gt; Executes the core logic laid out in the plan.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;2.&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Agent 2: The Tester.**&lt;/span&gt; Writes meaningful tests for the functionality, assuming the implementation will be completed correctly.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;3.&lt;/span&gt; &lt;span style="font-weight:bold"&gt;**Agent 3: The Documenter.**&lt;/span&gt; Updates my project documentation (see /my-feature/README.md in &lt;span style="color:#e6db74"&gt;`.ai/docs`&lt;/span&gt;) with the new feature being executed.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Assume the other agent will complete the work successfully.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that with this approach, the likelihood of the agents causing merge conflicts is incredibly slim. Yet, all three tasks are crucial for delivering high-quality software.&lt;/p&gt;
&lt;p&gt;This is typically when I&amp;rsquo;ll go grab a quick ☕. When I&amp;rsquo;m back, I&amp;rsquo;ll tab through the agents, but I spend most of my time with the implementer. As code is being written, I&amp;rsquo;ll pop open my IDE and review it, improving my own understanding of the surrounding codebase.&lt;/p&gt;
&lt;p&gt;Sometimes I might realize there&amp;rsquo;s a better way to structure something, or a pattern I&amp;rsquo;d missed previously. I don&amp;rsquo;t hesitate to stop the agents, refine the plan, and set them off again.&lt;/p&gt;
&lt;p&gt;Eventually, the agents complete their work. I commit the code as a checkpoint (I&amp;rsquo;m liberal with my &lt;code&gt;git commit&lt;/code&gt;s and use them as checkpoints).&lt;/p&gt;
&lt;h2 id="step-3-verify-and-refactor"&gt;
Step 3: Verify and Refactor
&lt;a class="heading-anchor" href="#step-3-verify-and-refactor" aria-label="Link to Step 3: Verify and Refactor"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I run the tests, and more often than not, something usually fails. This isn&amp;rsquo;t a setback; it&amp;rsquo;s part of the process. I analyze whether the code was wrong or the test was incomplete, switch to that context, and fix it.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="note" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-pencil"&gt;&lt;path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"&gt;&lt;/path&gt;&lt;path d="m15 5 4 4"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Refactor aggressively
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is also where I get aggressive with refactoring. It&amp;rsquo;s a painful but necessary habit for keeping the codebase clean. I still lead the strategy, devising the plan myself, and then direct the agent to execute it or validate my approach. While it usually agrees (which isn&amp;rsquo;t always comforting 🙄), it can sometimes spot details I might have otherwise missed.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Once I&amp;rsquo;m satisfied and the checks pass, I do a final commit and push up a draft PR.&lt;/p&gt;
&lt;h2 id="step-4-the-final-review"&gt;
Step 4: The Final Review
&lt;a class="heading-anchor" href="#step-4-the-final-review" aria-label="Link to Step 4: The Final Review"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I review the code again on GitHub, just as I would for any other PR. Seeing the diff in a different context often helps me spot mistakes I might have missed in the IDE. If it&amp;rsquo;s a small change, I&amp;rsquo;ll make it myself. For anything larger, I&amp;rsquo;ll head back to the agents.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;All the while, I&amp;rsquo;m very much in the flow. I may be writing less of the boilerplate code, but I&amp;rsquo;m still doing a lot of &amp;ldquo;code thinking&amp;rdquo;. I&amp;rsquo;m also not &amp;ldquo;vibe coding&amp;rdquo; by any stretch. If anything, I&amp;rsquo;m spending more time thinking about different approaches to the same problem and working to find a better solution. In this way, I&amp;rsquo;m producing better code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve been building like this for the past few weeks and it&amp;rsquo;s been miraculously effective. Deep work beats busywork, every time. Let agents handle the gruntwork, so you can stay locked in on what matters most.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;lest we get swept by this tide vs surfing smoothly over it&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I absolutely love &lt;a href="https://graphite.dev/guides/how-to-merge-stack-pull-requests-github#understanding-stacked-pull-requests"&gt;deconstructing a feature into multiple stacked PRs&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/agentic-coding-flow-state/</guid><pubDate>Wed, 23 Jul 2025 07:15:47 GMT</pubDate></item><item><title>
Introducing “shorts” for Henry (my Hugo blog engine/theme)</title><link>https://kau.sh/blog/shorts/</link><description>
&lt;p&gt;Introducing &amp;ldquo;shorts&amp;rdquo; for &lt;a href="https://kau.sh/henry"&gt;Henry&lt;/a&gt; (my Hugo blog engine/theme).&lt;/p&gt;
&lt;p&gt;Often, I find myself wanting to post a quick thought or note without the ceremony of a full-blown &amp;ldquo;blog post&amp;rdquo;. That&amp;rsquo;s usually when I&amp;rsquo;d post to &lt;a href="https://bluesky.kau.sh"&gt;Bluesky&lt;/a&gt;, &lt;a href="https://twitter.kau.sh"&gt;X&lt;/a&gt;, &lt;a href="https://threads.kau.sh"&gt;Threads&lt;/a&gt;, or &lt;a href="https://mastodon.kau.sh"&gt;Mastodon&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But as I&amp;rsquo;ve said &lt;a href="https://kau.sh/bluesky-comments-for-hugo/"&gt;before&lt;/a&gt;, I prefer investing in a feed I control. With Henry, I can effortlessly post a quick thought, and share it from a feed I own.&lt;/p&gt;</description><guid>https://kau.sh/blog/shorts/</guid><pubDate>Sun, 20 Jul 2025 00:00:00 GMT</pubDate></item><item><title>
🦊 How to Firefox</title><link>https://kau.sh/blog/how-to-firefox/</link><description>
&lt;p&gt;Chrome finally pulled the trigger on the web&amp;rsquo;s best ad-blocker, &lt;a href="https://www.windowscentral.com/software-apps/browsing/google-is-killing-ublock-origin-here-are-your-options"&gt;uBlock Origin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now that Chrome has hobbled uBO, Firefox — my beloved —&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; is surging again. I want to do my part to convince you to switch to Firefox and show you how I use it.&lt;/p&gt;
&lt;h1 id="why-firefox"&gt;
Why Firefox
&lt;a class="heading-anchor" href="#why-firefox" aria-label="Link to Why Firefox"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s get through the important talking points, in case you need a quick copy paste to convince a friend.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;100% open-source&lt;/li&gt;
&lt;li&gt;Un-enshittify the web&lt;/li&gt;
&lt;li&gt;Android users rejoice&lt;/li&gt;
&lt;li&gt;Customize to your heart&amp;rsquo;s content&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="100-open-source"&gt;
100% open-source
&lt;a class="heading-anchor" href="#100-open-source" aria-label="Link to 100% open-source"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This section can be quick.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a &lt;a href="https://github.com/mozilla-firefox/firefox"&gt;github link&lt;/a&gt; to the source code of the Firefox browser. You can clone the repo, pop open your favorite &lt;a href="https://kau.sh/tags/ai/"&gt;AI&lt;/a&gt; code assistant and start asking questions about your browser - the most important app you use.&lt;/p&gt;
&lt;p&gt;What libraries does their Android app use? &lt;a href="https://github.com/mozilla-firefox/firefox/blob/d17ad8314866c2f95b2a23577e897a0a2ae48cb6/gradle/libs.versions.toml#L4"&gt;libs.versions.toml&lt;/a&gt; boom! Also 8.11.1 on android gradle plugin? not bad Firefox.&lt;/p&gt;
&lt;p&gt;Their license allows you to fork and distribute &lt;a href="https://zen-browser.app/"&gt;alternative&lt;/a&gt; &lt;a href="https://librewolf.net/"&gt;versions&lt;/a&gt;. Vibe code a whole new browser.&lt;/p&gt;
&lt;h2 id="un-enshittify-the-web"&gt;
Un-enshittify the web
&lt;a class="heading-anchor" href="#un-enshittify-the-web" aria-label="Link to Un-enshittify the web"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Most of the web today is &lt;a href="https://www.merriam-webster.com/slang/enshittification"&gt;enshittified&lt;/a&gt; with a cesspool of ads, popups, cookie notices, and tracking scripts. Our primary defense has been ad-blockers, with the most powerful being uBlock Origin.&lt;/p&gt;
&lt;p&gt;uBO relies on community-curated filter lists that play a cat-and-mouse game to zap known ads, trackers, and other digital sludge. But with Chrome controlling the web, Google followed through on its &lt;a href="https://www.theverge.com/2023/11/16/23964509/google-manifest-v3-rollout-ad-blockers"&gt;promise&lt;/a&gt; to kneecap &lt;a href="https://github.com/gorhill/uBlock"&gt;uBO&lt;/a&gt; with Manifest V3, effectively blocking the full version from its extension store.&lt;/p&gt;
&lt;p&gt;Sure, there&amp;rsquo;s uBlock Origin &amp;ldquo;Lite&amp;rdquo; now, which does the same thing, right? &lt;a href="https://www.reddit.com/r/uBlockOrigin/comments/1067als/comment/j3h00xj/"&gt;Nope&lt;/a&gt;:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
uBlock Origin Lite != uBlock Origin
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;Filter lists update only when the extension updates
&lt;ul&gt;
&lt;li&gt;no fetching up to date lists from servers (this is a big one!)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;No custom filters
&lt;ul&gt;
&lt;li&gt;so no &lt;strong&gt;element picker&lt;/strong&gt; which allows you to point and zap&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Many filters are dropped at conversion time due to MV3&amp;rsquo;s limited filter syntax&lt;/li&gt;
&lt;li&gt;No strict-blocked pages&lt;/li&gt;
&lt;li&gt;No per-site switches&lt;/li&gt;
&lt;li&gt;No dynamic filtering&lt;/li&gt;
&lt;li&gt;No importing external lists&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Did you know, &lt;a href="https://github.com/gorhill/uBlock/wiki/uBlock-Origin-works-best-on-Firefox"&gt;uBlock Origin works best on Firefox&lt;/a&gt;. Why not just use the real thing then? My browsing experience is beautiful because I have most of the shit-bits blocked away.&lt;/p&gt;
&lt;p&gt;On my Pixel too.&lt;/p&gt;
&lt;h2 id="android-users-rejoice"&gt;
Android users rejoice
&lt;a class="heading-anchor" href="#android-users-rejoice" aria-label="Link to Android users rejoice"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;With Firefox for Android, you get seamless sync of tabs, bookmarks, passwords between browser and phone.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; Let&amp;rsquo;s face it, Safari between Mac and iPhone is a sublime experience. We can get that with Firefox.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/firefox-tabs.webp"
alt="Firefox syncs tabs from all devices"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h3 id="real-browser-extensions-for-mobile"&gt;
Real browser extensions for mobile
&lt;a class="heading-anchor" href="#real-browser-extensions-for-mobile" aria-label="Link to Real browser extensions for mobile"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Here&amp;rsquo;s something the iPhone isn&amp;rsquo;t getting anytime soon: honest-to-god browser extensions that you use on your desktop, also on your phone. Which means… you can run uBlock Origin on Android, completely &lt;a href="https://news.ycombinator.com/item?id=34377582"&gt;unnerfed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Safari has extensions, but they still require an App Store review for distribution on Apple platforms. They also &lt;a href="https://github.com/uBlockOrigin/uBOL-home/issues/327"&gt;&lt;em&gt;just&lt;/em&gt;&lt;/a&gt; got a version of the uBO &amp;ldquo;Lite&amp;rdquo; extension.&lt;/p&gt;
&lt;h2 id="customize-to-your-hearts-content"&gt;
Customize to your heart&amp;rsquo;s content
&lt;a class="heading-anchor" href="#customize-to-your-hearts-content" aria-label="Link to Customize to your heart’s content"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;But… Firefox doesn&amp;rsquo;t look as clean and minimal as Safari. You can claw the vertical tabs out of my cold dead Arc hands!&lt;/p&gt;
&lt;p&gt;This is what my Firefox browser looks like:&lt;/p&gt;
&lt;figure class="full-bleed"&gt;
&lt;div align="center"&gt;
&lt;img src="https://raw.githubusercontent.com/rakhalfps/gwfox-css/a48c59d71b99a00244582478ce7df9ab29cbfebe/Media/PREVIEW.png"
class="full-bleed"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://github.com/akkva/gwfox"&gt;
well akkva&amp;#39;s of gwfox-css technically
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;It only takes about five minutes and a browser restart to get this look.&lt;/p&gt;
&lt;h1 id="my-firefox-setup"&gt;
My Firefox Setup
&lt;a class="heading-anchor" href="#my-firefox-setup" aria-label="Link to My Firefox Setup"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ll walk you through my setup now, from essential add-ons to privacy tweaks and a few &amp;ldquo;nice-to-have&amp;rdquo; extras.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="warning" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-alert-triangle"&gt;&lt;path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"&gt;&lt;/path&gt;&lt;path d="M12 9v4"&gt;&lt;/path&gt;&lt;path d="M12 17h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Nerd Alert
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is &lt;em&gt;my&lt;/em&gt; setup. I&amp;rsquo;m a nerd, so I find joy in tinkering. You don&amp;rsquo;t need to do all of this, but a few small tweaks can give you a massively better browser.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="the-essentials-ublock-origin"&gt;
The Essentials: uBlock Origin
&lt;a class="heading-anchor" href="#the-essentials-ublock-origin" aria-label="Link to The Essentials: uBlock Origin"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Think of uBO as a powerful, wide-spectrum filter for the web. It uses community-maintained lists to block ads, trackers, cookie notices, and other digital sludge before it ever loads. Your browser stays faster and cleaner.&lt;/p&gt;
&lt;p&gt;It can be confusing to know which filter lists to enable. I follow the advice of a uBO &lt;a href="https://www.reddit.com/r/firefox/comments/1jmkrw1/comment/mtpsoaa/"&gt;wizard on Reddit&lt;/a&gt;, and these settings alone make the web 90% better. Check the same boxes, and you&amp;rsquo;re good to go.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/adblock-filers.webp"
alt="uBO filters to enable"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
You&amp;#39;re completely set with this.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Custom Filters are an exclusive uBO superpower
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;The &amp;ldquo;My filters&amp;rdquo; tab is where you can write your own rules to block nearly anything, from annoying widgets to entire domains.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 id="block-outgoing-network-traffic-to-facebook"&gt;
Block &lt;em&gt;outgoing&lt;/em&gt; network traffic to Facebook
&lt;a class="heading-anchor" href="#block-outgoing-network-traffic-to-facebook" aria-label="Link to Block outgoing network traffic to Facebook"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;For the truly privacy-conscious, uBO can block &lt;em&gt;all outgoing traffic&lt;/em&gt; to specific domains, like Facebook.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; In the past, Firefox&amp;rsquo;s &lt;a href="https://www.mozilla.org/en-US/firefox/facebookcontainer/"&gt;Facebook Container&lt;/a&gt; add-on helped by isolating your Facebook activity. But if any other site embedded a Facebook widget or tracker, your data could still leak to Meta&amp;rsquo;s servers, fingerprinting you even if you never visit Facebook directly.&lt;/p&gt;
&lt;p&gt;With a custom uBO rule, you can sever that connection entirely from non-Facebook sites. This is a level of control other browsers don&amp;rsquo;t offer.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/adblock-filter-custom.webp"
alt="uBO custom filters"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://www.reddit.com/r/firefox/comments/1digcp4/comment/l93oijg/"&gt;
uBO custom filter: courtesy the reddit wizard fsau again
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The other line you see there? That one-liner blocks all those &amp;ldquo;Sign in with Google?&amp;rdquo; pop-ups. This granular control is &lt;strong&gt;only&lt;/strong&gt; possible with the full uBlock Origin, not the &amp;ldquo;Lite&amp;rdquo; version found on other browsers.&lt;/p&gt;
&lt;p&gt;If you want to go deeper, &lt;a href="https://youtu.be/2lisQQmWQkY?si=feAVfrYe7qS5PVL-&amp;amp;t=9"&gt;this video&lt;/a&gt; is a great showcase of its advanced capabilities.&lt;/p&gt;
&lt;h2 id="privacy-power-up-containers"&gt;
Privacy power-up: Containers
&lt;a class="heading-anchor" href="#privacy-power-up-containers" aria-label="Link to Privacy power-up: Containers"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Firefox now includes &lt;a href="https://support.mozilla.org/en-US/kb/introducing-total-cookie-protection-standard-mode"&gt;Total Cookie Protection&lt;/a&gt; (TCP) by default. This automatically isolates cookies to the site that created them, giving each site its own &amp;ldquo;cookie jar&amp;rdquo;. Importantly it means sites aren&amp;rsquo;t allowed to read cookies from other site&amp;rsquo;s jars. This stops trackers from following you across the web.&lt;/p&gt;
&lt;p&gt;Firefox first piloted this feature as the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/"&gt;Multi-Account Containers&lt;/a&gt; (MAC) add-on. But with TCP, the containers feature is somewhat redundant &lt;em&gt;for basic anti-tracking&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;However, the container technology is still incredibly useful if you want to seamlessly manage multiple online identities. Instead of juggling separate browser profiles, you can use &amp;ldquo;Containers&amp;rdquo; to stay logged into two different Gmail accounts (e.g., &amp;ldquo;Work&amp;rdquo; and &amp;ldquo;Personal&amp;rdquo;) in the same browser window, with zero overlap.&lt;/p&gt;
&lt;p&gt;The old MAC add-on made this possible, but it was really clunky to setup. It&amp;rsquo;s actually much easier to do this today without installing an additional add-on.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;go to &lt;code&gt;about:config&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;set &lt;code&gt;privacy.userContext.newTabContainerOnLeftClick.enabled&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s it. This works without the extra MAC add-on because the Container concept is baked natively into Firefox. So with the above config tweaks, you enable the default built-in containers and whenever you click the new tab button, you can choose which container to open. You can now open gmail in these containers and it&amp;rsquo;s as if you&amp;rsquo;re opening an entirely new browser.&lt;/p&gt;
&lt;h3 id="even-more-control"&gt;
even more control?
&lt;a class="heading-anchor" href="#even-more-control" aria-label="Link to even more control?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;But what about links? A work link (like Datadog or Sentry) clicked from your email in a Work container, might open in the default container and use the wrong Google account. You could right click the link and say &amp;ldquo;Open in Container &amp;gt;&amp;rdquo; but that gets old fast. Wouldn&amp;rsquo;t it be nice if you could give Firefox a set of rules and say, always open these websites or URLs in these containers? and everything just worked magically?&lt;/p&gt;
&lt;p&gt;I created the firefox add-on &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/ctc/"&gt;Container Traffic Control&lt;/a&gt; to help with that exact requirement. It&amp;rsquo;s &lt;a href="https://github.com/kaushikgopal/ff-container-traffic-control"&gt;open source&lt;/a&gt; and pretty well documented + tested.&lt;/p&gt;
&lt;p&gt;This combination of the native config tweak and my add-on provides a simple, but more powerful multi-profile setup (than even MAC).&lt;/p&gt;
&lt;h2 id="gravy-add-ons"&gt;
Gravy add-ons
&lt;a class="heading-anchor" href="#gravy-add-ons" aria-label="Link to Gravy add-ons"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;These are also not essential, but they add a nice layer of polish.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://darkreader.org/"&gt;Dark Reader&lt;/a&gt;&lt;/strong&gt;: For a consistent, customizable dark mode on every site.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/styl-us/"&gt;Stylus&lt;/a&gt;&lt;/strong&gt;: To apply custom CSS. I use it to force my &lt;a href="https://kau.sh/tags/my-new-programming-font/"&gt;&lt;/a&gt; on code blocks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/return-youtube-dislikes/"&gt;Return YouTube Dislike&lt;/a&gt;&lt;/strong&gt;: Does what it says on the tin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/web-clipper-obsidian/"&gt;Obsidian Web Clipper&lt;/a&gt;&lt;/strong&gt;: To save notes and clippings directly to Obsidian, from desktop or mobile.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://addons.mozilla.org/en-US/firefox/addon/auto-tab-discard/"&gt;Auto Tab Discard&lt;/a&gt;&lt;/strong&gt;: Suspends background tabs to save RAM. A holdover from my RAM-strapped MacBook days, but it still does its job silently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="other-customizations"&gt;
Other Customizations
&lt;a class="heading-anchor" href="#other-customizations" aria-label="Link to Other Customizations"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Firefox is famously customizable via &lt;code&gt;about:config&lt;/code&gt;. Besides the container tweak, I only use one other:&lt;/p&gt;
&lt;h3 id="browsertabsinsertaftercurrent--true"&gt;
&lt;code&gt;browser.tabs.insertAfterCurrent&lt;/code&gt; → &lt;code&gt;true&lt;/code&gt;
&lt;a class="heading-anchor" href="#browsertabsinsertaftercurrent--true" aria-label="Link to browser.tabs.insertAfterCurrent → true"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;New tabs open next to your current tab, not at the end.&lt;/li&gt;
&lt;li&gt;You catch that &lt;a href="https://daringfireball.net/2018/12/safari_new_tab_next_to_current_tab"&gt;Mr.Gruber&lt;/a&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="hidden-gems"&gt;
Hidden Gems
&lt;a class="heading-anchor" href="#hidden-gems" aria-label="Link to Hidden Gems"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m collecting in this section, some of the cooler Firefox features that&amp;rsquo;ll make you wonder why every browser doesn&amp;rsquo;t have them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Type &lt;code&gt;/&lt;/code&gt; and start typing for quick find (vs ⌘F). But dig this, &lt;code&gt;'&lt;/code&gt; and Firefox will only match text for hyper links&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;If you have an obnoxious site disable right click, just hold Shift and Firefox will bypass and show it to you. No add-one required.&lt;/li&gt;
&lt;li&gt;URL bar search shortcuts: &lt;code&gt;*&lt;/code&gt; for bookmarks, &lt;code&gt;%&lt;/code&gt; for open tabs, &lt;code&gt;^&lt;/code&gt; for history&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The web can still be beautiful. You just need the right tools to see it. Go &lt;a href="https://www.firefox.com/"&gt;download Firefox&lt;/a&gt; and make your web beautiful again.&lt;/p&gt;
&lt;p&gt;If you try this setup or have suggestion, let me know in the comments.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;No, goddammit, AI didn&amp;rsquo;t write this post.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;For the few who have reached this point of the article and furiously questioned why I don&amp;rsquo;t just use Zen browser or Libre.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;You can of course send outgoing traffic from Meta owned websites so Threads etc. still work.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Seriously, try the apostrophe trick. It&amp;rsquo;s a game-changer for keyboard navigation.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/how-to-firefox/</guid><pubDate>Fri, 18 Jul 2025 00:00:00 GMT</pubDate></item><item><title>
AI Programming Paradigms: A Timeline</title><link>https://kau.sh/blog/ai-programming/</link><description>
&lt;p&gt;A developer podcast host recently said they only use AI for autocomplete. This
shocked me.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s two generations behind today&amp;rsquo;s state of the art. This is how the field is
evolving:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I spoke about this topic in episode
&lt;a href="https://fragmentedpodcast.com/episodes/301/"&gt;#301 of Fragmented&lt;/a&gt;. It was
recorded more recently so reflects my more current thoughts on the topic.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;
&lt;iframe width="100%" class="h-[52px] sm:h-[200px] w-full" frameborder="no" scrolling="no" seamless
src="https://player.simplecast.com/aa0766a9-30bd-4a71-bb18-f83e15ef9455?dark=true"&gt;
&lt;/iframe&gt;
&lt;/p&gt;
&lt;p class="mt-2!"&gt;
&lt;a class="download-link font-bold text-right text-sm block" href="https://cdn.simplecast.com/audio/20f35050-e836-44cd-8f7f-fd13e8cb2e44/episodes/aa0766a9-30bd-4a71-bb18-f83e15ef9455/audio/b30427b6-c805-45d4-a98c-5d11bbf2becc/default_tc.mp3?nocache"&gt;Download directly&lt;/a&gt;
&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="summary" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
AI Programming Paradigms at a Glance
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Super autocomplete&lt;/strong&gt;: AI predicts code, not just keywords.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversational Coding&lt;/strong&gt;: Chat with your IDE, direct the AI, iterate
together.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic Coding&lt;/strong&gt;: AI acts independently - runs commands, checks work,
iterates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Simul Agentic&lt;/strong&gt;? : Multiple sub-agents, parallel workflows, working
together.&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id="i-super-autocomplete"&gt;
I. Super autocomplete
&lt;a class="heading-anchor" href="#i-super-autocomplete" aria-label="Link to I. Super autocomplete"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This used to be one of the biggest selling points of certain IDEs like JetBrains
&amp;amp; Visual Studio.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The first wave of AI changed this. IDEs now predict entire classes and logic
blocks, not just keywords. They excel because they feed surrounding code context
to the LLM for relevant suggestions.&lt;/p&gt;
&lt;p&gt;GitHub deserves credit for kicking off the revolution with their Copilot
offering. Here&amp;rsquo;s an early
&lt;a href="https://youtu.be/Tr2YBmecdw4?si=E-n_BqARKVgzqiUk&amp;amp;t=53"&gt;YouTube video&lt;/a&gt; of mine
from June 2022&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; showing it in action.&lt;/p&gt;
&lt;p&gt;Autocomplete keeps advancing. Cursor (today&amp;rsquo;s most popular AI IDE) has
&lt;a href="https://cursor.com/en/blog/tab-update"&gt;&amp;ldquo;Tab&amp;rdquo;&lt;/a&gt;. JetBrains, the pre-AI
autocomplete champion, is building
&lt;a href="https://blog.jetbrains.com/ai/2025/02/why-and-how-jetbrains-built-mellum-the-llm-designed-for-code-completion/"&gt;Mellum&lt;/a&gt;
(an LLM built for code completion).&lt;/p&gt;
&lt;p&gt;This paradigm is alive and well, but it&amp;rsquo;s become table stakes. Most developers
use it, but it&amp;rsquo;s far from the frontier.&lt;/p&gt;
&lt;h1 id="ii--conversational-coding"&gt;
II – Conversational Coding
&lt;a class="heading-anchor" href="#ii--conversational-coding" aria-label="Link to II – Conversational Coding"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;When ChatGPT took the world by storm, a new paradigm emerged. Conversational
coding, where you chat with the AI and pair program together.&lt;/p&gt;
&lt;p&gt;Unlike the autocomplete era, where you trust the AI&amp;rsquo;s suggestions, here you
&lt;strong&gt;direct the AI&lt;/strong&gt;, give it context, and nudge it toward better solutions. This
is arguably what most devs use today and envision when they hear &amp;ldquo;AI
programming&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;It feels magical, productivity jumps are real, and it&amp;rsquo;s hard to think of every
going backwards. &lt;a href="https://cursor.com/"&gt;Cursor&lt;/a&gt; leads this charge: chat with your
IDE and have it make code changes on the fly.&lt;/p&gt;
&lt;p&gt;The challenge with conversational coding though is IDE lock-in. Android
developers love Android Studio, iOS developers love Xcode. Asking them to switch
to Cursor is a big ask. There are workarounds:
&lt;a href="https://firebender.com/"&gt;Firebender&lt;/a&gt; offers Cursor-like functionality to
existing IDEs, JetBrains has &lt;a href="https://www.jetbrains.com/junie/"&gt;Junie&lt;/a&gt; (though
they&amp;rsquo;re late to the party), and Xcode developers&amp;hellip; well, they wait for Apple.&lt;/p&gt;
&lt;p&gt;This constraint is why the next paradigm is starting to shine.&lt;/p&gt;
&lt;h1 id="iii--agentic-coding"&gt;
III – Agentic Coding
&lt;a class="heading-anchor" href="#iii--agentic-coding" aria-label="Link to III – Agentic Coding"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This is the bleeding edge.&lt;/p&gt;
&lt;p&gt;Agentic coding works anywhere you can run commands. Pop open a terminal, fire up
&lt;a href="https://www.anthropic.com/claude-code"&gt;Claude Code&lt;/a&gt;, and let it work
independently. The AI comes up with a plan, confirms the plan, makes changes,
runs tests, fixes errors — all through your existing CLI tools.&lt;/p&gt;
&lt;p&gt;Claude dominates here. The
&lt;a href="https://en.wikipedia.org/wiki/Text-based_user_interface"&gt;TUI&lt;/a&gt; feels retro but
works brilliantly for terminal-heavy development. You give the agent a task. It
runs commands, checks results, iterates. The feedback loop is tight: ask,
execute, verify, repeat.&lt;/p&gt;
&lt;p&gt;The space is exploding. Google&amp;rsquo;s
&lt;a href="https://blog.google/technology/developers/introducing-gemini-cli-open-source-ai-agent/"&gt;Gemini CLI&lt;/a&gt;
runs on Gemini 2.5 Pro with aggressive pricing.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; Cursor too has a
similar take that they call
&lt;a href="https://docs.cursor.com/background-agent"&gt;background agent&lt;/a&gt; mode. There&amp;rsquo;s also
agentic tools outside of the terminal like &lt;a href="https://jules.google/"&gt;Jules&lt;/a&gt;
(Google) and &lt;a href="https://openai.com/codex/"&gt;Codex&lt;/a&gt; (OpenAI) that run cloud agents
with GUIs. But Claude nails the terminal workflow imho.&lt;/p&gt;
&lt;p&gt;The core idea for agentic coding is the same: let the AI act on its own, not
just suggest.&lt;/p&gt;
&lt;p&gt;So what&amp;rsquo;s next?&lt;/p&gt;
&lt;h2 id="iv--simul-agentic-coding"&gt;
IV – Simul Agentic Coding
&lt;a class="heading-anchor" href="#iv--simul-agentic-coding" aria-label="Link to IV – Simul Agentic Coding"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/simul-agent-chess.jpeg"
alt="Chess prodigy playing simul chess with robot agents"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;My guess: we&amp;rsquo;ll move from mono agentic coding to multi sub-agent workflows.&lt;/p&gt;
&lt;p&gt;Claude can already do this today.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; Think of a chess master playing a
&lt;a href="https://en.wikipedia.org/wiki/Simultaneous_exhibition"&gt;(simul)taneous exhibition&lt;/a&gt;
match: multiple agents building features in parallel, coordinating to avoid
merge hell, and you at the center, reviewing each PR in its own tab.&lt;/p&gt;
&lt;p&gt;The field evolves at breakneck speed. By the time you read this, this might feel
like a mainstay. Revolutionary becomes quaint in two years. Today&amp;rsquo;s cutting edge
is tomorrow&amp;rsquo;s baseline.&lt;/p&gt;
&lt;h2 id="what-about-vibe-coding"&gt;
What About Vibe Coding?
&lt;a class="heading-anchor" href="#what-about-vibe-coding" aria-label="Link to What About Vibe Coding?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Ah, the million-dollar question. Where does vibe coding fit?&lt;/p&gt;
&lt;p&gt;&lt;a href="https://karpathy.ai/"&gt;Andrej Karpathy&lt;/a&gt; (AI Programming Yoda) coined
&lt;a href="https://x.com/karpathy/status/1886192184808149383"&gt;this term&lt;/a&gt;. But vibe coding
isn&amp;rsquo;t a paradigm, it&amp;rsquo;s a style. It&amp;rsquo;s about trusting the AI to make big decisions
with minimal guidance. You can apply it to any paradigm above, but the vibe gets
strong in collaborative and agentic modes.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re a programmer wondering where to invest today: don&amp;rsquo;t get too attached
to one specific tool. Try them all liberally. Start getting conversant. It&amp;rsquo;s too
early to say which tool or paradigm will dominate, but AI programming is
inevitable and will probably just be what we call programming.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;not that one. see
&lt;a href="https://learn.microsoft.com/en-us/visualstudio/ide/using-intellisense?view=vs-2022"&gt;intellisense&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Over 2.5 years ago! This is what I mean by &amp;ldquo;2 generations behind&amp;rdquo; (in AI
development time, each year seems to bring a fundamental shift in how we
interact with these tools).&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Google is offering 60 requests per minute and 1,000 requests per day
&lt;strong&gt;completely free&lt;/strong&gt;. For context, Claude’s Pro plan at $20/mo gives you
roughly 10–40 prompts every 5 hours.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;instruct it specifically to &amp;ldquo;use sub-agents for this workflow&amp;rdquo;.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/ai-programming/</guid><pubDate>Tue, 08 Jul 2025 00:00:00 GMT</pubDate></item><item><title>
Keep your AGENTS.md in sync — One Source of Truth for AI Instructions</title><link>https://kau.sh/blog/agents-md/</link><description>
&lt;p&gt;Getting useful results from your AI assistant often comes down to the
instructions you give it. Most developers treat that step casually, then
wonder why the output is mediocre.&lt;/p&gt;
&lt;p&gt;One of the simplest ways&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; to improve this is to keep one set of
master instructions in an &lt;code&gt;AGENTS.md&lt;/code&gt; file. In it, you define persistent rules
that shape every interaction with the agent.&lt;/p&gt;
&lt;p&gt;Writing those instructions once is easy enough. Keeping them in sync is the
annoying part.&lt;/p&gt;
&lt;p&gt;If you use more than one tool, things drift fast. So I keep one repo as the
source of truth for my AI setup, then wire my tools and projects back to it.&lt;/p&gt;
&lt;p&gt;This post is the simple version of that setup.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="info" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Good news
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Most coding tools have since
&lt;a href="https://agents.md"&gt;consolidated around &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; as the standard. That
makes this much simpler than it used to be.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id="how-i-organize-my-ai-instructions"&gt;
How I organize my AI instructions
&lt;a class="heading-anchor" href="#how-i-organize-my-ai-instructions" aria-label="Link to How I organize my AI instructions"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I keep my setup at two levels:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User-level instructions&lt;/strong&gt;: my personal defaults that I want available
everywhere&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Project-level instructions&lt;/strong&gt;: the rules and context that only apply to one
repo&lt;/li&gt;
&lt;/ol&gt;
&lt;!--Every AI tool wants its own special instruction file:
- [Claude Code](https://www.anthropic.com/claude-code): `CLAUDE.md`
- [Cursor](https://cursor.com): `.cursor/rules` and previously `.cursorrules`
- [Codex CLI](https://openai.com/codex/),
[OpenCode](https://opencode.ai/docs/rules/): `AGENTS.md`
- [Gemini CLI](https://github.com/google-gemini/gemini-cli): `GEMINI.md`
- [Amp](https://ampcode.com): `AGENT.md` [^amp]
- [Windsurf](https://windsurf.com): `.windsurfrules`
[^amp]:
conveniently they own the domain https://agents.md and have graciously been
trying to rally everyone behind this
--&gt;
&lt;h2 id="user-level-one-source-of-truth"&gt;
User level: one source of truth
&lt;a class="heading-anchor" href="#user-level-one-source-of-truth" aria-label="Link to User level: one source of truth"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I keep my shared AI setup in
&lt;a href="https://github.com/kaushikgopal/aikado"&gt;a single repo&lt;/a&gt;. That repo holds the
things I want to maintain in one place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reusable &lt;code&gt;skills/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;reusable &lt;code&gt;commands/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;compatibility shims like &lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I wire my local tools back to that repo with one command from the
&lt;a href="https://github.com/kaushikgopal/aikado/blob/master/Makefile"&gt;repo&amp;rsquo;s &lt;code&gt;Makefile&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make setup
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That command creates the symlinks for me. I am not updating the same
instructions by hand for Claude, Codex, and OpenCode. I edit the shared files
once in my repo, and the tools point back to them.&lt;/p&gt;
&lt;p&gt;At the user level, I keep it direct. The repo is the source of truth, so the
tool-specific paths symlink straight to it. For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/.claude/agents -&amp;gt; ~/my-local-directory/aikado/agents
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;~/.config/opencode/agents -&amp;gt; ~/my-local-directory/aikado/agents
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the maintenance story is simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;keep shared defaults in one repo&lt;/li&gt;
&lt;li&gt;symlink tools back to that repo&lt;/li&gt;
&lt;li&gt;edit once&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="project-level-thin-local-scaffolding"&gt;
Project level: thin local scaffolding
&lt;a class="heading-anchor" href="#project-level-thin-local-scaffolding" aria-label="Link to Project level: thin local scaffolding"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For each codebase, I still want a project-local &lt;code&gt;AGENTS.md&lt;/code&gt; and a place
for project-specific assets.&lt;/p&gt;
&lt;p&gt;So when I start a new repo, I run the project setup target from that same
&lt;a href="https://github.com/kaushikgopal/aikado/blob/master/Makefile"&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make setup-project dir&lt;span style="color:#f92672"&gt;=&lt;/span&gt;~/dev/oss/some-repo
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That command creates the local directories and symlinks, and the result looks
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;AGENTS.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CLAUDE.md
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.agents/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agents/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; skills/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commands/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; plans/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; tmp/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.claude/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agents -&amp;gt; ../.agents/agents
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commands -&amp;gt; ../.agents/commands
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; skills -&amp;gt; ../.agents/skills
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.opencode/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; agents -&amp;gt; ../.agents/agents
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;.codex/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; prompts -&amp;gt; ../.agents/commands
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The role of each piece is straightforward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;: the main instructions for this project&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;: just points Claude back at &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.agents/agents/&lt;/code&gt;: project-specific agents shared by Claude and OpenCode&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.agents/skills/&lt;/code&gt;: project-specific skills&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.agents/commands/&lt;/code&gt;: project-specific reusable commands&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.agents/plans/&lt;/code&gt;: execution plans for larger work&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.agents/tmp/&lt;/code&gt;: scratch output for the current session&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That gives me a clean split:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;shared behavior lives in my AI repo.&lt;/li&gt;
&lt;li&gt;project-specific behavior lives in the project&amp;rsquo;s &lt;code&gt;AGENTS.md&lt;/code&gt; and &lt;code&gt;.agents/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I do not force the exact same structure at both levels.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;at the user level, direct symlinks are simpler&lt;/li&gt;
&lt;li&gt;at the project level, &lt;code&gt;.agents/&lt;/code&gt; gives me one obvious place for agents,
skills, commands, plans, and temp files&lt;/li&gt;
&lt;/ul&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Generating your first &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;If you do not already have a good project &lt;code&gt;AGENTS.md&lt;/code&gt;, start with an init
command that analyzes the repo and writes a first draft. I use a custom
&lt;code&gt;/initialize&lt;/code&gt; command for this.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Nested &lt;code&gt;AGENTS.md&lt;/code&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Most AI tools support nested &lt;code&gt;AGENTS.md&lt;/code&gt; files. If one subdirectory needs
extra instructions, add another one there and keep the root file lean.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="warning" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-alert-triangle"&gt;&lt;path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"&gt;&lt;/path&gt;&lt;path d="M12 9v4"&gt;&lt;/path&gt;&lt;path d="M12 17h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Claude still needs a shim
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Most tools now read &lt;code&gt;AGENTS.md&lt;/code&gt; directly. Claude still wants &lt;code&gt;CLAUDE.md&lt;/code&gt;, so I
keep a tiny compatibility file that just says &lt;code&gt;See @AGENTS.md&lt;/code&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="fine-tuning-your-rules"&gt;
Fine-Tuning Your Rules
&lt;a class="heading-anchor" href="#fine-tuning-your-rules" aria-label="Link to Fine-Tuning Your Rules"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;To see what context your AI is actually using, ask it: &lt;em&gt;&amp;ldquo;What custom
instructions or context files are you currently using for this project?&amp;rdquo;&lt;/em&gt; Use
that feedback to prune redundancy and clarify ambiguous rules.&lt;/p&gt;
&lt;h3 id="revisions"&gt;
Revisions
&lt;a class="heading-anchor" href="#revisions" aria-label="Link to Revisions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;2026-03-08:
&lt;ul&gt;
&lt;li&gt;clarify that user-level setup keeps direct symlinks to the &lt;code&gt;aikado&lt;/code&gt; repo&lt;/li&gt;
&lt;li&gt;document the shared project-local &lt;code&gt;.agents/agents&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2026-03-07:
&lt;ul&gt;
&lt;li&gt;simplify the post around the current &lt;code&gt;aikado&lt;/code&gt; workflow&lt;/li&gt;
&lt;li&gt;clarify that shared instructions, skills, and commands live in one source of
truth repo&lt;/li&gt;
&lt;li&gt;focus the setup story on &lt;code&gt;make setup&lt;/code&gt; and &lt;code&gt;make setup-project&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2026-02-17: add &lt;a href="https://opencode.ai"&gt;OpenCode&lt;/a&gt; instructions (natively reads
&lt;code&gt;AGENTS.md&lt;/code&gt; and &lt;code&gt;.agents/skills/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;2026-02-09: migrate &lt;code&gt;.ai&lt;/code&gt; → &lt;code&gt;.agents&lt;/code&gt; (and update symlink examples)&lt;/li&gt;
&lt;li&gt;2026-01-02:
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://agentskills.io/home"&gt;skills is now an open standard&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;cleaned up old instructions for clarity&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2025-10-12: removing artifacts (then &lt;code&gt;.ai/&lt;/code&gt;): rules, checkpoints, docs
&lt;ul&gt;
&lt;li&gt;the notion of ExecPlans makes more sense than checkpoints&lt;/li&gt;
&lt;li&gt;AGENTS.md should have instructions, README.md should have docs&lt;/li&gt;
&lt;li&gt;rules should be incorporated into the AGENTS.md file&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;2025-09-18: switch to AGENTS.md as more tools consolidate around it
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://openai.com/index/introducing-codex/"&gt;Codex supports AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google-gemini/gemini-cli/discussions/1471"&gt;Gemini supports AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio/gemini/agent-files"&gt;Gemini in Android Studio supports AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cursor.com/docs/context/rules#agentsmd"&gt;Cursor supports AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.visualstudio.com/docs/copilot/customization/custom-instructions#_use-an-agentsmd-file-experimental"&gt;VSCode supports AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I wrote a later post distilling the
&lt;a href="https://kau.sh/blog/three-req-ai-coding/"&gt;three things&lt;/a&gt; that matter most for getting good
AI coding results.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/agents-md/</guid><pubDate>Thu, 03 Jul 2025 22:22:12 GMT</pubDate></item><item><title>
AI won't replace good engineers - it'll make them invaluable</title><link>https://kau.sh/blog/ai-engineering-humans/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/ai-conductor.jpeg"
alt="Human conducting an AI orchestra"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I just read a good piece by the Nilenso crew on &lt;a href="https://blog.nilenso.com/blog/2025/05/29/ai-assisted-coding/"&gt;AI-assisted coding for teams that can&amp;rsquo;t get away with vibes&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Blog post title aside, it helped me synthesize my own thoughts better on a question that keeps coming up: will AI replace all software engineers?&amp;quot;.&lt;/p&gt;
&lt;p&gt;Not only won&amp;rsquo;t it replace us, it&amp;rsquo;ll make the best engineers even more valuable.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s why.&lt;/p&gt;
&lt;h2 id="ai-amplifies-what-you-already-have"&gt;
AI amplifies what you already have
&lt;a class="heading-anchor" href="#ai-amplifies-what-you-already-have" aria-label="Link to AI amplifies what you already have"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
AI is a multiplier.
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;To make AI good, get good yourself. AI is a multiplier. If you are a small coefficient, you won&amp;rsquo;t see much gain. If you are a negative coefficient, expect negative gains.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;🎯. The best engineers extract far more value from AI tools. Why? Because they already have the fundamentals that AI needs to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;They communicate technical ideas clearly&lt;/strong&gt; - AI responds to good prompts, and good prompts require clear thinking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They have &lt;em&gt;&amp;ldquo;the mechanic&amp;rsquo;s touch&amp;rdquo;&lt;/em&gt;&lt;/strong&gt; - that intuitive sense for what makes good systems work. They steer AI toward better solutions.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They have strong fundamentals&lt;/strong&gt; - they can quickly evaluate AI suggestions and know when something&amp;rsquo;s off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;They have taste&lt;/strong&gt; - AI mirrors your sensibilities. Experienced engineers get better results because they ask for better things.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&amp;rsquo;ve seen this repeatedly. Give the same AI tool to a junior and senior engineer. The senior gets dramatically better results. Not because they&amp;rsquo;re smarter, but because they know what questions to ask.&lt;/p&gt;
&lt;h2 id="what-helps-humans-helps-ai"&gt;
What helps humans helps AI
&lt;a class="heading-anchor" href="#what-helps-humans-helps-ai" aria-label="Link to What helps humans helps AI"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s where it gets interesting. From the article:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="warning" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-alert-triangle"&gt;&lt;path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"&gt;&lt;/path&gt;&lt;path d="M12 9v4"&gt;&lt;/path&gt;&lt;path d="M12 17h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Messy code confuses AI too
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Our AI coding assistant struggled to complete a task of equal difficulty on the latter codebase when compared to the former! This is likely because the messier codebase was as confusing for the AI as it would be for a human.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Clean architecture, good naming, clear patterns - everything that makes codebases maintainable for humans also makes them effective for AI.&lt;/p&gt;
&lt;p&gt;So if you want to be more effective with AI, you still need all the craftsmanship skills that good software engineers have always needed. The fundamentals matter more now, not less.&lt;/p&gt;
&lt;p&gt;The craftsmen still matter.&lt;/p&gt;
&lt;h2 id="software-engineering-was-never-just-about-coding"&gt;
Software engineering was never just about coding
&lt;a class="heading-anchor" href="#software-engineering-was-never-just-about-coding" aria-label="Link to Software engineering was never just about coding"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
writing isn&amp;rsquo;t a wrist exercise with ink on paper
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Software engineering is not about writing code. Or at least, that&amp;rsquo;s not the defining characteristic, much like how writing is not wrist exercises with ink on paper.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The real value has always been in the decisions: &lt;em&gt;what&lt;/em&gt; to build, &lt;em&gt;how&lt;/em&gt; to architect it, &lt;em&gt;when&lt;/em&gt; to make tradeoffs, &lt;em&gt;why&lt;/em&gt; a particular approach makes sense. The actual typing? That&amp;rsquo;s just execution.&lt;/p&gt;
&lt;p&gt;AI is getting scary good at execution. But judgment calls (understanding context, business requirements, user needs, system constraints) — that&amp;rsquo;s still very much human territory.&lt;/p&gt;
&lt;p&gt;See also Rakhim&amp;rsquo;s post &lt;a href="https://rakhim.exotext.com/programming-vs-coding-vs-software-engineering"&gt;breaking down coding vs. programming vs. software engineering&lt;/a&gt;.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
&lt;a href="https://bsky.app/profile/kau.sh/post/3lnzuzruhr224"&gt;my&lt;/a&gt; &lt;a href="https://x.com/kaushikgopal/status/1917570097390276626"&gt;take&lt;/a&gt; on above
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;and by this definition I think LLM AIs will systematically replace coding first, programming next (😢) and software engineering last.&lt;/p&gt;
&lt;p&gt;so to remain relevant in this new world, imho become a stronger software engineer.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ll leave you with quote from an &lt;a href="https://simonwillison.net/2025/Mar/11/using-llms-for-code/#be-ready-for-the-human-to-take-over"&gt;article&lt;/a&gt; that Simon Willison[^sw] wrote:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="cite" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-quote"&gt;&lt;path d="M3 21c3 0 7-1 7-8V5c0-1.25-.756-2.017-2-2H4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2 1 0 1 0 1 1v1c0 1-1 2-2 2s-1 .008-1 1.031V20c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;path d="M15 21c3 0 7-1 7-8V5c0-1.25-.757-2.017-2-2h-4c-1.25 0-2 .75-2 1.972V11c0 1.25.75 2 2 2h.75c0 2.25.25 4-2.75 4v3c0 1 0 1 1 1z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
LLMs are no replacement for human intuition and experience.
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;I’ve spent enough time with GitHub Actions that I know what LLMs are no replacement for human intuition and experience. kind of things to look for, and in this case it was faster for me to step in and finish the project rather than keep on trying to get there with prompts.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Simon maintains the &lt;a href="https://github.com/simonw/llm"&gt;llm cli&lt;/a&gt; project and a voice you should be following to keep abreast of all the AI news around software engineering.&lt;/p&gt;</description><guid>https://kau.sh/blog/ai-engineering-humans/</guid><pubDate>Sat, 21 Jun 2025 00:00:00 GMT</pubDate></item><item><title>
Configure Karabiner with ease (&amp; Kotlin)</title><link>https://kau.sh/blog/karabiner-kt/</link><description>
&lt;p&gt;If you&amp;rsquo;re obsessed with productivity hacks, you&amp;rsquo;ve probably heard of &lt;a href="https://kau.sh/blog/hacking-your-keyboard/"&gt;Karabiner&lt;/a&gt; – the ultimate tool for keyboard customization on macOS. But maintaining Karabiner&amp;rsquo;s config is a pain. In this post I&amp;rsquo;ll show you a simpler, Kotlin-powered way to wrangle your Karabiner setup, so you don&amp;rsquo;t have to wrestle with massive, unwieldy JSON.&lt;/p&gt;
&lt;h2 id="on-karabiner"&gt;
On Karabiner
&lt;a class="heading-anchor" href="#on-karabiner" aria-label="Link to On Karabiner"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the gist: Karabiner lets you intercept any keystroke and remap it to… well, pretty much anything. Want to turn Caps Lock into a Hyper key? Make your keyboard launch confetti? It&amp;rsquo;s all possible.&lt;/p&gt;
&lt;p&gt;Take the classic usecase: remap Caps Lock to Hyper (⌘⌥⌃⇧) when held, Escape when tapped. That&amp;rsquo;s easy to do but just scratches the surface. With Karabiner, you can build many more customizations that supercharge your workflow.&lt;/p&gt;
&lt;h2 id="maintaining-that-karabinerjson"&gt;
Maintaining that karabiner.json
&lt;a class="heading-anchor" href="#maintaining-that-karabinerjson" aria-label="Link to Maintaining that karabiner.json"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I was watching a &lt;a href="https://www.youtube.com/watch?v=m5MDv9qwhU8"&gt;video&lt;/a&gt; by Max Stoiber where he uses Raycast&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; &amp;amp; Karabiner. But what struck me was how he customized and maintained his karabiner setup.&lt;/p&gt;
&lt;p&gt;Karabiner runs off a single &lt;code&gt;.json&lt;/code&gt; config file. But that .json configuration can become unwieldy and difficult to manage, as your rules grow in complexity. For example: I require a ~2700 line &lt;code&gt;.json&lt;/code&gt; for all my hacks. It&amp;rsquo;s impossible to maintain it as pure json.&lt;/p&gt;
&lt;p&gt;Max &lt;a href="https://github.com/mxstbr/karabiner"&gt;uses&lt;/a&gt; TypeScript to maintain his rules -which then compiles down to a .json file- that Karabiner can consume. I really like this approach. If you read my &lt;a href="https://kau.sh/blog/hacking-your-keyboard/"&gt;previous blog post&lt;/a&gt;, I also ran into a very similar problem and used goku, which in turn used the very esoteric and terse &lt;a href="https://github.com/edn-format/edn"&gt;edn&lt;/a&gt; format.&lt;/p&gt;
&lt;p&gt;I like TypeScript over edn but you know what I&amp;rsquo;d like even better? &lt;a href="https://kotlinlang.org/"&gt;Kotlin&lt;/a&gt;! So on a ✈️ ride back home, I decided to whip up &lt;a href="https://github.com/kaushikgopal/karabiner-kt"&gt;karabiner-kt&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m really happy with my solution. Kotlin affords a much more pleasant DSL than most other&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; languages.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what a Karabiner rule looks like in Kotlin :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;karabinerRule {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description = &lt;span style="color:#e6db74"&gt;&amp;#34;Right Cmd -&amp;gt; Ctrl (Enter alone)&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = RightCommand
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = RightControl
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKeyIfAlone = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.ReturnOrEnter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; forDevice { identifiers = &lt;span style="color:#a6e22e"&gt;DeviceIdentifier&lt;/span&gt;.APPLE_KEYBOARDS }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clean, expressive, and type-safe!&lt;/p&gt;
&lt;p&gt;Or here&amp;rsquo;s a fun one. Hold the &amp;ldquo;O&amp;rdquo; key and tap &amp;ldquo;0&amp;rdquo; for showing the Raycast Confetti:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;karabinerRuleSingle {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description = &lt;span style="color:#e6db74"&gt;&amp;#34;O + 0 -&amp;gt; Raycast Confetti&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; layerKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.O
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Num0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; shellCommand = &lt;span style="color:#e6db74"&gt;&amp;#34;open raycast://extensions/raycast/raycast/confetti&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;},
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="https://kau.sh/videos/content/confetti-raycast.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;Here&amp;rsquo;s a slightly more complex modification: If I hold the &amp;ldquo;f&amp;rdquo; key and tap j/k, it types out an open and closed bracket respectively.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;karabinerRule {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; description = &lt;span style="color:#e6db74"&gt;&amp;#34;F-key layer mappings&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; layerKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.F
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// J K
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// ( )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.J
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Num9
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toModifiers = listOf(LeftShift)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.K
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Num0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toModifiers = listOf(LeftShift)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// M ,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// [ ]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.M
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.OpenBracket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Comma
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.CloseBracket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// . /
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// { }
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Period
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.OpenBracket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toModifiers = listOf(LeftShift)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mapping {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; fromKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.Slash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toKey = &lt;span style="color:#a6e22e"&gt;KeyCode&lt;/span&gt;.CloseBracket
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; toModifiers = listOf(LeftShift)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="installation"&gt;
Installation
&lt;a class="heading-anchor" href="#installation" aria-label="Link to Installation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/kaushikgopal/karabiner-kt"&gt;repo is open source&lt;/a&gt;, so feel free to take a look and customize.&lt;/p&gt;
&lt;p&gt;Getting started is easy. Here&amp;rsquo;s how to set it up from scratch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install karabiner-elements&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install --cask karabiner-elements
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# app uses a simple gradle app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install gradle
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# let&amp;#39;s clean up the karabiner folder if they existed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rm -rf ~/.config/karabiner
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p ~/.config/karabiner
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# clone the repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/kaushikgopal/karabiner-kt.git ~/.config/karabiner/karabiner-kt
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="run-the-configurator"&gt;
Run the configurator
&lt;a class="heading-anchor" href="#run-the-configurator" aria-label="Link to Run the configurator"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Now every time you want to run the configurator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd ~/.config/karabiner/karabiner-kt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# you might have to do this once a while (if you restart your mac etc.)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make restart-karabiner
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="try-your-own-rules"&gt;
Try your own Rules!
&lt;a class="heading-anchor" href="#try-your-own-rules" aria-label="Link to Try your own Rules!"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;All your customizations live in &lt;a href="https://github.com/kaushikgopal/karabiner-kt/blob/master/app/src/main/kotlin/sh/kau/karabiner/Rules.kt"&gt;Rules.kt&lt;/a&gt;. My own config is about 300 lines of Kotlin. Compare that to the monstrous &lt;a href="https://github.com/kaushikgopal/karabiner-kt/blob/master/app/karabiner.json"&gt;2736-line JSON&lt;/a&gt; it generates. 😅&lt;/p&gt;
&lt;p&gt;You should start with a &lt;a href="https://github.com/kaushikgopal/karabiner-kt/blob/master/app/src/main/kotlin/sh/kau/karabiner/Rules.sample.kt"&gt;really simple &lt;/a&gt; setup and build your Rules over time. &lt;a href="https://github.com/kaushikgopal/karabiner-kt"&gt;Give it a shot&lt;/a&gt; and let me know if you run into &lt;a href="https://github.com/kaushikgopal/karabiner-kt/issues"&gt;issues&lt;/a&gt;!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;my &lt;a href="https://www.raycast.com/"&gt;launcher&lt;/a&gt; of choice.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;maybe TypeScript allows this but I understand Kotlin better&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/karabiner-kt/</guid><pubDate>Sat, 17 May 2025 20:23:40 GMT</pubDate></item><item><title>
Taildrop - transfer files between Android and MacOS</title><link>https://kau.sh/blog/taildrop/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/taildrop.webp"
alt="Taildrop notification"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I use the &lt;a href="https://en.wikipedia.org/wiki/Pixel_9"&gt;Pixel 9 Pro&lt;/a&gt; as my daily driver and love it. But one notable feature I&amp;rsquo;ve always missed from the Apple ecosystem is &lt;a href="https://support.apple.com/en-us/119857"&gt;AirDrop&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I typically have WhatsApp for Mac open and dump things there for quick access between devices. Clunky, but it worked.&lt;/p&gt;
&lt;p&gt;Until I realized &lt;a href="https://tailscale.com/"&gt;Tailscale&lt;/a&gt; — a VPN&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; that I love and use — has a feature called &lt;a href="https://tailscale.com/kb/1106/taildrop"&gt;Taildrop&lt;/a&gt;. The name suggests it&amp;rsquo;s meant as an AirDrop competitor, and I&amp;rsquo;ve been super happy with it.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/taildrop-2.webp"
width="300"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I can now send images uncompressed&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to any of my devices. I&amp;rsquo;m not restricted by Bluetooth, proximity, or other limitations. Just need Tailscale running on the device.&lt;/p&gt;
&lt;h2 id="advantages"&gt;
Advantages
&lt;a class="heading-anchor" href="#advantages" aria-label="Link to Advantages"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="not-limited-to-the-apple-ecosystem"&gt;
Not limited to the Apple ecosystem
&lt;a class="heading-anchor" href="#not-limited-to-the-apple-ecosystem" aria-label="Link to Not limited to the Apple ecosystem"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Probably the highlight here is that Taildrop works on any device that supports Tailscale. So between a Pixel, iPhone, MacBook, Windows machine — no walls here.&lt;/p&gt;
&lt;h3 id="unlimited-file-size"&gt;
Unlimited file size
&lt;a class="heading-anchor" href="#unlimited-file-size" aria-label="Link to Unlimited file size"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;There&amp;rsquo;s no filesize limit. In all fairness, AirDrop doesn&amp;rsquo;t have one either.&lt;/p&gt;
&lt;h3 id="taildrive"&gt;
Taildrive
&lt;a class="heading-anchor" href="#taildrive" aria-label="Link to Taildrive"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Another alpha feature they&amp;rsquo;re working on, similar to Taildrop, is &lt;a href="https://tailscale.com/kb/1369/taildrive"&gt;Taildrive&lt;/a&gt;. It&amp;rsquo;s effectively a shared folder on the internet. If you&amp;rsquo;re on the same network (Tailnet), you can download files from this folder from anywhere.&lt;/p&gt;
&lt;p&gt;This basically allows any folder on your device to become a mini NAS or file server. Bonkers!&lt;/p&gt;
&lt;h2 id="constraints"&gt;
Constraints
&lt;a class="heading-anchor" href="#constraints" aria-label="Link to Constraints"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s not all sunshine and rainbows though. There are some constraints and issues with Taildrop.&lt;/p&gt;
&lt;h3 id="can-only-send-to-yourself"&gt;
Can only send to yourself
&lt;a class="heading-anchor" href="#can-only-send-to-yourself" aria-label="Link to Can only send to yourself"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Even if you add another user to your Tailscale network, you can still only send files to yourself. I imagine this is still an early restriction, but the good news is that Tailscale is thinking about &lt;a href="https://www.reddit.com/r/Tailscale/comments/16c0wwf/comment/jzgpkai/"&gt;relaxing this over time&lt;/a&gt; (maybe).&lt;/p&gt;
&lt;p&gt;This is clearly an area where AirDrop is superior since it can more conveniently allow you to send files to anyone nearby.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note this doesn&amp;rsquo;t apply to Taildrive, just to Taildrop. With Taildrive, anyone on your Tailnet can access the shared files.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="can-only-send-files"&gt;
Can only send files
&lt;a class="heading-anchor" href="#can-only-send-files" aria-label="Link to Can only send files"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;AirDrop handles sharing URLs or links elegantly. Share a URL from your phone to computer, and it automatically opens in your default browser. You can&amp;rsquo;t share links with Taildrop yet.&lt;/p&gt;
&lt;h3 id="its-a-little-buggy"&gt;
It&amp;rsquo;s a little buggy
&lt;a class="heading-anchor" href="#its-a-little-buggy" aria-label="Link to It’s a little buggy"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;When Taildropping from Android, I&amp;rsquo;ve noticed it occasionally caches the &lt;em&gt;previous&lt;/em&gt; files that were sent. If you&amp;rsquo;re not carefully watching the preview, you can end up sending the wrong file.&lt;/p&gt;
&lt;p&gt;In all fairness, Tailscale has labeled it as an alpha, so they get a pass on this. Their solutions are typically rock solid, so I fully trust when they graduate it from alpha, it&amp;rsquo;ll be production ready.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Despite these limitations, Taildrop has become an essential tool in my cross-device workflow. For Android users longing for AirDrop-like functionality, it&amp;rsquo;s worth setting up Tailscale for this feature alone. And if you&amp;rsquo;re already using Tailscale for &lt;a href="https://kau.sh/blog/mac-mini-tailscale-benefits-tips-vpn-vps/"&gt;other purposes&lt;/a&gt;, enabling Taildrop is a no-brainer.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;VPN might conjure up a different picture here. Think of it simply as a secure tunnel to any of your other devices.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;One of the notable problems with my lazy WhatsApp strategy.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/taildrop/</guid><pubDate>Thu, 01 May 2025 15:03:32 GMT</pubDate></item><item><title>
Why I still podcast for Fragmented</title><link>https://kau.sh/blog/fragmented-10/</link><description>
&lt;p&gt;When you&amp;rsquo;ve been doing a &lt;a href="https://fragmentedpodcast.com/"&gt;podcast&lt;/a&gt; for so long and it&amp;rsquo;s a pure labor of love –time and again– you start to ask yourself: why am I doing this again?&lt;/p&gt;
&lt;p&gt;This is a note for myself and others, who sometimes worry about the podcast.&lt;/p&gt;
&lt;h2 id="positive-listener-feedback"&gt;
Positive listener feedback
&lt;a class="heading-anchor" href="#positive-listener-feedback" aria-label="Link to Positive listener feedback"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Probably, the single most influential reason for doing it is those 2 or 3 notes or messages I get over the year when someone says that the podcast helped change the course of their life as a developer.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll be honest. I didn&amp;rsquo;t think that would be the reason, when I started. I couldn&amp;rsquo;t fathom it would have that effect &lt;em&gt;on me&lt;/em&gt; if I&amp;rsquo;m being even more honest. But every time I&amp;rsquo;ve hit a slump, it takes one email, a YouTube comment or a random tweet or Bluesky post to just reinject that dose of enthusiasm I need, to keep going.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve had some incredible people with incredible stories write to me saying - they didn&amp;rsquo;t have money to be a developer and they used the podcast to learn while they saved up to eventually buy a phone. The only phone they could buy was an Android one and that allowed them to build their very first app and get a job in the process. Some have even written about being the first person in their family to use a computer and become a mobile developer by listening to Fragmented. I&amp;rsquo;ve had folks tell me they managed to get the dream jobs of their life because of the topics we covered and them being able to get through interviews because of that knowledge. I&amp;rsquo;ve had others write in saying they got promoted as a mobile developer, because of ideas they got listening to the podcast landing the impact and convincing their bosses.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m really not trying to sound more important here but even getting one of those messages, even the simple thanks for doing this! immediately gives me a kick in the butt.&lt;/p&gt;
&lt;p&gt;We&amp;rsquo;re all searching for purpose and meaning. Being able to help people in this small way is a privilege, I don&amp;rsquo;t take lightly.&lt;/p&gt;
&lt;h2 id="my-own-learning"&gt;
My own learning
&lt;a class="heading-anchor" href="#my-own-learning" aria-label="Link to My own learning"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;But to say, I only do this for the people would be disingenuous. The second most important reason is my own learning.&lt;/p&gt;
&lt;p&gt;Having to talk about a topic or even interview an expert requires me to research a topic to a level of depth where I need to be able to grok it. In my job at Instacart, I no longer am paid just to code,&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; but as my manager repeatedly tells me - my job is to make the right calls at the right time.&lt;/p&gt;
&lt;p&gt;Fragmented has allowed me to do that better. I don&amp;rsquo;t know how much self motivation I&amp;rsquo;d have personally to research every single topic, just for the curiosity of it. I know others who do and I remain envious of them.&lt;/p&gt;
&lt;p&gt;This is also one really good reason why I constantly encourage people to write more blog posts about topics they&amp;rsquo;ve learned or create content about it in some way. It doesn&amp;rsquo;t matter that one of the experts you know has already written about it. It doesn&amp;rsquo;t matter that Google has an indepth guide. What matters the most is explaining it through your voice and your own transformative learning through the process.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="access-to-android-community"&gt;
Access to Android community
&lt;a class="heading-anchor" href="#access-to-android-community" aria-label="Link to Access to Android community"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Lastly of course, the podcast is the reason I personally have access to some of the best Android developers and folks at Google.&lt;/p&gt;
&lt;p&gt;If I don&amp;rsquo;t know something or cannot understand a topic, I can usually reach out to the author or the people who wrote the framework and get an answer. Again, I fully recognize this privilege.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve managed to chat with some incredibly smart people working at Google on Android OS because they recognize Fragmented.&lt;/p&gt;
&lt;p&gt;Also, Fragmented is solely the reason I remain a &lt;a href="https://g.dev/kaushikgopal"&gt;GDE&lt;/a&gt;. Fragmented is the reason anyone in the community even recognizes my name. Funnily enough I&amp;rsquo;m mostly not recognized at Google I/O until I&amp;rsquo;m striking a conversation with someone, typically when I&amp;rsquo;m in a group with one of the other more famous faces, and people usually get the 💡 moment after hearing me converse with them for a few minutes.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;It&amp;rsquo;s been the privilege of my life to keep working on Fragmented. &lt;a href="https://www.donnfelker.com/"&gt;Donn&lt;/a&gt; and I put in an incredible number of extra hours into it, but we also got really lucky and I cannot be more grateful to the listeners we have.&lt;/p&gt;
&lt;p&gt;Fragmented hit its 10 year mark 🥲 somewhat recently.&lt;/p&gt;
&lt;p&gt;It evolves in different shapes and forms. On its most recent iteration with Donn &lt;a href="https://fragmentedpodcast.com/episodes/250/"&gt;bowing&lt;/a&gt; out, I&amp;rsquo;ve switched to a seasonal approach. Research for sometime and come out with a slew of episodes. Take the time to research some more, and come out with another set.&lt;/p&gt;
&lt;p&gt;As I said, it is a privilege to keep going but Fragmented also has to evolve with me, as I hit a big personal milestone myself this year! If you&amp;rsquo;re a listener, 🙇‍♂️ thank you for your ears.&lt;/p&gt;
&lt;p&gt;This is the email I sent Donn - 28th Jan 2015.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/fragmented-first-email.webp"
alt="first email i sent Donn proposing Fragmented"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Feb 17th 2015 is when we released &lt;a href="https://fragmentedpodcast.com/episodes/1/"&gt;our very first episode&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;to be clear I still do a healthy amount of it, to keep my own sanity.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I cannot say the number of times I&amp;rsquo;ve opted to instead read someone&amp;rsquo;s blog post over the technical docs, when getting up to speed on a topic. Blog posts by humans have a way of authentically trimming down all the fat and highlighting what&amp;rsquo;s important. Producing your own content even if it&amp;rsquo;s a blog post, giving a talk, or writing a note to yourself is one of the most powerful ways of learning.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/fragmented-10/</guid><pubDate>Tue, 08 Apr 2025 14:58:03 GMT</pubDate></item><item><title>
🖤 Automatic Dark mode for Hugo using Tailwind CSS</title><link>https://kau.sh/blog/dark-mode-hugo-henry-tailwind/</link><description>
&lt;p&gt;On a ✈️ back home, I decided to add Dark mode support to &lt;a href="https://kau.sh/henry"&gt;Henry&lt;/a&gt;. Having recently &lt;a href="https://bsky.app/profile/kau.sh/post/3llon57bvts2g"&gt;upgraded&lt;/a&gt; to Tailwind CSS 4.0, this was a piece of cake to do.&lt;/p&gt;
&lt;p&gt;I leverage the widely &lt;a href="https://caniuse.com/?search=prefers-color-scheme"&gt;supported&lt;/a&gt; CSS @media &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme"&gt;prefers-color-scheme&lt;/a&gt; feature and Tailwind&amp;rsquo;s &lt;a href="https://tailwindcss.com/docs/dark-mode"&gt;native&lt;/a&gt; dark-mode support.&lt;/p&gt;
&lt;p&gt;The implementation only required a few changes:&lt;/p&gt;
&lt;h3 id="1-update-main-themecss"&gt;
1. update main theme.css
&lt;a class="heading-anchor" href="#1-update-main-themecss" aria-label="Link to 1. update main theme.css"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In my main &lt;code&gt;theme.css&lt;/code&gt; file where I declare the existing colors, I add the CSS &lt;code&gt;@media&lt;/code&gt; selector for the &amp;ldquo;dark&amp;rdquo; variable indicator.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@&lt;span style="color:#66d9ef"&gt;theme&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* henry background */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;--color-hbg&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#f92672"&gt;--color-slate-50&lt;/span&gt;&lt;span style="color:#f92672"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;--color-hbg-dark&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;var&lt;/span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#f92672"&gt;--color-slate-200&lt;/span&gt;&lt;span style="color:#f92672"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; @&lt;span style="color:#66d9ef"&gt;media&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#f92672"&gt;prefers-color-scheme&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;dark&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;dark&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* henry background */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --color-hbg: &lt;span style="color:#ae81ff"&gt;#1c2b33&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --color-hbg-dark: &lt;span style="color:#ae81ff"&gt;#152027&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-update-base-html-body"&gt;
2. update base html body
&lt;a class="heading-anchor" href="#2-update-base-html-body" aria-label="Link to 2. update base html body"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In my root html page, I simply add the &amp;ldquo;dark&amp;rdquo; variable indicator class&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;html&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;dark&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now CSS automatically knows to pull in the dark variant of colors when the system indicates light/dark mode.&lt;/p&gt;
&lt;p&gt;There is no step 3!&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="https://kau.sh/videos/content/henry-dark-mode.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;This was so easy to do, in fact, that I even managed to catch up on &lt;a href="https://www.themoviedb.org/movie/558449-gladiator-ii"&gt;Gladiator II&lt;/a&gt; on the plane ride which I personally give a &lt;a href="https://kau.sh/blog/movie-rating-system/"&gt;solid 3.0&lt;/a&gt; just for Denzel.&lt;/p&gt;</description><guid>https://kau.sh/blog/dark-mode-hugo-henry-tailwind/</guid><pubDate>Fri, 04 Apr 2025 23:01:08 GMT</pubDate></item><item><title>
AirPods Max: now great for podcasters with 0 latency!</title><link>https://kau.sh/blog/airpods-max-for-podcasters/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Update is now out
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;The latest firmware update is out now. Update your Mac (iPhone or iPad) to the latest version &amp;amp; connect your Airpods Max should &lt;a href="https://support.apple.com/en-us/106340"&gt;update its firmware&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Apple just &lt;a href="https://www.apple.com/newsroom/2025/03/lossless-audio-and-ultra-low-latency-audio-come-to-airpods-max/"&gt;announced&lt;/a&gt; ultra-low latency support for the USB-C AirPods Max, and it changes everything for podcasters.&lt;/p&gt;
&lt;p&gt;Yes, they still cost an absurd $500. But now they&amp;rsquo;re worth their weight in starlight&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; gold.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://www.apple.com/v/airpods-max/i/images/overview/media-card/colors_static__vc9xxywyt4ya_large_2x.jpg"
alt="AirPods Max headphone colors"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://www.apple.com/airpods-max/"&gt;
Courtesy: Apple.com
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="why-airpods-max-are-now-perfect-for-podcasting"&gt;
Why AirPods Max Are Now Perfect for Podcasting
&lt;a class="heading-anchor" href="#why-airpods-max-are-now-perfect-for-podcasting" aria-label="Link to Why AirPods Max Are Now Perfect for Podcasting"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the part that&amp;rsquo;s changing: &lt;strong&gt;you can now &amp;ldquo;monitor&amp;rdquo; your own voice in real-time&lt;/strong&gt; while speaking into a mic and recording. This is an important feature for podcasters, to make sure they&amp;rsquo;re speaking into the mic clearly.&lt;/p&gt;
&lt;p&gt;Until now, I needed two separate headphones: my trusty &lt;a href="https://amzn.to/4l9yC06"&gt;Sony MDR 7506&lt;/a&gt; while recording my podcast for monitoring purposes and more fun sounding ones for everything else. This update eliminates that hassle of needing two headphones on my desk.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This single update transforms the AirPods Max from luxury listening headphones into professional podcasting gear.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s some notes that are also not immediately clear from their announcement but super exciting:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No special cable needed&lt;/strong&gt;: The standard USB-C cable that came in the box will work perfectly for all this. Importantly you don&amp;rsquo;t need that &lt;a href="https://www.apple.com/shop/product/MDV84AM/A/usb-c-to-35-mm-audio-cable-12-m"&gt;$40 cable&lt;/a&gt; everyone is complaining about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wired mode&lt;/strong&gt;: You can use your headphones in wired mode. They &lt;em&gt;should&lt;/em&gt; charge and play audio in lossles quality. This is because the signal chain is digital all the way until they produce sound in your ears.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The $40 cable is still useful if you want to connect to an audio jack but I honestly prefer not having a special cable that&amp;rsquo;s not USB-C while traveling.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="not-the-best-sounding-but-still-worth-it"&gt;
Not the best sounding but still worth it
&lt;a class="heading-anchor" href="#not-the-best-sounding-but-still-worth-it" aria-label="Link to Not the best sounding but still worth it"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The AirPods Max are not the best &lt;em&gt;sounding&lt;/em&gt; headphones for your money but they are good enough. Like really really good enough.&lt;/p&gt;
&lt;p&gt;They are also pretty comofortable (but can be slighlty heavy for some). I&amp;rsquo;m able to wear these headphones for 6 hours straight comfortably.&lt;/p&gt;
&lt;p&gt;These are one of the best in noise cancelation. This is especially a godsend for me when I&amp;rsquo;m in a flight with crying babies and can&amp;rsquo;t do anything about it. Turn NC on and the volume ever so slightly up, and I can drown out all the noise.&lt;/p&gt;
&lt;p&gt;That good USB-C one cable life baby. My phone, laptop and now headphones all can use the same cable.&lt;/p&gt;
&lt;p&gt;For podcasters looking to simplify their setup without sacrificing quality, the AirPods Max now are an excellent choice.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://amzn.to/4j9v3FF"&gt;Amazon&lt;/a&gt; currently has the Starlight color for $479 (lowest price I&amp;rsquo;ve seen). I personally bought mine from Costco because I also know I can return them if there&amp;rsquo;s a manufacturing defect without needing to shell out for that AppleCare+ surcharge.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;The Sony MDR 7506 are good for recording but are &lt;a href="https://www.headphonesty.com/2021/06/sound-signature/"&gt;flat&lt;/a&gt;. Them being wired also means I&amp;rsquo;m tethered to my computer when I just want to do some casual listening.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Use the compact &lt;a href="https://amzn.to/3FKCNQ1"&gt;Twelve South AirFly SE instead&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/airpods-max-for-podcasters/</guid><pubDate>Sat, 29 Mar 2025 03:36:53 GMT</pubDate></item><item><title>
AI &amp; Software Engineering: Time for optimism</title><link>https://kau.sh/blog/ai-learning/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/ai-learning-woman-software-engineer.webp"
alt="software engineering using AI for learning in a library"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Many software engineers (myself included, not long ago) have been anxious about AI replacing our jobs. It&amp;rsquo;s a natural concern.&lt;/p&gt;
&lt;p&gt;But I&amp;rsquo;ve been diving deep into using AI for software engineering. I&amp;rsquo;ve had a realization:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We should stop worrying about job losses. Start seeing AI as a tool to make us &lt;em&gt;more&lt;/em&gt; valuable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The key? Use AI for learning, not just code generation.&lt;/p&gt;
&lt;p&gt;Relying solely on AI for code generation is the trap.&lt;/p&gt;
&lt;p&gt;The truly valuable skill is judgment.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; The judgment to decide &lt;em&gt;what&lt;/em&gt; to build and &lt;em&gt;how&lt;/em&gt; to build it.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; AI excels at the &lt;em&gt;actual building&lt;/em&gt; (code generation). Embrace this. It&amp;rsquo;s inevitable. Most future code will be written|generated with AI.&lt;/p&gt;
&lt;p&gt;The good news? AI is &lt;em&gt;amazing&lt;/em&gt; for learning.&lt;/p&gt;
&lt;h3 id="some-examples"&gt;
Some examples
&lt;a class="heading-anchor" href="#some-examples" aria-label="Link to Some examples"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I think back to my undergraduate days. A concept didn&amp;rsquo;t click? It meant a bike ride to the library. I&amp;rsquo;d search for textbooks, pull references, and try to piece it all together. &lt;em&gt;Assuming&lt;/em&gt; the books were even available. Guess what I often did? I didn&amp;rsquo;t bother. Today, I&amp;rsquo;d just pull up my phone and look it up.&lt;/p&gt;
&lt;p&gt;The internet was a game-changer for students. AI is the next game-changer for all of us willing to learn.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A more recent example&lt;/strong&gt;: I&amp;rsquo;m doing an upcoming &lt;a href="https://fragmentedpodcast.com/"&gt;Fragmented&lt;/a&gt; episode on an article that my friend Colin&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;a href="https://code.cash.app/dispatchers-unconfined"&gt;wrote&lt;/a&gt; on Coroutine Dispatcher use.&lt;/p&gt;
&lt;p&gt;I understood about 75% on the first pass. But a few gaps remained, especially around how they related to Coroutine fundamentals. I needed to brush up.&lt;/p&gt;
&lt;p&gt;Without AI, my process would have been:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open a bunch of articles and docs.&lt;/li&gt;
&lt;li&gt;Read each one thoroughly.&lt;/li&gt;
&lt;li&gt;Synthesize the information mentally.&lt;/li&gt;
&lt;li&gt;Connect the dots with the article.&lt;/li&gt;
&lt;li&gt;Time: 2-3 hours, plus significant mental bandwidth.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With AI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Uploaded Colin&amp;rsquo;s article and official Kotlin docs to Google&amp;rsquo;s &lt;a href="https://notebooklm.google/"&gt;Notebook LM&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Asked targeted questions, addressing my knowledge gaps &lt;em&gt;directly&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Time: 30 minutes to reach a firm understanding.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What would have taken 2-3 hours took about 30 minutes.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; And I understood the concepts &lt;em&gt;better&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;AI is your opportunity to level up. Learn faster. Position yourself to make the critical decisions: &amp;ldquo;what&amp;rdquo; and &amp;ldquo;how&amp;rdquo; to build. This is where software engineers, &lt;em&gt;especially new ones&lt;/em&gt;, should focus. You don&amp;rsquo;t have the luxury of years of experience that some of us &amp;ldquo;senior&amp;rdquo; engineers had. AI lets you catch up, fast.&lt;/p&gt;
&lt;p&gt;Yes, the bar will be raised. Getting into the field won&amp;rsquo;t be as easy. But for those willing to learn and adapt, AI is an incredible advantage. Embrace it.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;My friend &lt;a href="https://www.vinaygaba.me/writing/fragmented-podcast-future-of-android-development/"&gt;Vinay&lt;/a&gt; and someone who&amp;rsquo;s also working very closely with AI for software development eloquently mentioned this in &lt;a href="https://fragmentedpodcast.com/episodes/257/"&gt;ep #257&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;For now, AI isn&amp;rsquo;t as good as this. It will catch up but my bet is it&amp;rsquo;ll take longer to close this specific gap.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Yes, &lt;em&gt;that&lt;/em&gt; &lt;a href="https://coil-kt.github.io/coil/"&gt;one&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;If i&amp;rsquo;m being honeset, a lart part of the time was spent thinking about how I&amp;rsquo;d connect the dots to write this blog post.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/ai-learning/</guid><pubDate>Sun, 09 Mar 2025 22:22:02 GMT</pubDate></item><item><title>
Terrifyingly Inspiring - My Life in Weeks</title><link>https://kau.sh/blog/terrifyingly-inspiring-life-in-weeks/</link><description>
&lt;p&gt;I&amp;rsquo;ve been following the awesome Gina Trapani for quite some time.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; She recently built a tool to track her life in weeks (inspired by Tim Urban of Wait But Why fame&amp;rsquo;s &lt;a href="https://waitbutwhy.com/2014/05/life-weeks.html"&gt;own article&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;As I get close to another complete revolution around the sun, the only thought that went through my mind reading this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Terrifyingly Inspiring.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Lifehacker ❤️. It shaped my early internet years.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/terrifyingly-inspiring-life-in-weeks/</guid><pubDate>Sat, 15 Feb 2025 22:22:12 GMT</pubDate></item><item><title>
Styled checkboxes for Obsidian tasks without plugins or a custom theme</title><link>https://kau.sh/blog/obsidian-icon-checkboxes-without-theme-plugins/</link><description>
&lt;p&gt;I use Obsidian for tracking my weekly tasks.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; One thing I like to do, is mark tasks as &amp;ldquo;canceled&amp;rdquo;, &amp;ldquo;rescheduled&amp;rdquo;, &amp;ldquo;in progress&amp;rdquo;, etc. in case I move them between weeks. These tasks should visually stand out from the rest.&lt;/p&gt;
&lt;p&gt;Custom themes like &lt;a href="https://minimal.guide/checklists#Alternate&amp;#43;checkboxes"&gt;Obsidian Minimal&lt;/a&gt; provide this as an option. There&amp;rsquo;s also plugins that do this. But I like to keep my Obsidian setup lean and only wanted this feature without other cruft.&lt;/p&gt;
&lt;p&gt;CSS to the rescue &lt;a href="https://kau.sh/blog/custom-fonts-obsidian-mobile/"&gt;again&lt;/a&gt;! The linked forum post had the starting css snippet I needed. I customized further to include some more options that the Minimal theme includes.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/uploads/2025/obsidian-checkboxes.gif" alt="Styled checkboxes in Obsidian"&gt;&lt;/p&gt;
&lt;p&gt;You can find my custom css snippet &lt;a href="https://gist.github.com/kaushikgopal/5e3018581946e09c49ac43aa8657b94c"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;that&amp;rsquo;s another post in itself.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/obsidian-icon-checkboxes-without-theme-plugins/</guid><pubDate>Mon, 10 Feb 2025 08:00:00 GMT</pubDate></item><item><title>
Age of the AI phone</title><link>https://kau.sh/blog/ai-phones-will-win/</link><description>
&lt;p&gt;In Vinay&amp;rsquo;s &lt;a href="https://jetpackcomposeapp.beehiiv.com/p/dispatch-issue-11"&gt;latest newsletter&lt;/a&gt;, he asks a few of us #AndroidDev to predict what the future of Android development is going to look like.&lt;/p&gt;
&lt;p&gt;Yours truly had this as one of the predictions:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;AI everything 🙄
&amp;hellip;
On the product side, we’ll see more on-device AI, with smaller models like Gemini Flash/o3-mini running locally to provide operator-like intelligence directly on phones and this will probably be what most folks are geared towards doing for mobile development.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Looking at this Youtube video that&amp;rsquo;s now doing the rounds, I&amp;rsquo;m starting to feel ever so slightly validated.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/JmxqOX0RN1w?rel=0}&amp;start=109&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Google (or Samsung) truly have their shot of gaining significant Mobile market share again, especially given how much Apple has been floundering in the AI space.&lt;/p&gt;</description><guid>https://kau.sh/blog/ai-phones-will-win/</guid><pubDate>Sun, 09 Feb 2025 18:53:19 GMT</pubDate></item><item><title>
How to enable custom fonts in Obsidian Mobile</title><link>https://kau.sh/blog/custom-fonts-obsidian-mobile/</link><description>
&lt;p&gt;One of the challenges with note taking apps on mobile is the limited font selection. While desktops let you install custom fonts system-wide, mobile platforms are much more restrictive.&lt;/p&gt;
&lt;p&gt;However, there&amp;rsquo;s an elegant solution for Obsidian given it&amp;rsquo;s an electron app. Obsidian allows you to inject a CSS snippet to tweak the appearnace of the app. CSS allows you to embed base64 encoded fonts directly in them for styling text 💡.&lt;/p&gt;
&lt;p&gt;I figured let&amp;rsquo;s try this and see if it works on mobile. Works like a charm!&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how to do it:&lt;/p&gt;
&lt;h3 id="1-convert-your-font-file-to-base64-and-store-in-the-clipboard"&gt;
1. convert your font&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; file to base64 and store in the clipboard
&lt;a class="heading-anchor" href="#1-convert-your-font-file-to-base64-and-store-in-the-clipboard" aria-label="Link to 1. convert your font file to base64 and store in the clipboard"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# do this on a terminal app&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;base64 -i &amp;lt;path_to_font_file&amp;gt; | pbcopy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="2-create-a-new-css-snippet-in-obsidian-settings--appearance--css-snippets-and-add-your-font-declaration"&gt;
2. Create a new CSS snippet in Obsidian (Settings &amp;gt; Appearance &amp;gt; CSS snippets) and add your font declaration:
&lt;a class="heading-anchor" href="#2-create-a-new-css-snippet-in-obsidian-settings--appearance--css-snippets-and-add-your-font-declaration" aria-label="Link to 2. Create a new CSS snippet in Obsidian (Settings &amp;gt; Appearance &amp;gt; CSS snippets) and add your font declaration:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@&lt;span style="color:#66d9ef"&gt;font-face&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;font-family&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;custom-font-name&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;font-weight&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;350&lt;/span&gt; &lt;span style="color:#f92672"&gt;900&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;url&lt;/span&gt;&lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#f92672"&gt;data&lt;/span&gt;:&lt;span style="color:#a6e22e"&gt;application&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#f92672"&gt;octet-stream&lt;/span&gt;&lt;span style="color:#f92672"&gt;;&lt;/span&gt;&lt;span style="color:#f92672"&gt;base64&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&amp;lt;&lt;/span&gt;&lt;span style="color:#f92672"&gt;paste_base64_string_here&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remember to enable the snippet after creating it. Also font files can get large when converted to base64. So you&amp;rsquo;re not going to be able to type out that encoded string.&lt;/p&gt;
&lt;h3 id="3-activate-custom-font-in-obsidian"&gt;
3. Activate custom font in Obsidian
&lt;a class="heading-anchor" href="#3-activate-custom-font-in-obsidian" aria-label="Link to 3. Activate custom font in Obsidian"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Under Settings &amp;gt; Appearance &amp;gt; (Text|Monospace) Font, enter &amp;ldquo;custom-font-name&amp;rdquo;. It won&amp;rsquo;t show up in the dropdown but if you type the name exactly, you&amp;rsquo;ll see a checkmark after a second and your new font is enabled.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I typically do the above on my desktop and sync settings to my mobile. But nothing stops you from doing it directly on your mobile (if you don&amp;rsquo;t have sync enabled).&lt;/p&gt;
&lt;p&gt;I now have my favorite reading font applied across my notes on mobile and my favorite monospace one applied to all the code in them!&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2025/obsidian-mobile-custom-font.webp"
alt="Obsidian on Android with custom fonts"
height="300"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Obsidian on Android with custom fonts
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I encoded a &lt;a href="https://fonts.google.com/knowledge/glossary/variable_fonts"&gt;variable&lt;/a&gt; font for convenience so i didn&amp;rsquo;t have to declare italicized, bolded, etc. versions of the font.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/custom-fonts-obsidian-mobile/</guid><pubDate>Wed, 05 Feb 2025 08:00:00 GMT</pubDate></item><item><title>
Powering your Hugo blog with Bluesky Comments - invest in a feed you control</title><link>https://kau.sh/blog/bluesky-comments-for-hugo/</link><description>
&lt;p&gt;Twitter&amp;rsquo;s change in ownership triggered a cascade of events birthing various social media clones. It’s been tricky to decide which network to invest time and energy in. I’m not an influencer, but I’d be kidding myself if I thought having a presence on social media wasn’t important.&lt;/p&gt;
&lt;p&gt;The recent &lt;a href="https://www.theverge.com/2025/1/19/24347340/tiktok-ban-app-store-google-play"&gt;TikTok drama&lt;/a&gt; has given me clarity on one extremely important principle that all creators should have learned by now: &lt;strong&gt;Invest in channels you control&lt;/strong&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Invest in channels you control&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I recently &lt;a href="https://buttondown.com/fragmentedcast/archive/2025-jan"&gt;moved&lt;/a&gt; Fragmented (my podcast) from WordPress to Hugo in this same spirit; and was looking for a way to engage more closely with the community. With Bluesky gaining traction, it made sense to power my blog comments using a decentralized commenting system that I could control. Bluesky is built on the &lt;a href="https://atproto.com/"&gt;AT protocol&lt;/a&gt;, which aligns perfectly with this idea.&lt;/p&gt;
&lt;p&gt;Thanks to Hugo’s flexibility, I was able to integrate Bluesky as a commenting system quite easily. I used &lt;a href="https://gist.github.com/basyliq/ca50ae5442dce9e4f01b1821de7d973d"&gt;this post&lt;/a&gt; as inspiration, but if you’d like to see my final implementation, you can check out the source code here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/kaushikgopal/henry-hugo/blob/master/layouts/partials/comments-bsky.html"&gt;&lt;code&gt;comments-bsky.html&lt;/code&gt;&lt;/a&gt; (template for the comments)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kaushikgopal/henry-hugo/blob/master/assets/js/comments-bsky.js"&gt;&lt;code&gt;comments-bsky.js&lt;/code&gt;&lt;/a&gt; (straightforward JS to load the comments)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I particularly like my implementation as it gently allows you to jump to the exact comment, and allows nesting. To see how this looks open the website on your browser and see the Comments sections below!&lt;/p&gt;</description><guid>https://kau.sh/blog/bluesky-comments-for-hugo/</guid><pubDate>Sun, 19 Jan 2025 08:00:00 GMT</pubDate></item><item><title>
How to make the iPhone Continuity Camera work properly in Chrome</title><link>https://kau.sh/blog/iphone-camera-chrome/</link><description>
&lt;p&gt;I use a pixel as my primary android phone. But as a mobile developer, I also keep an iPhone handy for those times I need to test how an app works on iOS (especially if it&amp;rsquo;s not available on Android&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;p&gt;But my iPhone doesn&amp;rsquo;t sit in a drawer unused. It doubles up as my webcam when not being used as a phone. This is possible because I use macOS for my primary desktop needs, and this wonderful feature it enables called &amp;ldquo;Continuity Camera&amp;rdquo;. I&amp;rsquo;ve removed a webcam from my setup thanks to this, and the camera actually works much better than most webcams out there today.&lt;/p&gt;
&lt;p&gt;But there&amp;rsquo;s one really annoying thing about this setup. It won&amp;rsquo;t be recognized in Google Meet when using a chromium browser. Searching on Reddit points out that this is a bizzarre known security requirement for chromium browsers alone.&lt;/p&gt;
&lt;p&gt;Your iPhone needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;to be in landscape mode (so don&amp;rsquo;t have portrait lock on)&lt;/li&gt;
&lt;li&gt;have the screen turned off&lt;/li&gt;
&lt;li&gt;to be locked&lt;/li&gt;
&lt;li&gt;motionless (so won&amp;rsquo;t work if handheld)&lt;/li&gt;
&lt;li&gt;and have an unobstructed camera&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;wut 😵‍💫&lt;/p&gt;
&lt;p&gt;But works like a charm. So if you run into this issue, fix those things and it&amp;rsquo;ll all work like a charm.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;thinking about you &lt;a href="https://flighty.app/"&gt;Flighty&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/iphone-camera-chrome/</guid><pubDate>Thu, 12 Dec 2024 07:00:00 GMT</pubDate></item><item><title>
My new programming font - Commit Mono</title><link>https://kau.sh/blog/commit-mono/</link><description>
&lt;p&gt;I&amp;rsquo;ve been using this font for a while as my primary programming font and referenced it in one of my &lt;a href="https://kau.sh/letter/3#new-font---commitmono"&gt;newsletters&lt;/a&gt;. Thought i&amp;rsquo;ll share this more prominently. This website now uses Commit Mono for code. I &lt;a href="https://commitmono.com/"&gt;customized&lt;/a&gt; a few of the characters:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/letter/3/commit-mono-customization.webp" alt="Commit Mono Customization"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/letter/3/commit-mono-sample.webp" alt="Commit Mono Sample"&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are better ways to design than putting a big effort into making something look special. Special is generally less useful than normal, and less rewarding in the long term. Special things demand attention for the wrong reasons, interrupting potentially good atmosphere with their awkward presence.&lt;/p&gt;
&lt;p&gt;— &lt;em&gt;Jasper Morrison, Super Normal Manifesto&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;</description><guid>https://kau.sh/blog/commit-mono/</guid><pubDate>Sat, 21 Sep 2024 07:00:00 GMT</pubDate></item><item><title>
Coroutine Testing - Controlling time</title><link>https://kau.sh/blog/coroutine-testing-time/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is part of a series of posts on &lt;a href="https://kau.sh/blog/coroutine-testing"&gt;Coroutine Testing&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-dispatchers"&gt;Picking the right Dispatcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-backgroundscope"&gt;Never ending tests &amp;amp; backgroundscope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;Controlling time&lt;/a&gt; ←&lt;/li&gt;
&lt;li&gt;Helpful @Junit TestRule extension (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Full USF example for Android (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure &gt;
&lt;img src="hero.webp"
alt="Coroutine Testing - Tardis"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;My journey with coroutine testing started with this &amp;ldquo;simple&amp;rdquo; requirement — to control virtual time in concurrent logic.&lt;/p&gt;
&lt;p&gt;From my &lt;a href="https://kau.sh/blog/coroutine-testing-dispatchers"&gt;previous post&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… If you don’t use a &lt;code&gt;StandardTestDispatcher&lt;/code&gt; explicitly, then operators like &lt;code&gt;runCurrent&lt;/code&gt;, &lt;code&gt;advanceTimeBy&lt;/code&gt; etc. have no meaning.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have a confession to make. I was coy saying &amp;ldquo;have no meaning&amp;rdquo;. I didn&amp;rsquo;t say &amp;ldquo;won&amp;rsquo;t work&amp;rdquo; because in reality those apis will &amp;ldquo;work&amp;rdquo; just not in the way you&amp;rsquo;d expect.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dive deeper.&lt;/p&gt;
&lt;h1 id="the-3-apis"&gt;
The 3 apis
&lt;a class="heading-anchor" href="#the-3-apis" aria-label="Link to The 3 apis"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;There are 3 important apis in coroutine tests to play with time:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;runCurrent&lt;/code&gt;&lt;/strong&gt;
- &lt;em&gt;execute&lt;/em&gt; tasks &lt;em&gt;scheduled&lt;/em&gt; at the current moment of virtual time&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;advanceTimeBy&lt;/code&gt;&lt;/strong&gt;
- advance virtual time by a number of milliseconds and then execute tasks scheduled in the meantime.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;advanceUntilIdle&lt;/code&gt;&lt;/strong&gt;
- similar to advanceTimeBy but instead of advancing by a specific number of milliseconds, it keeps advancing until no more scheduled tasks are found.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the context of coroutine testing, when you think of &amp;ldquo;time&amp;rdquo;, it refers simply to the order that the &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; decides to execute your scheduled coroutines. That&amp;rsquo;s why the definitions above stress on words &amp;ldquo;execute&amp;rdquo; vs &amp;ldquo;scheduled&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;In reality, these apis &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/#-833584629%2FFunctions%2F1391162071"&gt;belong&lt;/a&gt; to the class &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; (not StandardTestDispatcher as the coy comment might have indicated).&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s attempt to understand this all, with some code.&lt;/p&gt;
&lt;h1 id="standardtestdispatcher--the-3-apis"&gt;
StandardTestDispatcher &amp;amp; the 3 apis
&lt;a class="heading-anchor" href="#standardtestdispatcher--the-3-apis" aria-label="Link to StandardTestDispatcher &amp;amp; the 3 apis"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This animation should illustrate how the different apis are designed to work.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="animation.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;&lt;code&gt;StandardTestDispatcher&lt;/code&gt; is good about respecting the 3 apis.&lt;/p&gt;
&lt;p&gt;A basic test demonstrating the use of &lt;code&gt;runCurrent&lt;/code&gt; that shouldn&amp;rsquo;t be surprising:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;() = runTest(StandardTestDispatcher()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;X&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;X&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Before &lt;code&gt;runCurrent&lt;/code&gt; the coroutine isn&amp;rsquo;t launched so we expect the result &amp;ldquo;X&amp;rdquo;. On &lt;code&gt;runCurrent&lt;/code&gt; we execute any scheduled coroutines without advancing the virtual clock, so expected result emitted&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; is A.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s &lt;code&gt;advanceTimeBy&lt;/code&gt; exactly 1 second (the first delay):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;advanceTimeBy(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isNotEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isNotEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice that even though we advanced time by the delay, the result hasn&amp;rsquo;t updated or changed from A. This is because you&amp;rsquo;ve &lt;em&gt;scheduled&lt;/em&gt; the next emit with the delay but haven&amp;rsquo;t &lt;em&gt;executed&lt;/em&gt; it yet.&lt;/p&gt;
&lt;p&gt;Throw in a &lt;code&gt;runCurrent&lt;/code&gt; like before and it&amp;rsquo;ll proceed to execute, giving you the result you expect:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;advanceTimeBy(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isNotEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isNotEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To really test your understanding - what happens if you advance time by 2 seconds (greater than the first delay of 1 second but less than the next one of 2 more seconds):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;advanceTimeBy(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Advancing it by 2 seconds means you&amp;rsquo;ve gone past the initial one second delay (where the job is scheduled) + an additional 1 second, nudges the job to execute (releasing B &amp;amp; C). We don&amp;rsquo;t need a &lt;code&gt;runCurrent&lt;/code&gt; here like the previous example because you&amp;rsquo;ve nudged the virtual clock past the scheduled time, well into execution. This demonstrates how going past the scheduled delay -even by a small amount- will automatically execute on the results.&lt;/p&gt;
&lt;p&gt;This &lt;a href="https://github.com/kaushikgopal/coroutine-testing/blob/4728cff1f45a09845c1def929f262a8dde1409c1/app/src/test/kotlin/kau/sh/oss/testing/StandardVsUnconfinedTestDispatcherTests.kt#L66"&gt;entire test&lt;/a&gt; should now make sense:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;() = runTest(StandardTestDispatcher()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;X&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; advanceTimeBy(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// result A executed
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// schedule B &amp;amp; C
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// execute B &amp;amp; C
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// no-op (no time advancement)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; advanceTimeBy(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// schedule D
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// execute D
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; assertThat(result)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// 😉
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;The key point to take away with StandardTestDispatcher is that it respects the apis but you have to really understand the difference between scheduling a job and executing it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="unconfinedtestdispatcher--the-3-apis"&gt;
UnconfinedTestDispatcher &amp;amp; the 3 apis
&lt;a class="heading-anchor" href="#unconfinedtestdispatcher--the-3-apis" aria-label="Link to UnconfinedTestDispatcher &amp;amp; the 3 apis"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is where things get complicated. Don&amp;rsquo;t take my word for it , even the &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt#L63"&gt;docs&lt;/a&gt; say so:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="utd-src-1.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;&amp;ldquo;Unusual&amp;rdquo; is correct.&lt;/p&gt;
&lt;p&gt;I want to first point you to some source code that will help build the necessary context. If you look at the &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt"&gt;implementation&lt;/a&gt; for both TestDispatchers, you&amp;rsquo;ll see that they both utilize a &lt;code&gt;TestCoroutineScheduler&lt;/code&gt;:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="sch-std.webp"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-standard-test-dispatcher.html"&gt;
StandardTestDispatcher src
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure &gt;
&lt;img src="sch-utd.webp"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-unconfined-test-dispatcher.html"&gt;
UnconfinedTestDispatcher src
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Remember &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; is effectively your virtual clock and is the actual class that provides the 3 big apis. So it follows that &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt; has &lt;em&gt;some&lt;/em&gt; effect from the apis.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s two methods of interest &lt;code&gt;isDispatchNeeded&lt;/code&gt; and &lt;code&gt;dispatch&lt;/code&gt;.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="td-src-1.webp"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-core/common/src/CoroutineDispatcher.kt#L97"&gt;
isDispatchNeeded from CoroutineDispatcher
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Looking at the &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt#L95"&gt;implementation&lt;/a&gt; for the method &lt;code&gt;dispatch&lt;/code&gt; in both TestDispatcher classes. You can piece together some things:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// UnconfinedTestDispatcher
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;isDispatchNeeded&lt;/span&gt;(context: CoroutineContext): Boolean = &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch&lt;/span&gt;(context: CoroutineContext, block: Runnable) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; checkSchedulerInContext(scheduler, context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scheduler.sendDispatchEvent(context)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// StandardTestDispatcher
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// fun isDispatchNeeded not overridden so defaults to true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;dispatch&lt;/span&gt;(context: CoroutineContext, block: Runnable) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scheduler.registerEvent(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, block, context) { &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;UnconfinedTestDispatcher sends the dispatch event right away. It further overrides &lt;code&gt;isDispatchNeeded&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; meaning - execute the coroutine immediately, don&amp;rsquo;t first register (or schedule) the coroutine just send or dispatch.&lt;/p&gt;
&lt;p&gt;StandardTestDispatcher on the other hand is &amp;ldquo;simpler&amp;rdquo; and merely registers (or schedules) the event to be dispatched. When does it get executed or dispatched then if right away? That&amp;rsquo;s where the 3 apis come in 💡. This is why it &lt;em&gt;feels&lt;/em&gt; like the 3 apis to advance time have more meaning with StandardTestDispatcher.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at test code for &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt; similar to our StandardTestDispatcher examples. This is a super basic test for UnconfinedTestDispatcher.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;test&lt;/span&gt;() = runTest(UnconfinedTestDispatcher()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;X&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(result).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Already, we start with some unusual-ness. Notice that we didn&amp;rsquo;t &lt;code&gt;runCurrent&lt;/code&gt; but the very first value &amp;ldquo;A&amp;rdquo; was eagerly emitted .&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; In the case of StandardTestDispatcher, the value would have been &amp;ldquo;X&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-test/common/src/TestCoroutineDispatchers.kt#L15"&gt;docs&lt;/a&gt; actually point this behavior out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* Like [&lt;span style="color:#a6e22e"&gt;Dispatchers&lt;/span&gt;.Unconfined], &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; one does not provide guarantees about the execution order &lt;span style="color:#66d9ef"&gt;when&lt;/span&gt; several coroutines
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* are queued &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt; dispatcher. However, we ensure that the [launch] and [async] blocks at the top level of [runTest]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* are entered eagerly. This allows launching child coroutines and not calling [runCurrent] &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; them to start executing.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The funny thing is UnconfinedTestDispatcher will actually behave very similarly to StandardTestDispatcher now in terms of advancing time (after it is eagerly launched):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;launch {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; delay(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; result = &lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// now it&amp;#39;ll behave like StandardTestDispatcher
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;advanceTimeBy(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;seconds)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;runCurrent()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;C&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;advanceTimeBy(&lt;span style="color:#ae81ff"&gt;2.&lt;/span&gt;seconds.plus(&lt;span style="color:#ae81ff"&gt;1.&lt;/span&gt;milliseconds))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;assertThat(result).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;D&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So it &lt;em&gt;does&lt;/em&gt; respect the 3 apis but only after eagerly launching. Hopefully that clarifies the original comment.&lt;/p&gt;
&lt;p&gt;To be honest I&amp;rsquo;ve asked myself many times what the point of having both kinds of TestDispatchers is, if this remains the only big difference. One important reason is that &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md#replace-runblockingtest-with-runtestunconfinedtestdispatcher"&gt;seismic change&lt;/a&gt; in apis with 1.6.0 — older testing apis like &lt;code&gt;runBlockingTest&lt;/code&gt; (now deprecated) used to launch coroutines eagerly to make testing less tedious, so it was an easier migration path to swap in place with &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s my takeaway between the two dispatchers:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;UnconfinedTestDispatcher launches the coroutine eagerly (explained above) which makes it less tedious during testing&lt;/li&gt;
&lt;li&gt;Order is not guaranteed with UnconfinedTestDispatcher, so StandardTestDispatcher is more predictable especially in complicated concurrent scenarios&lt;/li&gt;
&lt;li&gt;UnconfinedTestDispatcher is good about collecting every single item (this doesn&amp;rsquo;t have to do with time) but maybe in a future post I&amp;rsquo;ll show how testing &lt;code&gt;StateFlow&lt;/code&gt;s can benefit from UnconfinedTestDispatcher.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;Just as manual and automatic transmission cars offer different levels of control and convenience in driving, the &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt; and &lt;code&gt;StandardTestDispatcher&lt;/code&gt; in Kotlin coroutines provide distinct approaches for managing the execution of test code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I personally do prefer using &lt;code&gt;StandardTestDispatcher&lt;/code&gt; because I&amp;rsquo;m not one to shy away from boilerplate, so I happily trade off tediousness with predictability but your mileage might vary.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My thanks to &lt;a href="https://zsmb.co/"&gt;Márton&lt;/a&gt; for reviewing this post. Next up: A Helpful @Junit CoroutineTestRule extension (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I spent way too much time making this animation, so I&amp;rsquo;m dropping it right at the start. Though if I&amp;rsquo;m being honest, it&amp;rsquo;s faster to look at the following code and understand.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I&amp;rsquo;m using the terminology &lt;code&gt;emit&lt;/code&gt; because the animation was meant to illustrate a &lt;code&gt;MutableStateFlow&lt;/code&gt;. For the tests shown here, think of emit as the &lt;code&gt;result&lt;/code&gt; being assigned a value.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Again I&amp;rsquo;m using the terminology &lt;code&gt;emit&lt;/code&gt; as before, but think of emit as the &lt;code&gt;result&lt;/code&gt; being assigned a value.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/coroutine-testing-time/</guid><pubDate>Wed, 04 Sep 2024 07:00:00 GMT</pubDate></item><item><title>
Mobile Rewrites &amp; Crisis Management</title><link>https://kau.sh/blog/mobile-rewrite-crisis-mgt/</link><description>
&lt;p&gt;My good friend &lt;a href="https://mustafaali.net/"&gt;Mustafa&lt;/a&gt; has some sage advice for Sonos on the recent rewrite fiasco.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Fix all issues and test the new app very thoroughly with external beta testers before relaunching.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Have the best crisis leader at the company take over. Find all ICs who have been raising the alarm and give them authority to move fast. Remove anything that gets in the way - &amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a single source of truth - all issues, feedback and bugs should flow into a single place. Have a small team (&amp;lt;=3) triage and prioritize them. Everyone else focuses on fixing them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Appoint DRIs - Divide problems by areas (feature x, stability, performance, etc) and appoint a single Directly Responsible Individual (DRI) for each. &amp;hellip; Prioritize skill over titles/experience while picking them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Sign the entire company up for testing - You can no longer rely on just the QA team to find bugs. Everyone needs to test the app daily, especially all managers and execs. All hands on deck.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;gold.&lt;/p&gt;</description><guid>https://kau.sh/blog/mobile-rewrite-crisis-mgt/</guid><pubDate>Wed, 04 Sep 2024 07:00:00 GMT</pubDate></item><item><title>
Coroutine Testing - Never ending tests &amp; backgroundScope</title><link>https://kau.sh/blog/coroutine-testing-backgroundscope/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is part of a series of posts on &lt;a href="https://kau.sh/blog/coroutine-testing"&gt;Coroutine Testing&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-dispatchers"&gt;Picking the right Dispatcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-backgroundscope"&gt;Never ending tests &amp;amp; backgroundscope&lt;/a&gt; ←&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;Controlling time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Helpful @Junit TestRule extension (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Full USF example for Android (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure &gt;
&lt;img src="hero.webp"
alt="Android bot looking through telescope"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;If you&amp;rsquo;ve spent some time testing Coroutines this exception should look familiar:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;After waiting for 1m,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;the test coroutine is not completing,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;there were active child jobs
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This tends to happen when you have a coroutine job in your test, that fails to complete on its own. Let&amp;rsquo;s take a simple example.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="bscope-src.webp"
alt="example code with channel"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://github.com/kaushikgopal/coroutine-testing/blob/b235297768cbc099796b485251ee7390d9e805a6/app/src/main/kotlin/kau/sh/oss/testing/App.kt#L25"&gt;
src on github
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We use a &lt;code&gt;Channel&lt;/code&gt; &lt;a href="https://kotlinlang.org/docs/coroutines-and-channels.html#channels"&gt;here&lt;/a&gt; which is the proverbial event bus for Coroutines.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kotlinlang.org/docs/images/using-channel-many-coroutines.png"
alt="Channel diagram"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
source: kotlinlang.org
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Channels don&amp;rsquo;t terminate on their own. So when you run a simple test checking the emission, while the items might get collected correctly per the assert statement in the test, the test itself fails like so:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="bscope-tst.webp"
alt="example code with channel"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://github.com/kaushikgopal/coroutine-testing/blob/b235297768cbc099796b485251ee7390d9e805a6/app/src/test/kotlin/kau/sh/oss/testing/AppTest.kt#L32"&gt;
test on github
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The test here is waiting for that coroutine job to complete, which in turn requires the Channel to, but that never happens and the test times out.&lt;/p&gt;
&lt;h1 id="where-else-would-i-run-into-this-problem"&gt;
Where else would I run into this problem?
&lt;a class="heading-anchor" href="#where-else-would-i-run-into-this-problem" aria-label="Link to Where else would I run into this problem?"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Channels aren&amp;rsquo;t the only case you&amp;rsquo;ll run into this problem. For example if you use a &amp;ldquo;hot&amp;rdquo; Flow like &lt;code&gt;SharedFlow&lt;/code&gt; or &lt;code&gt;StateFlow&lt;/code&gt;, they don&amp;rsquo;t terminate on their own, so the onus is on you to complete or cancel their &lt;code&gt;Job&lt;/code&gt; in tests.&lt;/p&gt;
&lt;p&gt;Android developers can frequently run into this problem too if you use &lt;code&gt;ViewModel&lt;/code&gt;s and have an internal &lt;code&gt;StateFlow&lt;/code&gt; providing your &amp;ldquo;view level data&amp;rdquo; (what i &lt;a href="https://github.com/kaushikgopal/movies-usf-android?tab=readme-ov-file#movie-search-using-a-unidirectional-state-flow-pattern"&gt;personally&lt;/a&gt; like to call &amp;ldquo;view state&amp;rdquo;).&lt;/p&gt;
&lt;p&gt;You typically use the &lt;code&gt;viewModelScope&lt;/code&gt; to launch internal coroutine jobs in a ViewModel. The OS then calls the lifecycle method &lt;code&gt;onClear&lt;/code&gt; when the &lt;code&gt;Activity&lt;/code&gt; or &lt;code&gt;Fragment&lt;/code&gt; no longer needs the ViewModel where all jobs started in the viewModelScope are canceled.&lt;/p&gt;
&lt;p&gt;But when unit testing these ViewModels, you don&amp;rsquo;t have access to the &lt;code&gt;viewModelScope&lt;/code&gt; and shouldn&amp;rsquo;t need it anyway.&lt;/p&gt;
&lt;h1 id="solving-this-problem"&gt;
Solving this problem
&lt;a class="heading-anchor" href="#solving-this-problem" aria-label="Link to Solving this problem"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s two ways to solve this problem:&lt;/p&gt;
&lt;h2 id="1-manually-cancel-the-job"&gt;
1. Manually cancel the Job
&lt;a class="heading-anchor" href="#1-manually-cancel-the-job" aria-label="Link to 1. Manually cancel the Job"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you have access to the &lt;code&gt;Job&lt;/code&gt; that spawns the never-ending coroutine, it&amp;rsquo;s trivial to cancel the Job. Here&amp;rsquo;s that first test, now passing:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="bscope-tst-fix-1.webp"
alt="fixed test - manually cancel job"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;A simple &lt;code&gt;job.cancel&lt;/code&gt; does the trick.&lt;/p&gt;
&lt;h2 id="2-use-a-backgroundscope"&gt;
2. Use a &lt;code&gt;backgroundScope&lt;/code&gt;
&lt;a class="heading-anchor" href="#2-use-a-backgroundscope" aria-label="Link to 2. Use a backgroundScope"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;But you don&amp;rsquo;t always have access to the &lt;code&gt;Job&lt;/code&gt; directly or it requires a level of pass-through that makes your code cumbersome.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s where &lt;code&gt;backgroundScope&lt;/code&gt; comes in and is a pretty elegant solution to this problem.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="bscope-tst-fix-2.webp"
alt="fixed test - using backgroundScope"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Instead of getting access to the job directly, launch the desired coroutines within a &lt;code&gt;backgroundScope&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;backgroundScope&lt;/code&gt; is a special construct provided in coroutine tests alone that makes sure all child coroutines are explicitly canceled once the test body completes.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="how-does-backgroundscope-work"&gt;
How does backgroundScope work?
&lt;a class="heading-anchor" href="#how-does-backgroundscope-work" aria-label="Link to How does backgroundScope work?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The beauty of Kotlin is everything is open source. Let&amp;rsquo;s go find the code that that powers this magic:&lt;/p&gt;
&lt;p&gt;The relevant code can be found as part of &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/d38672a53ccdfda6319ab5f11669af8ad96d60a5/kotlinx-coroutines-test/common/src/TestBuilders.kt#L370"&gt;&lt;code&gt;runTest&lt;/code&gt; in &lt;code&gt;TestBuilders.kt&lt;/code&gt;&lt;/a&gt;.Here&amp;rsquo;s the interesting parts of the implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestScope&lt;/span&gt;.runTest(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; timeout: Duration = &lt;span style="color:#a6e22e"&gt;DEFAULT_TIMEOUT&lt;/span&gt;.getOrThrow(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; testBody: &lt;span style="color:#66d9ef"&gt;suspend&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestScope&lt;/span&gt;.() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; Unit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;) { scope &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; createTestResult {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; testBodyFinished = AtomicBoolean(&lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scope.start(&lt;span style="color:#a6e22e"&gt;CoroutineStart&lt;/span&gt;.UNDISPATCHED, scope) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; yield()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; testBody()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; testBodyFinished.&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; withTimeout(timeout) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; testBodyFinished.&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; activeChildren.isNotEmpty() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;there were active child jobs: &lt;/span&gt;&lt;span style="color:#e6db74"&gt;$activeChildren&lt;/span&gt;&lt;span style="color:#e6db74"&gt;. &amp;#34;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; testBodyFinished.&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;the test completed, but only after the timeout&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;the test body did not run to completion&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (_: TimeoutCancellationException) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; } &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; backgroundScope.cancel()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Make &lt;code&gt;backgroundScope&lt;/code&gt; a regular part of your testing arsenal. In most of my tests these days, I liberally use and inject &lt;code&gt;backgroundScope&lt;/code&gt; to make sure those tests don&amp;rsquo;t hang.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Next up: &lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;Controlling time&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/coroutine-testing-backgroundscope/</guid><pubDate>Fri, 30 Aug 2024 07:00:45 GMT</pubDate></item><item><title>
Coroutine Testing</title><link>https://kau.sh/blog/coroutine-testing/</link><description>
&lt;figure class="full-bleed"&gt;
&lt;img src="hero.webp"
class="full-bleed"
alt="Desktop testing setup"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;When the #androiddevs transitioned from &lt;a href="https://reactivex.io/"&gt;Rx&lt;/a&gt; to &lt;a href="https://kotlinlang.org/docs/coroutines-overview.html"&gt;coroutines&lt;/a&gt; the topic of testing didn&amp;rsquo;t get as much attention in this new world of concurrency.&lt;/p&gt;
&lt;p&gt;It didn’t help that there was a &lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md#replace-runblockingtest-with-runtestunconfinedtestdispatcher"&gt;seismic change&lt;/a&gt; in Kotlin’s testing apis with 1.6.0. A whole bunch of online resources and tutorials are now defunct courtesy this change.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;My journey in the matter started because I simply couldn&amp;rsquo;t understand why test apis like &lt;code&gt;advanceTimeBy&lt;/code&gt; wouldn&amp;rsquo;t work reliably for me. The name made sense&amp;hellip; but my time wasn&amp;rsquo;t being advanced in any meaningful way.&lt;/p&gt;
&lt;p&gt;Then there&amp;rsquo;s the issue of flaky tests. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="test-fail.webp"
alt="Example of a failing test with advanceTimeBy"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://github.com/kaushikgopal/coroutine-testing/blob/c97a87dd374ee668184e824957be22a37385c485/app/src/test/kotlin/org/example/AppTest.kt"&gt;
flaky test code on github
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Run each test individually and it will pass; run them together as one test suite and &lt;code&gt;test2&lt;/code&gt; alone will fail. &lt;code&gt;test1&lt;/code&gt; passes but it takes a full 3s to run the test. If I have 300 of these in my app, are my tests going to take 15 minutes to run?&lt;/p&gt;
&lt;p&gt;I needed to understand many core concepts in order to confidently explain all the above phenomena. I&amp;rsquo;d like to share my learnings from going down the rabbit hole, in this series of posts:&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is part of a series of posts on &lt;a href="https://kau.sh/blog/coroutine-testing"&gt;Coroutine Testing&lt;/a&gt;: ←&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-dispatchers"&gt;Picking the right Dispatcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-backgroundscope"&gt;Never ending tests &amp;amp; backgroundscope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;Controlling time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Helpful @Junit TestRule extension (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Full USF example for Android (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If you&amp;rsquo;re looking for the most current and useful resources on coroutine testing today:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=nKCsIHWircA"&gt;Untangling Coroutine Testing - Marton Braun&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/README.md"&gt;jetbrains official docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/kotlin/coroutines/test"&gt;developer.android.com docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Kotlin/kotlinx.coroutines/blob/master/kotlinx-coroutines-test/MIGRATION.md"&gt;1.6.0 Coroutines test migration guide&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/coroutine-testing/</guid><pubDate>Sun, 25 Aug 2024 07:00:45 GMT</pubDate></item><item><title>
Coroutine Testing - Picking the right Dispatcher</title><link>https://kau.sh/blog/coroutine-testing-dispatchers/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is part of a series of posts on &lt;a href="https://kau.sh/blog/coroutine-testing"&gt;Coroutine Testing&lt;/a&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-dispatchers"&gt;Picking the right Dispatcher&lt;/a&gt; ←&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-backgroundscope"&gt;Never ending tests &amp;amp; backgroundscope&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;Controlling time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Helpful @Junit TestRule extension (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Full USF example for Android (&lt;em&gt;coming soon&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;figure &gt;
&lt;img src="hero.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Most of the problems and flakiness around coroutine testing stem from running them on different &lt;code&gt;Dispatchers&lt;/code&gt;. This is because the choice of Dispatcher can significantly impact the behavior of coroutines. This was also the most confusing&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; part for me starting out — understanding the implications of using a &lt;code&gt;Scope&lt;/code&gt;, &lt;code&gt;Context&lt;/code&gt; or &lt;code&gt;Dispatcher&lt;/code&gt;.&lt;/p&gt;
&lt;figure class="borderless"&gt;
&lt;img src="anatomy-coroutine.webp"
class="borderless"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I recommend &lt;a href="https://elizarov.medium.com/coroutine-context-and-scope-c8b255d59055#:~:text=Coroutine%20Context%20and%20Scope%20Different%20uses%20of%20physically,the%20same%20thing.%20%28Wikipedia%20on%20Hindley-Milner%20type%20system%29"&gt;Roman&amp;rsquo;s article&lt;/a&gt; if you want to brush up on the fundamentals. But in a nutshell: think of &lt;code&gt;CoroutineContext&lt;/code&gt; as a collection of elements that define the coroutine. It &lt;em&gt;contains&lt;/em&gt; a &lt;code&gt;Dispatcher&lt;/code&gt;, &lt;code&gt;Job&lt;/code&gt; &amp;amp; a &lt;code&gt;CoroutineName&lt;/code&gt;. When you launch a coroutine, it inherits the parent&amp;rsquo;s &lt;code&gt;CoroutineContext&lt;/code&gt; (and Dispatcher), unless you specify it explicitly.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;CoroutineScope&lt;/code&gt; on the other hand is just a way to manage and cancel (multiple) coroutines. It also defines a context and lifecycle for the coroutines launched &lt;em&gt;within&lt;/em&gt; it (the context could be linked to yet another Dispatcher). Any coroutine when launched, runs within a &lt;code&gt;CoroutineScope&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take an example:&lt;/p&gt;
&lt;figure class="tac"&gt;
&lt;img src="test-dispatcher-jump.webp"
class="tac"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Notice how the current &lt;code&gt;Dispatcher&lt;/code&gt; of the coroutine shifts from &lt;code&gt;StandardTestDispatcher&lt;/code&gt; → &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt; → &lt;code&gt;Dispatcher.IO&lt;/code&gt; in the span of three innocuous lines based on the coroutine builder (&lt;code&gt;runTest&lt;/code&gt;) or scope used (&lt;code&gt;TestScope&lt;/code&gt;, &lt;code&gt;turbineScope&lt;/code&gt; from the 3rd party library, App &lt;code&gt;scope&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://kau.sh/blog/coroutine-testing/"&gt;my initial post&lt;/a&gt; I pointed out this flaky test:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/blog/coroutine-testing/test-fail.webp"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://github.com/kaushikgopal/coroutine-testing/blob/c97a87dd374ee668184e824957be22a37385c485/app/src/test/kotlin/org/example/AppTest.kt"&gt;
flaky test code on github
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;em&gt;fix&lt;/em&gt; for this is as simple as explicitly injecting a &lt;code&gt;TestScope&lt;/code&gt; and making sure the same scope is used throughout.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="test-fix.webp"
alt="Flaky test fixed"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://github.com/kaushikgopal/coroutine-testing/commit/c12e90c5151a5587ffa272b515ae32788bf2f3bf?diff=split&amp;amp;w=1"&gt;
fixed test code on github
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Explicitly injecting the &lt;code&gt;CoroutineScope&lt;/code&gt; and substituting it with the &lt;code&gt;TestScope&lt;/code&gt; works really well and is my preferred strategy. This approach allows for more control over the Dispatcher used in tests . For reasons you&amp;rsquo;ll see later, these tests also run instantly (72&lt;strong&gt;ms&lt;/strong&gt; vs 6&lt;strong&gt;s&lt;/strong&gt; 82ms).&lt;/p&gt;
&lt;p&gt;But the story doesn&amp;rsquo;t end there.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;em&gt;official&lt;/em&gt; android docs, they recommend using the same &lt;em&gt;&lt;code&gt;Scheduler&lt;/code&gt;&lt;/em&gt;. Not a &lt;code&gt;Scope&lt;/code&gt; or &lt;code&gt;Dispatcher&lt;/code&gt;.&lt;/p&gt;
&lt;figure class="tac"&gt;
&lt;img src="doc-android.webp"
class="tac"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://developer.android.com/kotlin/coroutines/test#injecting-test-dispatchers"&gt;
developer.android.com
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h1 id="what-is-a-scheduler"&gt;
What is a Scheduler?
&lt;a class="heading-anchor" href="#what-is-a-scheduler" aria-label="Link to What is a Scheduler?"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This got me thinking… what &lt;code&gt;Scheduler&lt;/code&gt; do my tests use? Easy enough to check:&lt;/p&gt;
&lt;figure class="tac"&gt;
&lt;img src="run-test.webp"
class="tac"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;StandardTestDispatcher[
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scheduler=TestCoroutineScheduler@1623134f
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;StandardTestDispatcher&lt;/code&gt; &amp;amp; &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; - the plot thickens. What even is a &lt;code&gt;Scheduler&lt;/code&gt;? Rx had this &lt;a href="https://reactivex.io/documentation/scheduler.html"&gt;concept&lt;/a&gt; but with Coroutines, Dispatchers is the only thing we deal with.&lt;/p&gt;
&lt;p&gt;Let’s dive deeper.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="dispatcher-scheduler.webp"
alt="Coroutine test parts"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://youtu.be/nKCsIHWircA?si=0WzvY5d_E1ja0Da9&amp;amp;t=351"&gt;
Courtesy: Márton&amp;#39;s talk on Coroutine testing
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;code&gt;runTest&lt;/code&gt; is the basic api we use for coroutine testing and like other &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/"&gt;coroutine builders&lt;/a&gt; (runBlocking , launch, async etc.) implicitly uses a &lt;code&gt;CoroutineScope&lt;/code&gt;. For testing, there is a special scope called &lt;code&gt;TestScope&lt;/code&gt;. And given that &lt;code&gt;CoroutineScope&lt;/code&gt;s get linked to a &lt;code&gt;Dispatcher&lt;/code&gt;,&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; for this special scope there exists a special &lt;a href="https://developer.android.com/kotlin/coroutines/test#testdispatchers"&gt;&lt;code&gt;TestDispatcher&lt;/code&gt;&lt;/a&gt; (&lt;em&gt;more on this later&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;When testing coroutines, we often need precise control over their execution timing, especially when dealing with virtual time. &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-coroutine-scheduler/"&gt;&lt;code&gt;TestCoroutineScheduler&lt;/code&gt;&lt;/a&gt; is the construct that provides us this fine-grained control. This is also why you don&amp;rsquo;t see or use Schedulers in production or your app code, where there isn&amp;rsquo;t this need.&lt;/p&gt;
&lt;p&gt;In a future post, I&amp;rsquo;ll show you a &lt;code&gt;@Junit&lt;/code&gt; test rule that makes sure the &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; is shared across injected dispatchers.&lt;/p&gt;
&lt;p&gt;But before we get into that, let&amp;rsquo;s look at that official android doc again:&lt;/p&gt;
&lt;figure class="tac"&gt;
&lt;img src="doc-android.webp"
class="tac"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://developer.android.com/kotlin/coroutines/test#injecting-test-dispatchers"&gt;
developer.android.com
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Any number of &lt;code&gt;TestDispatcher&lt;/code&gt;s? How many are there?&lt;/p&gt;
&lt;h2 id="testdispatchers"&gt;
TestDispatchers
&lt;a class="heading-anchor" href="#testdispatchers" aria-label="Link to TestDispatchers"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In testing, instead of using one of the &lt;a href="https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#dispatchers-and-threads"&gt;regular&lt;/a&gt; Dispatchers (Default, IO, Main, Unconfined) you need to use a special &lt;code&gt;TestDispatcher&lt;/code&gt;. Think of the &lt;code&gt;TestCoroutineScheduler&lt;/code&gt; as the virtual clock and TestDispatchers as special Dispatchers. These special Dispatchers use the virtual clock to schedule and manage the execution of coroutines in tests.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TestDispatcher&lt;/code&gt; itself is an &lt;a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/kotlinx.coroutines.test/-test-dispatcher/"&gt;abstract class&lt;/a&gt; though and there exists two implementations that we can use - &lt;code&gt;StandardTestDispatcher&lt;/code&gt; &amp;amp; &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt;. Craig has an &lt;a href="https://craigrussell.io/2022/01/19/comparing-standardtestdispatcher-and-unconfinedtestdispatcher"&gt;excellent post&lt;/a&gt; comparing the two but in a nutshell:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;StandardTestDispatcher:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;does not execute tasks automatically&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;you have full control over what is executed and when&lt;/li&gt;
&lt;li&gt;you have to use methods like &lt;code&gt;runCurrent&lt;/code&gt;, &lt;code&gt;advanceTimeBy&lt;/code&gt; &amp;amp; &lt;code&gt;advanceUntilIdle&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;if not specified explicitly, &lt;code&gt;runTest&lt;/code&gt; will use a &lt;code&gt;StandardTestDispatcher&lt;/code&gt; by default&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;UnconfinedTestDispatcher:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;starts new coroutines eagerly (which means you don&amp;rsquo;t need to manually advance the coroutines in your test)&lt;/li&gt;
&lt;li&gt;there&amp;rsquo;s no guarantee on the order of your coroutines launched&lt;/li&gt;
&lt;li&gt;far more convenient to use in tests (especially if you don&amp;rsquo;t care about concurrency in your tests).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="eager-loading-of-coroutines-in-tests"&gt;
Eager loading of coroutines in tests
&lt;a class="heading-anchor" href="#eager-loading-of-coroutines-in-tests" aria-label="Link to Eager loading of coroutines in tests"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The fact that &lt;code&gt;StandardTestDispatcher&lt;/code&gt; does &lt;em&gt;not&lt;/em&gt; execute tasks automatically lands up being a pretty important difference in the world of concurrency. &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt;s are more convenient to use but you trade-off on a deterministic test run.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll observe that the extremely convenient coroutine &lt;code&gt;Flow&lt;/code&gt; testing library &lt;a href="https://github.com/cashapp/turbine"&gt;Turbine&lt;/a&gt; uses an &lt;code&gt;UnconfinedTestDispatcher&lt;/code&gt; &lt;a href="https://github.com/cashapp/turbine/blob/trunk/src/commonMain/kotlin/app/cash/turbine/flow.kt#L199-L201"&gt;internally&lt;/a&gt;.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;My complaint in &lt;a href="https://kau.sh/blog/coroutine-testing/"&gt;the initial post&lt;/a&gt; around why &lt;code&gt;advanceTimeBy&lt;/code&gt; wasn&amp;rsquo;t working, should now make sense. If you don&amp;rsquo;t use a &lt;code&gt;StandardTestDispatcher&lt;/code&gt; explicitly, then operators like &lt;code&gt;runCurrent&lt;/code&gt;, &lt;code&gt;advanceTimeBy&lt;/code&gt; etc. have no meaning.&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; This is because coroutines are loaded eagerly, especially with vanilla Turbine test usage.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I strongly recommend using &lt;code&gt;StandardTestDispatcher&lt;/code&gt; when you care about concurrency.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Phew! You&amp;rsquo;re now primed with the necessary fundamentals for Coroutine testing. The following posts should be targeted and a breeze.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My thanks to &lt;a href="https://zsmb.co/"&gt;Márton&lt;/a&gt; &amp;amp; &lt;a href="https://www.linkedin.com/in/cubanazcuy/"&gt;Robert&lt;/a&gt; for reviewing this post. Next up: &lt;a href="https://kau.sh/blog/coroutine-testing-backgroundscope"&gt;Never ending tests &amp;amp; backgroundscope&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;and arguably one of the ways coroutines is more complex than RxJava&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;In an earlier version of this post I mentioned CoroutineScopes contain a Dispatcher. This is strictly not true. It can for example have an &lt;code&gt;EmptyCoroutineContext&lt;/code&gt; which is the object that usually contains the Dispatcher, but the EmptyCoroutineContext doesn&amp;rsquo;t specify a &lt;code&gt;Dispatcher&lt;/code&gt; at all. When you &lt;code&gt;launch&lt;/code&gt; though using a builder function, that function supplies the scope, since there&amp;rsquo;s nothing else to use.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;There&amp;rsquo;s a nuance here again. Most places mention the tasks do not execute automatically. That&amp;rsquo;s strictly not true. StandardTestDispatcher itself executes them for example if you manually yield the test thread. But the point still stands in terms of observed behavior/functionality.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;em&gt;I can go into the nuances of Turbine in a future post. It&amp;rsquo;s more involved than a simple UnconfinedTestDispatcher replacement.&lt;/em&gt;&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;Make sure to read my post on &lt;a href="https://kau.sh/blog/coroutine-testing-time"&gt;controlling time&lt;/a&gt; for more on this.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/coroutine-testing-dispatchers/</guid><pubDate>Sun, 25 Aug 2024 07:00:45 GMT</pubDate></item><item><title>
Campground Pull Request</title><link>https://kau.sh/blog/campground-pr/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="campground-pr.jpeg"
alt="Campground PR"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Before diving into a new feature, I always tell my team to scout the parts of the codebase that will need to change. As we explore, refactor for clarity.&lt;/p&gt;
&lt;p&gt;These cleanup changes get their own pull request&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; (PR) before we touch the new feature. This PR shouldn&amp;rsquo;t change functionality, just tidy up the existing code.&lt;/p&gt;
&lt;p&gt;This gives us a few big wins:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Better estimates&lt;/strong&gt;: We can estimate the feature&amp;rsquo;s timeline more accurately now that we have a much better sense of what’s about to change.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smoother reviews&lt;/strong&gt;: Campground PRs are quicker to review, since no functionality has changed. Importantly, you don&amp;rsquo;t clutter your &lt;em&gt;feature&lt;/em&gt; PR with refactors or distracting changes — prompting the ire of code reviewers.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Codebase quality&lt;/strong&gt;: You gradually increase the code quality of your codebase — especially the parts that won’t get touched by your feature PR but could use the love.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is not a new concept , you&amp;rsquo;ve probably seen versions of it before:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Boy Scout Rule&lt;/strong&gt;: Leave your codebase cleaner than you found it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Broken Windows&lt;/strong&gt;: Fix small issues before they proliferate through your code base become big.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I haven&amp;rsquo;t found a popular name for this specific concept, so I’ve started calling them &amp;ldquo;Campground PRs&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t just leave the code better &lt;em&gt;after&lt;/em&gt; your feature, do it &lt;em&gt;before&lt;/em&gt; hand.&lt;/p&gt;
&lt;p&gt;Got a better name? Let me know!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Or &lt;em&gt;merge&lt;/em&gt; request (MR), for the Gitlab folks.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/campground-pr/</guid><pubDate>Sat, 06 Jul 2024 22:52:31 GMT</pubDate></item><item><title>
Adding Tailwind CSS to a Hugo blog</title><link>https://kau.sh/blog/tailwind-hugo/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Updated for &lt;a href="https://tailwindcss.com/blog/tailwindcss-v4"&gt;Tailwind CSS V4&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I veer away from CSS frameworks like Bootstrap because they result in sterilized designs. On a recent side project, I gave &lt;a href="https://tailwindcss.com/"&gt;Tailwind CSS&lt;/a&gt; a try and am now a believer. &amp;ldquo;Rapidly build modern websites without ever leaving your HTML&amp;rdquo; makes a lot of sense to me.&lt;/p&gt;
&lt;p&gt;Case in point: I rebuilt my Hugo theme, &lt;a href="https://kau.sh/henry"&gt;Henry&lt;/a&gt;, with Tailwind in three days.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; I quite like how it&amp;rsquo;s turned out. Let me show you how to add it to a Hugo theme. This has become even simpler with Tailwind CSS v4.&lt;/p&gt;
&lt;h2 id="approach"&gt;
Approach
&lt;a class="heading-anchor" href="#approach" aria-label="Link to Approach"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I like to keep things as simple as possible. So, it was important for me to use both Hugo &amp;amp; Tailwind as you would independently.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t use Tailwind as a PostCSS plugin as much of the internet recommends. I prefer &lt;a href="https://tailwindcss.com/docs/installation/tailwind-cli"&gt;Tailwind CLI&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="installation"&gt;
Installation
&lt;a class="heading-anchor" href="#installation" aria-label="Link to Installation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s assume you created your new site with hugo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo new site my-blog
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd my-blog
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;From this root hugo project directory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# create a package.json file (with default options)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm init -y
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install Tailwind CSS as a dependency&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -D tailwindcss @tailwindcss/cli
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s create the necessary css files for Tailwind to latch on to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p my-blog/assets/css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;touch my-blog/assets/css/input.css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;touch my-blog/assets/css/theme.css
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I use the &lt;code&gt;input.css&lt;/code&gt; file to help Tailwind wire everything together, and &lt;code&gt;output.css&lt;/code&gt; is the final css that Tailwind compiles out.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-css" data-lang="css"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/* input.css */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;tailwindcss&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;@&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;./theme.css&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/* custom css you might want to add as overrides */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! You can now fire up Tailwind &amp;amp; Hugo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# start tailwind&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npx @tailwindcss/cli &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; -i ./assets/css/input.css &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; -o ./assets/css/output.css &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; --watch
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# start hugo server&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo server
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="bonus-tip"&gt;
Bonus tip
&lt;a class="heading-anchor" href="#bonus-tip" aria-label="Link to Bonus tip"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Starting Tailwind and Hugo separately in two different shells is a bore. I use a &lt;code&gt;Makefile&lt;/code&gt; that conveniently allows me to spin up both processes in parallel.&lt;/p&gt;
&lt;p&gt;All I have to do when I&amp;rsquo;m ready to spin up my blog locally is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# runs the default comand which is &amp;#34;run&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# it runs both taliwind css &amp;amp; the hugo server in parallel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Sometimes I want to just build the site, not run the servers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This runs the hugo build command &lt;strong&gt;and&lt;/strong&gt; the tailwind css build command btw.&lt;/p&gt;
&lt;p&gt;You can take a look at my &lt;a href="https://github.com/kaushikgopal/henry-hugo/blob/master/Makefile"&gt;&lt;code&gt;Makefile&lt;/code&gt;&lt;/a&gt; to see how all the commands are wired up:&lt;/p&gt;
&lt;figure class="center borderless"&gt;
&lt;img src="./make.webp"
class="center borderless"
alt="output of make help command"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h1 id="just-use-henry"&gt;
Just use Henry
&lt;a class="heading-anchor" href="#just-use-henry" aria-label="Link to Just use Henry"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;If you just want a solid Hugo theme that uses Tailwind and is easy to get up and running, &lt;a href="https://github.com/kaushikgopal/henry-hugo?tab=readme-ov-file#getting-started"&gt;just use Henry&lt;/a&gt;. This is everything you need to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# brew install hugo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hugo new site blog-henry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd blog-henry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone https://github.com/kaushikgopal/henry-hugo.git themes/henry
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p assets/css
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp themes/henry/assets/css/input.css assets/css/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# brew install node # if you don&amp;#39;t have npm installed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm init -y
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install -D tailwindcss @tailwindcss/cli
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#39;theme = &amp;#34;henry&amp;#34;&amp;#39;&lt;/span&gt; | cat - hugo.toml &amp;gt; temp &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; mv temp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp themes/henry/Makefile ./
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;make
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="revisions"&gt;
Revisions
&lt;a class="heading-anchor" href="#revisions" aria-label="Link to Revisions"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Upgrade to &lt;a href="https://tailwindcss.com/docs/upgrade-guide#changes-from-v3"&gt;Tailwind CSS v4&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Remove convoluted Procfile + foreman setup with much simpler Makefile.&lt;/li&gt;
&lt;li&gt;Changed the instructions to do the &lt;code&gt;npm install&lt;/code&gt; of tailwind in my blog folder vs the theme. This allows more flexibility in customizing via tailwind.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;That&amp;rsquo;s a lot of hand-wrangled css that I could port over pretty quickly.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/tailwind-hugo/</guid><pubDate>Fri, 05 Jul 2024 18:36:23 GMT</pubDate></item><item><title>
Pick the right JDK for Android Studio</title><link>https://kau.sh/blog/studio-jdk/</link><description>
&lt;p&gt;If Android developers don&amp;rsquo;t setup the JDK correctly, you&amp;rsquo;ll be greeted with nasty errors like this:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="java-lang-error.webp"
alt="error shown in Android Studio when incorrect jdk used"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Check your module classpath error
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Cannot access &amp;#39;java.lang.constant.Constable&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;which is a supertype of &amp;#39;java.lang.Class&amp;#39;.
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Check your module classpath for missing or conflicting dependencies
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Your app might compile fine but the IDE will keep surfacing pesky errors. This is because Android developers have a variety of ways to setup the JDK for your development environment. Too many.&lt;/p&gt;
&lt;p&gt;On episode &lt;a href="https://fragmentedpodcast.com/episodes/249-java-and-the-jdk-powering-the-android-landscape-with-michael-bailey/"&gt;#249 of Fragmented&lt;/a&gt; we discussed the numerous JDK options and what they&amp;rsquo;re used for. Google now has an excellent &lt;a href="https://developer.android.com/build/jdks"&gt;developer doc&lt;/a&gt; for this.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="jdks.webp"
alt="different ways for android developer to choose a jdk"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://developer.android.com/build/jdks"&gt;
Courtesy: Android Developer docs
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;If you don&amp;rsquo;t have time to catch up on all the valuable content above, here&amp;rsquo;s the takeaways:&lt;/p&gt;
&lt;h1 id="1-use-the-jdk-that-comes-with-android-studio"&gt;
1. Use the JDK that comes with Android Studio
&lt;a class="heading-anchor" href="#1-use-the-jdk-that-comes-with-android-studio" aria-label="Link to 1. Use the JDK that comes with Android Studio"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;JBR&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; is battle tested with Android Studio and includes enhancements for running Android Studio well. To use it as your SDK, switch your Gradle JDK to JBR:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="jbr.webp"
alt="pick JBR"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
how to set jbr as your gradle jdk
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Do a File &amp;gt; Invalidate Caches after this…&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="invalidate-caches.webp"
alt="Famous Android Studio option - Invalidate Caches"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
mother of all fixes
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h1 id="2-match-yourjava_home-with-the-gradle-jdk"&gt;
2. Match your&lt;code&gt;JAVA_HOME&lt;/code&gt; with the Gradle JDK
&lt;a class="heading-anchor" href="#2-match-yourjava_home-with-the-gradle-jdk" aria-label="Link to 2. Match yourJAVA_HOME with the Gradle JDK"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The Gradle JDK from Step 1 is used to compile your app from Android Studio. If you use the command line to build your app on the other hand, the JDK from your &lt;code&gt;$JAVA_HOME&lt;/code&gt;&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; is used.&lt;/p&gt;
&lt;p&gt;If both of these don&amp;rsquo;t match, you can have multiple gradle daemons running around. Make sure to match the two.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ~/.zshenv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export JAVA_HOME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$HOME/Applications/Android&lt;span style="color:#ae81ff"&gt;\ &lt;/span&gt;Studio.app/Contents/jbr/Contents/Home
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re not sure where the jbr-jdk is located:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="jbr-path.webp"
alt="JBR path"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;This is the way.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;no seriously, it&amp;rsquo;s really good.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;JetBrains Runtime&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;…if you haven&amp;rsquo;t customized your environment too much. It&amp;rsquo;s a little more &lt;a href="https://developer.android.com/build/jdks#jdk-android-studio"&gt;involved&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/studio-jdk/</guid><pubDate>Wed, 06 Mar 2024 08:00:00 GMT</pubDate></item><item><title>
letter.kau.sh – 2024 Mar</title><link>https://kau.sh/letter/3/</link><description>
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/newsletter-banner.webp"
alt="banner for letter.kau.sh"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Welcome to the third installment of my &lt;a href="https://kau.sh/letter/"&gt;newsletter&lt;/a&gt;. Your support as always means everything to me.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dive in.&lt;/p&gt;
&lt;h1 id="what-im-watching-listening-reading"&gt;
What I&amp;rsquo;m watching, listening, reading
&lt;a class="heading-anchor" href="#what-im-watching-listening-reading" aria-label="Link to What I’m watching, listening, reading"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h3 id="video-you"&gt;
[video] &lt;a href="https://www.youtube.com/watch?v=mxURe-EUmAs"&gt;You&amp;rsquo;re Not Forgetful: My System for Memorizing Everything&lt;/a&gt;
&lt;a class="heading-anchor" href="#video-you" aria-label="Link to [video] You"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Most people think they have a terrible memory. I know I did. This video by a YouTuber doctor challenged me to rethink that. I haven&amp;rsquo;t noticed a change in my memory&amp;rsquo;s capacity, but I&amp;rsquo;ve learned &lt;em&gt;how&lt;/em&gt; to remember more.&lt;/p&gt;
&lt;h3 id="video-apple-tv-masters-of-the-air"&gt;
[video] &lt;a href="https://www.themoviedb.org/tv/46518-masters-of-the-air"&gt;Apple TV+ Masters of the Air&lt;/a&gt;
&lt;a class="heading-anchor" href="#video-apple-tv-masters-of-the-air" aria-label="Link to [video] Apple TV&amp;#43; Masters of the Air"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Exceptionally shot. Solid &lt;a href="https://kau.sh/blog/movie-rating-system#3-like"&gt;3.5 rating&lt;/a&gt; and worth the watch. Apple TV+ has been killing it.&lt;/p&gt;
&lt;h3 id="music-video-sample-breakdown-daft-punk---discovery"&gt;
[music video] &lt;a href="https://www.youtube.com/watch?v=5AqHSvR9bqs"&gt;Sample Breakdown: Daft Punk - Discovery&lt;/a&gt;
&lt;a class="heading-anchor" href="#music-video-sample-breakdown-daft-punk---discovery" aria-label="Link to [music video] Sample Breakdown: Daft Punk - Discovery"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Sampling is the art of reusing a portion of a sound recording in another recording. It&amp;rsquo;s had a complicated history, especially with music licensing concerns and whatnot. Young Guru (Jay-Z&amp;rsquo;s tour D.J) has a good &lt;a href="https://www.youtube.com/watch?v=nnIPWgzXvyA"&gt;video&lt;/a&gt; explaining this.&lt;/p&gt;
&lt;p&gt;But to fully appreciate sampling as an art, it&amp;rsquo;s not enough to hear the music; you have to visually &amp;ldquo;see&amp;rdquo; it. See watch Daft Punk &lt;a href="https://www.youtube.com/watch?v=5AqHSvR9bqs"&gt;do their magic&lt;/a&gt; now.&lt;/p&gt;
&lt;h3 id="audio-book-leadership-strategy-and-tactics"&gt;
[audio book] &lt;a href="https://a.co/d/8kkgYyK"&gt;Leadership Strategy and Tactics&lt;/a&gt;
&lt;a class="heading-anchor" href="#audio-book-leadership-strategy-and-tactics" aria-label="Link to [audio book] Leadership Strategy and Tactics"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve been looking for good reads on leadership and this book by an ex-Navy seal came recommended. I have qualms with the narrator&amp;rsquo;s voice but I must say this was a good book with apt analogies.&lt;/p&gt;
&lt;h1 id="tips"&gt;
Tips
&lt;a class="heading-anchor" href="#tips" aria-label="Link to Tips"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="-apple-music"&gt;
🚫 Apple Music
&lt;a class="heading-anchor" href="#-apple-music" aria-label="Link to 🚫 Apple Music"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I use bluetooth headphones with my Mac and it drives me 🦇 💩 🍌 😡 every time I hit the play/pause button and see this window:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="apple-music.webp"
alt="Apple Music Start Window"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I don&amp;rsquo;t use Apple Music. I don&amp;rsquo;t want it to open every time. I&amp;rsquo;d love to disable it. Heck, I&amp;rsquo;d love to uninstall it altogether. Sadly, Apple doesn&amp;rsquo;t allow you to do this. No worries, &lt;a href="https://github.com/tombonez/noTunes"&gt;noTunes&lt;/a&gt; has got your back:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install --cask notunes
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A minor yet significant annoyance, zapped away.&lt;/p&gt;
&lt;h2 id="new-font---commitmono"&gt;
New font - CommitMono
&lt;a class="heading-anchor" href="#new-font---commitmono" aria-label="Link to New font - CommitMono"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Readers of my blog know I&amp;rsquo;m obsessed with monospaced &lt;a href="https://kau.sh/tags/font/"&gt;fonts&lt;/a&gt;. For my daily programming, I used a custom version of &lt;a href="https://kau.sh/blog/freeze-alt-char-open-type-font/"&gt;IBM Plex Mono&lt;/a&gt; and now more often &lt;a href="https://developer.apple.com/fonts/"&gt;San Francisco Mono&lt;/a&gt; &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;. I&amp;rsquo;m weary of using SF Mono though because of &lt;a href="https://github.com/supercomputra/SF-Mono-Font/issues/3#issuecomment-529811872"&gt;Apple&amp;rsquo;s restrictive licensing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Imagine my happiness finding a new worthy contender - &lt;a href="https://commitmono.com/"&gt;CommitMono&lt;/a&gt;. I admire the Danish creator&amp;rsquo;s philosophy around it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are better ways to design than putting a big effort into making something look special. Special is generally less useful than normal, and less rewarding in the long term. Special things demand attention for the wrong reasons, interrupting potentially good atmosphere with their awkward presence.&lt;/p&gt;
&lt;p&gt;— Jasper Morrison, Super Normal Manifesto&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There&amp;rsquo;s an option to customize it and you bet I did…&lt;/p&gt;
&lt;figure &gt;
&lt;img src="commit-mono-customization.webp"
alt="Customizations for CommitMono"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I do wish there was an alternative version of &amp;ldquo;r&amp;rdquo; with the extender at the baseline. That would make it 🧑‍🍳 💋.&lt;/p&gt;
&lt;h1 id="signing-off"&gt;
Signing off
&lt;a class="heading-anchor" href="#signing-off" aria-label="Link to Signing off"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This was a tough month. A few of my colleagues and friends who I deeply admire were let go. Turbulent times in tech :/.&lt;/p&gt;
&lt;p&gt;Thank you once again for your support and readership. Your &lt;a href="https://kau.sh/contact/"&gt;feedback&lt;/a&gt; is always welcome.&lt;/p&gt;
&lt;p&gt;Until the next one.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;which comes baked in with the Mac&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/letter/3/</guid><pubDate>Fri, 01 Mar 2024 08:00:00 GMT</pubDate></item><item><title>
Option ⌥ key for shortcuts vs special characters</title><link>https://kau.sh/blog/option-keyboard-shortcut-macos/</link><description>
&lt;p&gt;Ever tried using the option key (&lt;code&gt;⌥&lt;/code&gt;) with another character as a keyboard shortcut in a macOS app?&lt;/p&gt;
&lt;p&gt;For example, in &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt; I use &lt;code&gt;⌥ r&lt;/code&gt; to replace templates in the active file; or &lt;code&gt;⌥ a&lt;/code&gt; to archive completed tasks for the day.&lt;/p&gt;
&lt;p&gt;Using the &lt;em&gt;default English &amp;gt; U.S.&lt;/em&gt; keyboard (or &lt;em&gt;ABC&lt;/em&gt;) types special characters in your app like å or ® vs executing your app shortcuts. It&amp;rsquo;s frustrating.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="kb-US-ABC.png"
alt="macOS option for keyboard input type"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;If you&amp;rsquo;re heavy keyboard shortcut or a programmer wanting to use more keyboard shortcuts in your IDE like me, you&amp;rsquo;ll derive more use from ⌥ being used as a keyboard shortcut modifier.&lt;/p&gt;
&lt;h2 id="solution-1-"&gt;
Solution 1 ✗
&lt;a class="heading-anchor" href="#solution-1-" aria-label="Link to Solution 1 ✗"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m not one to shy away from &lt;a href="https://kau.sh/blog/hacking-your-keyboard/"&gt;keyboard hacking&lt;/a&gt;. So I tried Karabiner to force remap the &lt;code&gt;⌥&lt;/code&gt; keystroke when typed with another character, sending it directly to the application. That didn&amp;rsquo;t work.&lt;/p&gt;
&lt;p&gt;I then pulled out my trusty &lt;a href="https://kau.sh/tags/maestro/"&gt;Keyboard Maestro&lt;/a&gt; which is the tool I fall back to when all else fails. Nope, macOS seems to hijack these keystrokes at a deeper level.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="solution-2-"&gt;
Solution 2 ~
&lt;a class="heading-anchor" href="#solution-2-" aria-label="Link to Solution 2 ~"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s a quicker solution&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; – pick a different keyboard. A lot of online posts specifically recommend a &amp;ldquo;Unicode Hex Input&amp;rdquo; keyboard.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="kb-uhi.png"
alt="macOS option for keyboard input type"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;You can start using &lt;code&gt;⌥ a&lt;/code&gt; without å showing up, no sweat and the keyboard shortcuts work. The Unicode Hex Input keyboard is additionally cool cause it allows typing out unicode characters with their full code. Here&amp;rsquo;s an example: hold the &lt;code&gt;⌥&lt;/code&gt; key and type &lt;code&gt;03c0&lt;/code&gt; to see what you get.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Problem solved? Not completely.&lt;/p&gt;
&lt;p&gt;Open a program like &lt;a href="https://ia.net/writer"&gt;iA writer&lt;/a&gt;&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; or &lt;a href="https://arc.net/"&gt;Arc&lt;/a&gt; and try using the keyboard shortcut &lt;code&gt;⌥ ⇧ →&lt;/code&gt; or &lt;code&gt;⌥ ⇧ ←&lt;/code&gt;. These basic macOS wide shortcuts should select the next/previous word respectively. Those no longer work. Let&amp;rsquo;s see if there&amp;rsquo;s any other options in there?&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="kb-uhi-options.png"
alt="macOS option for keyboard input type"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;The selection shortcut start working in some apps but not all of them. Sigh. The search continues.&lt;/p&gt;
&lt;h2 id="solution-3-"&gt;
Solution 3 ✓
&lt;a class="heading-anchor" href="#solution-3-" aria-label="Link to Solution 3 ✓"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After scouring forums, apple support threads, reddit etc. I finally found a solution that works.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll need to create a custom keyboard and provide it as an Input source to the Mac (as you saw in earlier screenshots). You can use a program like &lt;a href="https://github.com/sillsdev/Ukelele"&gt;Ukelele&lt;/a&gt; or this &lt;a href="https://wordherd.com/keyboards/"&gt;online tool&lt;/a&gt; that someone kindly created for free.&lt;/p&gt;
&lt;p&gt;Alternatively, &lt;a href="https://gist.github.com/kaushikgopal/8e8c6e641746afe53117c2194212a0c3"&gt;download&lt;/a&gt; the keyboard I made called &amp;ldquo;US Plain&amp;rdquo; and copy it over to your &lt;code&gt;~/Library/Keyboard Layouts&lt;/code&gt; directory. Log out of the mac and log in again. When you go to add a keyboard in the Input Sources menu, you&amp;rsquo;ll now find your keyboard under &amp;ldquo;Others&amp;rdquo;.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="kb-USP.png"
alt="macOS option for keyboard input type"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;This solution reliably resolves all issues.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;probably redirecting control character codes differently 🤷🏽.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;one would incorrectly assume.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;π&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;my favorite macOS app for writing.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/option-keyboard-shortcut-macos/</guid><pubDate>Sun, 04 Feb 2024 08:00:00 GMT</pubDate></item><item><title>
letter.kau.sh – 2024 Feb</title><link>https://kau.sh/letter/2/</link><description>
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/newsletter-banner.webp"
alt="banner for letter.kau.sh"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Welcome to the second installment of my &lt;a href="https://kau.sh/letter/"&gt;newsletter&lt;/a&gt;. Your support means the world to me.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been told the key to building an audience is consistency. I&amp;rsquo;m committing to this idea by releasing my letter on the first day of each month. This way, you&amp;rsquo;ll always know when to look out for a new letter.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dive in.&lt;/p&gt;
&lt;h1 id="what-im-watching-listening-reading"&gt;
What I&amp;rsquo;m watching, listening, reading
&lt;a class="heading-anchor" href="#what-im-watching-listening-reading" aria-label="Link to What I’m watching, listening, reading"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;[podcast] &lt;a href="https://pod.link/1710609544"&gt;What Now?&lt;/a&gt; – Trevor Noah&amp;rsquo;s standup is good but he&amp;rsquo;s an even better interview host. His &lt;a href="https://youtu.be/MJ9F9ZuQIzE"&gt;&amp;ldquo;Between the Scenes&amp;rdquo;&lt;/a&gt; &lt;a href="https://www.youtube.com/watch?v=TLhJCerHFAw"&gt;segments&lt;/a&gt; are some of my favorite. His new podcast is really good so far.&lt;/li&gt;
&lt;li&gt;[tv] &lt;a href="https://www.themoviedb.org/tv/95480-slow-horses"&gt;Slow Horses&lt;/a&gt; – Just finished Season 1 and I&amp;rsquo;m giddy that there are two additional seasons waiting for me. The best feeling.&lt;/li&gt;
&lt;li&gt;[tv] &lt;a href="https://www.themoviedb.org/tv/225180-blue-eye-samurai?language=en-US"&gt;Blue Eye Samurai&lt;/a&gt; – I&amp;rsquo;ve swooned about this show before but I&amp;rsquo;ll promote it again any chance I get. It&amp;rsquo;s the best thing I watched in 2023.&lt;/li&gt;
&lt;li&gt;[video] &lt;a href="https://youtu.be/1mY5FNRh0h4"&gt;Where are U Now | Diary of a Song&lt;/a&gt; – Yes, it&amp;rsquo;s the one you&amp;rsquo;re thinking of, and yes it&amp;rsquo;s Beiber, but this video is specifically around how Diplo &amp;amp; Skrillex collaborated to &lt;em&gt;make&lt;/em&gt; the song. I&amp;rsquo;ve got a bunch of these to share, so stay tuned.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="tech-tips"&gt;
Tech Tips
&lt;a class="heading-anchor" href="#tech-tips" aria-label="Link to Tech Tips"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="making-use-of-the-tmp-directory"&gt;
Making use of the &lt;code&gt;/tmp&lt;/code&gt; directory
&lt;a class="heading-anchor" href="#making-use-of-the-tmp-directory" aria-label="Link to Making use of the /tmp directory"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="linux-file-system.webp"
alt="cheatsheet for linux file system"
height="650"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://twitter.com/alexxubyte/status/1753457840541634626"&gt;
Courtesy: Alex Xu via Twitter
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Are you aware that Unix-based systems, like macOS, offer a &amp;ldquo;hidden&amp;rdquo; &lt;code&gt;/tmp&lt;/code&gt; directory that clears out every time you reboot?&lt;/p&gt;
&lt;p&gt;I set it as my default download location for files that are momentarily needed like downloads, screenshots, or installation packages. There&amp;rsquo;s no need for a manual cleanup of these files as your next reboot will just wipe them clean.&lt;/p&gt;
&lt;p&gt;Caveat: since this directory is &lt;em&gt;world-writable&lt;/em&gt;, other user profiles on your device can access these files.&lt;/p&gt;
&lt;h2 id="hiding-your-macos-dock"&gt;
Hiding your macOS dock
&lt;a class="heading-anchor" href="#hiding-your-macos-dock" aria-label="Link to Hiding your macOS dock"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The Dock is one of those macOS features that I never use and strangely, it can&amp;rsquo;t be disabled. Nicholas shared a &lt;a href="https://www.threads.net/@jitkoff/post/C2DII-sJXCR/"&gt;workaround&lt;/a&gt; that effectively hides it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;defaults write com.apple.dock autohide-delay -int &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; killall Dock
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s been working like a charm.&lt;/p&gt;
&lt;h1 id="from-my-blog"&gt;
From my blog
&lt;a class="heading-anchor" href="#from-my-blog" aria-label="Link to From my blog"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h3 id="get-to-inbox-0-with-gmail"&gt;
&lt;a href="https://kau.sh/blog/inbox-0-gmail/"&gt;Get to Inbox 0 with Gmail&lt;/a&gt;
&lt;a class="heading-anchor" href="#get-to-inbox-0-with-gmail" aria-label="Link to Get to Inbox 0 with Gmail"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I wrote this post excited to share my tips on taming an email inbox. I shared it with the author of the plugin mentioned. He was kind and gracious but told me that my post was vapid (without using those words). He&amp;rsquo;s right. I rushed that post in an excitement to get it out. I oversold with that title.&lt;/p&gt;
&lt;p&gt;This is why I love the internet and am willing to &lt;a href="https://kau.sh/blog/learning-and-looking-foolish/"&gt;look foolish&lt;/a&gt;. It means I&amp;rsquo;m learning. I&amp;rsquo;ve since been thinking about re-editing that post. The title should have just been - how &lt;em&gt;I&lt;/em&gt; use Gmail.&lt;/p&gt;
&lt;h3 id="replace-git-number-with-git-aliases"&gt;
&lt;a href="https://kau.sh/blog/git-alias/"&gt;Replace git-number with git aliases&lt;/a&gt;
&lt;a class="heading-anchor" href="#replace-git-number-with-git-aliases" aria-label="Link to Replace git-number with git aliases"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I wanted to go on more about my &lt;a href="https://kau.sh/blog/simplify"&gt;simplify&lt;/a&gt; series and how pumped I am about a queue of posts I have in mind for this topic. But reason and restraint found me and I figured folks will find their way to it, in time.&lt;/p&gt;
&lt;p&gt;But if you&amp;rsquo;re curious… dear newsletter reader (❤️) the ones I&amp;rsquo;m thinking of: &amp;ldquo;How I Obsidian&amp;rdquo;, &amp;ldquo;iTerm → Terminal &amp;amp; sane dotfiles&amp;rdquo;, &amp;ldquo;Fish → Zsh&amp;rdquo;, &amp;ldquo;VSCode + NeoVim → Jetbrains&amp;rdquo;, &amp;ldquo;KeyboardMaestro + Alfred + Pastebot + WorldClock widget + … → Raycast&amp;rdquo;&lt;/p&gt;
&lt;p&gt;If there&amp;rsquo;s one that you&amp;rsquo;re eager to read, &lt;a href="https://kau.sh/contact/"&gt;let me know&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="musings"&gt;
Musings
&lt;a class="heading-anchor" href="#musings" aria-label="Link to Musings"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="programming-for-good"&gt;
Programming for good
&lt;a class="heading-anchor" href="#programming-for-good" aria-label="Link to Programming for good"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;As a programmer, I&amp;rsquo;m always looking for opportunities to apply my skills to benefit society and perform my civic duty. The opportunities are there…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The San Francisco subway system still runs on &lt;a href="https://sfstandard.com/transportation/sfs-market-street-subway-runs-on-reagan-era-floppy-disks/"&gt;5 1/4-inch floppies&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;By way of &lt;a href="https://kottke.org/23/12/52-interesting-things-i-learned-2023"&gt;Jason Kotke&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;SF has the best software engineers in the world and they&amp;rsquo;re riding the very Munis that run on 5¼ floppies, all around San Francisco. Yet, there isn&amp;rsquo;t an obvious way to volunteer time to help improve these systems. 💡&lt;/p&gt;
&lt;h2 id="free-stuff-getting-better-over-time"&gt;
Free stuff getting better over time
&lt;a class="heading-anchor" href="#free-stuff-getting-better-over-time" aria-label="Link to Free stuff getting better over time"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re reading this letter on a browser, you&amp;rsquo;ll notice I&amp;rsquo;ve spruced the website up a lot. I&amp;rsquo;m going to write more about the numerous subtle features a casual reader might miss.&lt;/p&gt;
&lt;p&gt;But every time I start to work on it, there&amp;rsquo;s this guilt of sinking time into &lt;a href="https://github.com/kaushikgopal/henry-hugo"&gt;the effort&lt;/a&gt;. But I&amp;rsquo;m writing this to remind future me: it&amp;rsquo;s ok. Working on &lt;a href="https://kau.sh/henry"&gt;Henry&lt;/a&gt; is meditative for me - low stakes programming that makes atleast one person happy (me).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also something fulfilling about the idea of improving the work you give out for free, over time. How often can you say that about other free things in life? 🧘🏽&lt;/p&gt;
&lt;h1 id="resolutions--goals"&gt;
Resolutions &amp;amp; &lt;a href="https://kau.sh/letter/1#lets-kick-this-off"&gt;Goals&lt;/a&gt;
&lt;a class="heading-anchor" href="#resolutions--goals" aria-label="Link to Resolutions &amp;amp; Goals"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="tolerance-in-thinking-platformer-leaves-substack"&gt;
[Tolerance in thinking] Platformer leaves Substack
&lt;a class="heading-anchor" href="#tolerance-in-thinking-platformer-leaves-substack" aria-label="Link to [Tolerance in thinking] Platformer leaves Substack"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Casey Newton on &lt;a href="https://www.platformer.news/why-platformer-is-leaving-substack/"&gt;leaving substack&lt;/a&gt;. I thought this was a measured response and he went about it the right way. I was especially curious how he&amp;rsquo;d handle the usual questions around freedom of speech &amp;amp; tolerance in thinking. His FAQ at the end does a good job.&lt;/p&gt;
&lt;h2 id="self-care-back-to-weightlifting"&gt;
[Self care] Back to Weightlifting
&lt;a class="heading-anchor" href="#self-care-back-to-weightlifting" aria-label="Link to [Self care] Back to Weightlifting"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;My transformation from a 100kg (220 lbs) teenager to a 70kg (154 lbs) fit young adult was anything but easy. It wasn&amp;rsquo;t inspiration but severe back pain that drove me. An elderly chiropractor candidly remarked &amp;ldquo;you&amp;rsquo;re the youngest non-injured patient I&amp;rsquo;ve ever had in my career&amp;rdquo;. That scared the shit out of me cause that dude was &lt;strong&gt;old&lt;/strong&gt;. I learnt then that the only activity I was consistent with is weightlifting (thankfully).&lt;/p&gt;
&lt;figure &gt;
&lt;img src="workout-x.webp"
alt="workout image poster"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I&amp;rsquo;m aiming to regain my former fitness by starting simple. For now, I&amp;rsquo;m alternating between two full-body workouts that I found on this slapstick &lt;a href="https://www.youtube.com/watch?v=U9ENCvFf9yQ&amp;amp;t=445s"&gt;YouTube&lt;/a&gt; channel.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="workout-1.webp"
alt="workout image 1"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;figure &gt;
&lt;img src="workout-2.webp"
alt="workout image 2"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Next month, I&amp;rsquo;ll switch it up with a different set.&lt;/p&gt;
&lt;h1 id="signing-off"&gt;
Signing off
&lt;a class="heading-anchor" href="#signing-off" aria-label="Link to Signing off"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Thank you genuinely for subscribing or reading my letters. I&amp;rsquo;d love to always &lt;a href="https://kau.sh/contact/"&gt;hear more from you&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What would you like to see more or less of — tech insights, programming chatter, personal stories?&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m finding my way. I&amp;rsquo;d appreciate all the help.&lt;/p&gt;
&lt;p&gt;Until the next one.&lt;/p&gt;</description><guid>https://kau.sh/letter/2/</guid><pubDate>Thu, 01 Feb 2024 20:00:00 GMT</pubDate></item><item><title>
Simplify &amp; replace git-number with git aliases</title><link>https://kau.sh/blog/git-alias/</link><description>
&lt;p&gt;I wrote a blog post about &lt;a href="https://kau.sh/blog/git-number/"&gt;git-number&lt;/a&gt; and how useful it was for my command line git usage. In an on-going effort to &lt;a href="https://kau.sh/blog/simplify/"&gt;simplify&lt;/a&gt; my lifestyle &amp;amp; setup, I replaced git-number with a few git aliases. I&amp;rsquo;ll explain how I recreated the functionality in this blog post.&lt;/p&gt;
&lt;h2 id="why-replace-it"&gt;
Why replace it?
&lt;a class="heading-anchor" href="#why-replace-it" aria-label="Link to Why replace it?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Looking at git-number&amp;rsquo;s &lt;a href="https://github.com/holygeek/git-number"&gt;source&lt;/a&gt; I realize it is written in Perl. I&amp;rsquo;m not here to cast judgement on another programming language but reading the code, it felt like overkill for my use cases.&lt;/p&gt;
&lt;p&gt;It also meant I could eliminate one additional 3rd party library that I have to install every time.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go ahead and recreate it with some good old unix command line fu.&lt;/p&gt;
&lt;h1 id="building-git-add-number"&gt;
Building git add &lt;code&gt;&amp;lt;number&amp;gt;&lt;/code&gt;
&lt;a class="heading-anchor" href="#building-git-add-number" aria-label="Link to Building git add &amp;lt;number&amp;gt;"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;figure &gt;
&lt;img src="git-add-1.webp"
alt="git status showing files"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;To git add the first file, I have to manually type the entire path &lt;code&gt;git add 0.collapse-categories-into...&lt;/code&gt;. This is tedious and error-prone. If your file is nested in a series of directories (like an Android or iOS app), it becomes worse.&lt;/p&gt;
&lt;p&gt;With git-number, this is as simple as&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git-number add &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# where 1 stands for the order&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# in which a file shows up in git status&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s recreate that with a git alias.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# in your .gitconfig file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;alias&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; !&lt;span style="color:#e6db74"&gt;&amp;#34;a() { &amp;lt;UNIX COMMAND GOES HERE&amp;gt; ; }; a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key now is to come up with a unix command that will take in a number and pluck that file for you. Here&amp;rsquo;s a simple version of that command:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="git-add-4.webp"
alt="pluck line"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;You can now make it a git alias by adding it to your .gitconfig file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .gitconfig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;alias&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; !&lt;span style="color:#e6db74"&gt;&amp;#34;a() { git add &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | cut -b4- | sed -n &lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;$1p;$1q&lt;span style="color:#ae81ff"&gt;\&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; }; a&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;img src="git-add-x.webp"
alt="git add demo"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;You can systematically recreate all of the git-number functionality in pure git aliases!&lt;/p&gt;
&lt;h1 id="bonus-tweaks"&gt;
Bonus tweaks
&lt;a class="heading-anchor" href="#bonus-tweaks" aria-label="Link to Bonus tweaks"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Over time, I&amp;rsquo;ve tweaked the above command to a behemoth shell command that accounts for a variety of use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;g a&lt;/code&gt; - git add all files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g a .&lt;/code&gt; - git add all files&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g a 2-3&lt;/code&gt; - git add second and third file&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g a 3-5&lt;/code&gt; - git add second &lt;em&gt;to&lt;/em&gt; fifth file&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s what my actual alias is, in all its glory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .gitconfig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;alias&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;!f() { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$#&lt;span style="color:#e6db74"&gt; -eq 0 || \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34; == \&amp;#34;.\&amp;#34; ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add .; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add=(); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for arg in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | wc -l&lt;span style="color:#66d9ef"&gt;))&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add+=(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | sed -n &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;arg&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;p | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;-&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;-&amp;#39; read -ra RANGE &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[0]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; elif [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;,&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;,&amp;#39; read -ra NUMS &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;NUMS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$1-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }; f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="understanding-the-command"&gt;
Understanding the command:
&lt;a class="heading-anchor" href="#understanding-the-command" aria-label="Link to Understanding the command:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In this day of chat-gpt you don&amp;rsquo;t need to understand the above command. The &lt;em&gt;&lt;strong&gt;idea&lt;/strong&gt;&lt;/em&gt; and how to leverage it, is far more important.&lt;/p&gt;
&lt;h3 id="other-git-commands"&gt;
Other git commands?
&lt;a class="heading-anchor" href="#other-git-commands" aria-label="Link to Other git commands?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;An earlier version of me would have put this in a tidy shell script, made it executable &amp;amp; repeatable; then called the script from my gitconfig alias.&lt;/p&gt;
&lt;p&gt;A more experienced version of current me knows, I shouldn&amp;rsquo;t waste more time on prettifying this. It works, is zippy, and fits in my .gitconfig without adding any more dependencies. I&amp;rsquo;m probably not going to touch this for a long time.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# .gitconfig&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;alias&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;!f() { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$#&lt;span style="color:#e6db74"&gt; -eq 0 || \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34; == \&amp;#34;.\&amp;#34; ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add .; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add=(); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for arg in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | wc -l&lt;span style="color:#66d9ef"&gt;))&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add+=(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | sed -n &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;arg&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;p | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;-&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;-&amp;#39; read -ra RANGE &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[0]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; elif [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;,&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;,&amp;#39; read -ra NUMS &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;NUMS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git add \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$1-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }; f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; r &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;!f() { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$#&lt;span style="color:#e6db74"&gt; -eq 0 || \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34; == \&amp;#34;.\&amp;#34; ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git reset .; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add=(); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for arg in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | wc -l&lt;span style="color:#66d9ef"&gt;))&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add+=(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | sed -n &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;arg&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;p | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;-&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;-&amp;#39; read -ra RANGE &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[0]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git reset \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; elif [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;,&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;,&amp;#39; read -ra NUMS &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;NUMS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git reset \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git reset \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$1-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }; f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ch &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;!f() { \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$#&lt;span style="color:#e6db74"&gt; -eq 0 || \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34; == \&amp;#34;.\&amp;#34; ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git checkout .; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add=(); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for arg in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | wc -l&lt;span style="color:#66d9ef"&gt;))&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; files_to_add+=(&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status --porcelain | sed -n &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;arg&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;p | awk &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;); \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; if [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;-&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;-&amp;#39; read -ra RANGE &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in &lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;seq &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[0]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;RANGE[1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git checkout \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; elif [[ &lt;/span&gt;$1&lt;span style="color:#e6db74"&gt; == *&amp;#39;,&amp;#39;* ]]; then \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; IFS=&amp;#39;,&amp;#39; read -ra NUMS &amp;lt;&amp;lt;&amp;lt; \&amp;#34;&lt;/span&gt;$1&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; for i in \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;NUMS[@]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; do \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git checkout \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$i-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; done; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; else \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; git checkout \&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;files_to_add[$1-1]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;\&amp;#34;; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; fi; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; }; f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# and so on ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It took me 5 seconds to add the other commands. I&amp;rsquo;m not going to waste time tidying it up, cause it just works.&lt;/p&gt;
&lt;p&gt;One less dependency in &lt;a href="https://kau.sh/blog/simplify"&gt;this journey&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/git-alias/</guid><pubDate>Sat, 27 Jan 2024 20:00:00 GMT</pubDate></item><item><title>
Let me help you get to Inbox 0 with Gmail</title><link>https://kau.sh/blog/inbox-0-gmail/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;As penance for that click baity title, I won&amp;rsquo;t bury the lede. These are the tools you need:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Gmail filters&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simpl.fyi/about"&gt;Simpl.fyi plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Read on for specifics and examples.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ve tested several email apps — Mimestream, Superhuman, and Hey — but none quite stuck. They either pushed me too far from Gmail&amp;rsquo;s setup or simply didn&amp;rsquo;t resonate with me. The closest match was &lt;a href="https://en.wikipedia.org/wiki/Inbox_by_Gmail"&gt;Inbox&lt;/a&gt;, until Google notoriously put the kibosh on that.&lt;/p&gt;
&lt;p&gt;Imagine my surprise then, when I discovered &lt;a href="https://leggett.org/"&gt;Michael Legett&lt;/a&gt;, (Inbox co-founder) maintained a web extension that tries to emulate Inbox.&lt;/p&gt;
&lt;figure class="full-bleed"&gt;
&lt;img src="gmail.webp"
class="full-bleed"
alt="my gmail inbox for 3 days with and without the simplified plugin"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;One of the obvious benefits of this extension is how clean and actionable Gmail looks now. But aesthetics alone won&amp;rsquo;t get you to inbox nirvana. It&amp;rsquo;s the killer feature &amp;ldquo;Bundles&amp;rdquo;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; &lt;a href="https://beta.simpl.fyi/"&gt;currently in beta&lt;/a&gt; that will.&lt;/p&gt;
&lt;p&gt;Simpl.fyi consolidates related emails and organizes them in a collapsible format. For instance, my subscribed newsletter emails are grouped under &amp;ldquo;news,&amp;rdquo; while bank related emails are under &amp;ldquo;bank&amp;rdquo; and so on. This inline grouping allows me to go through my emails pretty swiftly. I can speed read GitHub/Jira/Calendar notifications and archive them all in one click.&lt;/p&gt;
&lt;p&gt;But it gets better… Simpl.fyi Bundles are just &lt;a href="https://support.google.com/mail/answer/118708?hl=en"&gt;Gmail labels&lt;/a&gt; and you have complete control of them with &lt;a href="https://support.google.com/mail/answer/6579?hl=en"&gt;Gmail filters&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Simpl.fyi bundles are just Gmail labels&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="gmail-filters"&gt;
Gmail filters
&lt;a class="heading-anchor" href="#gmail-filters" aria-label="Link to Gmail filters"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Gmail filters allow you to add certain rules and have your emails be tagged with a label.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure class="borderless"&gt;
&lt;img src="filters-settings.webp"
class="borderless"
alt="how to get to gmail filters"
height="450"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;figure class="borderless"&gt;
&lt;img src="filters-mine.webp"
class="borderless"
alt="the filters i currently have"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;You can follow Gmail&amp;rsquo;s support doc to see how to &lt;a href="https://support.google.com/mail/answer/6579?hl=en"&gt;create basic filters&lt;/a&gt;. But this is an advanced class, so let&amp;rsquo;s have more fun with it.&lt;/p&gt;
&lt;h2 id="gmail-advanced-filters"&gt;
Gmail advanced filters
&lt;a class="heading-anchor" href="#gmail-advanced-filters" aria-label="Link to Gmail advanced filters"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="mix-and-match-conditions"&gt;
Mix and match conditions
&lt;a class="heading-anchor" href="#mix-and-match-conditions" aria-label="Link to Mix and match conditions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Gmail filters treat curly braces &lt;code&gt;{ }&lt;/code&gt; as an &lt;code&gt;OR&lt;/code&gt; condition &amp;amp; parenthesis &lt;code&gt;( )&lt;/code&gt; as an &lt;code&gt;AND&lt;/code&gt; condition.&lt;/p&gt;
&lt;p&gt;Say I want any email that has the word &amp;ldquo;receipt&amp;rdquo; in the subject of the email to be labeled &amp;ldquo;receipts&amp;rdquo;, I create the gmail filter with the condition:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;subject&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;receipt&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="borderless tac"&gt;
&lt;img src="filters-create.webp"
class="borderless tac"
alt="the filters i currently have"
width="500"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Easy.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But Lyft &amp;amp; Uber also send me receipts and don&amp;rsquo;t add &amp;ldquo;receipt&amp;rdquo; in the subject line. I&amp;rsquo;d like to include them with this same label. I know Lyft sends their emails from an address &lt;code&gt;no-reply@lyftmail.com&lt;/code&gt; and Uber tags their emails as coming from &amp;ldquo;Uber Receipts&amp;rdquo;. This filter should catch it all:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;subject&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;receipt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Uber Receipts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;no&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;reply&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;lyftmail&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But say you don&amp;rsquo;t want to include the receipts from stripe (for some odd reason). I change the filter to add an &lt;code&gt;AND&lt;/code&gt; and &lt;code&gt;NOT&lt;/code&gt; condition:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;subject&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;receipt&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Uber Receipts&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;no&lt;/span&gt;&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;reply&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;lyftmail&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt;@&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;stripe&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should allow you to target any email with a variety of conditions.&lt;/p&gt;
&lt;h3 id="use-gmails-categories"&gt;
Use Gmail&amp;rsquo;s categories
&lt;a class="heading-anchor" href="#use-gmails-categories" aria-label="Link to Use Gmail’s categories"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;At some point in time, Gmail had its own &amp;ldquo;categories&amp;rdquo; like &amp;ldquo;Travel&amp;rdquo;, &amp;ldquo;Purchases&amp;rdquo; etc. They do a pretty good job filtering your incoming mail picking up on a lot of these filters. If you want to leverage those in your filters. For example here is my filter for all &amp;ldquo;travel&amp;rdquo;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;category&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;travel&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;OR&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;from&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;aa&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;delta&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;com&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Couple of notes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;American &amp;amp; Delta weren&amp;rsquo;t caught by the category, so I used the previous tip and included them forcefully&lt;/li&gt;
&lt;li&gt;Gmail filters are forgiving with the syntax, so I&amp;rsquo;m intentionally showing a less terse way to write this filter. But the good old brackets approach works reliably.&lt;/li&gt;
&lt;li&gt;&amp;ldquo;Travel&amp;rdquo; is a reserved keyword for category and Gmail won&amp;rsquo;t allow you to use it as a label. Keen observers will therefore note my screenshot saying &amp;ldquo;travl&amp;rdquo;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="simplfyi"&gt;
Simpl.fyi
&lt;a class="heading-anchor" href="#simplfyi" aria-label="Link to Simpl.fyi"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Simplify Gmail is a &lt;a href="https://simpl.fyi/plans"&gt;paid plugin&lt;/a&gt; and runs about $2 per month if you go with the annual plan like me. Before you&amp;rsquo;re scared away by that, I encourage you to give the free trial a shot.&lt;/p&gt;
&lt;p&gt;Bundles is just one of many many features that come with it. That alone makes it personally worth every cent for me but there&amp;rsquo;s a bajillion other customizations that you might fancy.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;You can find Michael&amp;rsquo;s in-depth video about this feature on &lt;a href="https://www.youtube.com/watch?v=CPTcZKcFtMA"&gt;YouTube&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Gmail (in)famously doesn&amp;rsquo;t do folders, everything is just a label.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I&amp;rsquo;ve found entering my entire criteria in &amp;ldquo;Has the words&amp;rdquo; is enough.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/inbox-0-gmail/</guid><pubDate>Mon, 15 Jan 2024 20:00:00 GMT</pubDate></item><item><title>
letter.kau.sh – 2024 Jan</title><link>https://kau.sh/letter/1/</link><description>
&lt;figure class="tac borderless"&gt;
&lt;img src="newsletter.webp"
class="tac borderless"
alt="lady holding letters"
width="400"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h1 id="welcome-to-my-newsletter"&gt;
Welcome to my newsletter!
&lt;a class="heading-anchor" href="#welcome-to-my-newsletter" aria-label="Link to Welcome to my newsletter!"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Why start a newsletter when I have a blog? I love writing and I want to write more. But I want a more casual format, where I can share without the burden of permanence or meticulousness that a &amp;ldquo;blog post&amp;rdquo; demands.&lt;/p&gt;
&lt;p&gt;While I continue to use social media, like others I remain skeptical of its future as a reliable destination for this sort of writing.&lt;/p&gt;
&lt;h2 id="what-to-expect"&gt;
What to expect
&lt;a class="heading-anchor" href="#what-to-expect" aria-label="Link to What to expect"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Think of this truly, as letters from me to you. I intend to share quick tips and tricks; helpful software I’ve installed and am using; hardware that I’m trying out and recommend; books, blog posts or other newsletters that I’m reading; thoughts on what’s going on in my personal life; lessons and learnings.&lt;/p&gt;
&lt;p&gt;I want to be brave with ideas and opinions that are still baking in my head… but could also turn out wrong.&lt;/p&gt;
&lt;h2 id="what-is-the-format"&gt;
What is the format
&lt;a class="heading-anchor" href="#what-is-the-format" aria-label="Link to What is the format"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I don’t know if I want to keep these posts &lt;em&gt;short&lt;/em&gt; but I promise to keep them as easy reads. The newsletters I love, share this quality.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m aiming for a monthly cadence and will nest these under &lt;a href="https://kau.sh/letter/"&gt;/letter&lt;/a&gt; but it should also show up in the general feed. You’re under no obligation to read these btw if all you want are the regular posts. Since I use my own theme for this site&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; there&amp;rsquo;s ∞ flexibility. You can choose a customized feed based on your liking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/feed.xml"&gt;/feed.xml&lt;/a&gt; — all the stuff I write&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/feed.xml"&gt;/blog/feed.xml&lt;/a&gt; - “blog posts” only&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/letter/feed.xml"&gt;/letter/feed.xml&lt;/a&gt; - “newsletter” only&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Slap &lt;code&gt;/feed.xml&lt;/code&gt; (or &lt;code&gt;/feed.json&lt;/code&gt;) at the end of most urls here, and you&amp;rsquo;ll get a feed for that topic (e.g. my &lt;a href="https://kau.sh/tags/programming/"&gt;posts on programming&lt;/a&gt; are at &lt;a href="https://kau.sh/tags/programming/feed.xml"&gt;/tags/programming/feed.xml&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re an email gal/guy on the other hand, open this page in a browser and all the way at the bottom is a way to add your email to a list. You&amp;rsquo;ll get these letters in your inbox, as soon as I post them.&lt;/p&gt;
&lt;h1 id="lets-kick-this-off"&gt;
Let&amp;rsquo;s kick this off!
&lt;a class="heading-anchor" href="#lets-kick-this-off" aria-label="Link to Let’s kick this off!"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/newsletter-banner.webp"
alt="banner for letter.kau.sh"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Happy 2024 🥳! If you haven&amp;rsquo;t noticed, I&amp;rsquo;m big on &lt;a href="https://kau.sh/tags/new-year/"&gt;new-year&lt;/a&gt; posts and resolutions. Here are my resolutions for 2024:&lt;/p&gt;
&lt;h2 id="brevity-in-writing"&gt;
Brevity in writing
&lt;a class="heading-anchor" href="#brevity-in-writing" aria-label="Link to Brevity in writing"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I want to get better and sharper with my writing.&lt;/p&gt;
&lt;p&gt;Two books that are classics that I should dust off and read again: &lt;a href="https://en.wikipedia.org/wiki/The_Elements_of_Style"&gt;Elements of Style&lt;/a&gt; &amp;amp; &lt;a href="https://amzn.to/3ROwj4S"&gt;On Writing Well&lt;/a&gt;. I follow the social media account &lt;a href="https://www.threads.net/@techemails/post/C1k85lsPeko"&gt;tech emails&lt;/a&gt;. In addition to the 🍿 I&amp;rsquo;m impressed by the brevity with how the top executives correspond and write. No superfluous adverbs or flowery adjectives. I remember an online discussion&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; where people questioned, &amp;ldquo;Why do these leaders write like this? Feels bizarrely direct and grammatically lacking or incomplete&amp;rdquo;. An interesting response went something like this: &amp;ldquo;these people &lt;strong&gt;cannot afford to be misunderstood&lt;/strong&gt;, so are as direct as possible. They dispense with flowery &amp;amp; verbose language, and even with grammar at times, if it gets the point across better&amp;rdquo;. Not saying I want to emulate this but I&amp;rsquo;d like to improve my own writing for clarity.&lt;/p&gt;
&lt;h2 id="tolerance-in-thinking"&gt;
Tolerance in thinking
&lt;a class="heading-anchor" href="#tolerance-in-thinking" aria-label="Link to Tolerance in thinking"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I want to have healthy discussions with people having differing opinions.&lt;/p&gt;
&lt;p&gt;We live in a polarized time where if someone holds a differing ideology, everything that comes out of their mouth must be wrong or opposed. Over the 2023 Xmas holidays, I was watching &lt;a href="https://en.wikipedia.org/wiki/The_West_Wing"&gt;The West Wing&lt;/a&gt; since it&amp;rsquo;s one of those shows that&amp;rsquo;s recommended highly. It reminded me of Sorkin&amp;rsquo;s &lt;a href="https://en.wikipedia.org/wiki/The_Newsroom_%28American_TV_series%29"&gt;The Newsroom&lt;/a&gt; which came out later and is one of my favorites. I love how this idea is pushed in both shows. Why can friends not hold opposing ideologies today? How can we be more graceful in debate?&lt;/p&gt;
&lt;h2 id="focus-on-self-care"&gt;
Focus on self care
&lt;a class="heading-anchor" href="#focus-on-self-care" aria-label="Link to Focus on self care"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I will carve out time and be conscious about my self-care practice.&lt;/p&gt;
&lt;p&gt;I was actually inspired by one of my colleagues on this one. Self care is about making sure you&amp;rsquo;re spending time to do things that inspire and invigorate you. Self care can be fitness, it can be watching that TV show that only you seem to like in your family, it can be eating healthier, it can be taking the time to talk to a therapist or a friend more equipped to listen. What it shouldn&amp;rsquo;t be… is an after thought.&lt;/p&gt;
&lt;h2 id="new-ventures-in-tech"&gt;
New ventures in tech
&lt;a class="heading-anchor" href="#new-ventures-in-tech" aria-label="Link to New ventures in tech"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I have two new ideas&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; in this space that I&amp;rsquo;d like to make a reality.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fragmentedpodcast.com/"&gt;Fragmented&lt;/a&gt; — the podcast I started with &lt;a href="https://www.donnfelker.com/"&gt;Donn&lt;/a&gt; is on a hiatus&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; of sorts. But I&amp;rsquo;ve been cooking something else in the background, so stay tuned. I&amp;rsquo;m not ready to share more just yet but I&amp;rsquo;ve been thinking about these for sometime now and am excited to see them materialize in 2024.&lt;/p&gt;
&lt;p&gt;Thank you genuinely for subscribing or reading my newsletter. I&amp;rsquo;d love to hear more from you too. What are your resolutions for 2024?&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;that I&amp;rsquo;ve &lt;a href="https://github.com/kaushikgopal/henry-hugo"&gt;open sourced&lt;/a&gt; btw&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;can&amp;rsquo;t to find it now.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;details to come in the future 😉&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;more on that soon too.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/letter/1/</guid><pubDate>Wed, 03 Jan 2024 18:48:58 GMT</pubDate></item><item><title>
Slashed 0 in IBM Plex - freeze alt. characters in otf fonts</title><link>https://kau.sh/blog/freeze-alt-char-open-type-font/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="./loki-freeze-variant-otf.webp"
alt="Spotlight Alfred show Terminal.app and iTerm"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;OpenType fonts have font &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/OpenType_fonts_guide#discovering_availability_of_features_in_fonts"&gt;features or variants&lt;/a&gt; that you can use to represent certain characters differently.&lt;/p&gt;
&lt;p&gt;For example, my beloved &lt;a href="https://kau.sh/my-new-programming-font"&gt;IBM Plex Mono&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; has quite a few alternative glyphs to pick from:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/blog/freeze-alt-char-open-type-font/ibm-plex-mono-repertoire.webp" alt="ibm-plex-mono-repertoire.webp"&gt;&lt;/p&gt;
&lt;p&gt;I prefer the single story &amp;ldquo;g&amp;rdquo; and slashed &amp;ldquo;0&amp;rdquo; characters for clarity. But you&amp;rsquo;ll find no obvious way to turn these version of the characters on permanently. After searching and trying quite a few ways to do this, I found an approach that works pretty well.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This blog post will explain how you can pick alternative characters that come with certain open type fonts like IBM Plex Mono and build a version of the font that will permanently switch these alternative character variants on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="step-1-obtain-the-complete-otf-font-file"&gt;
Step 1: obtain the complete .otf font file
&lt;a class="heading-anchor" href="#step-1-obtain-the-complete-otf-font-file" aria-label="Link to Step 1: obtain the complete .otf font file"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s important to have your source &lt;code&gt;.otf&lt;/code&gt; file have the complete list of characters. If your font doesn&amp;rsquo;t have these to begin with, this approach will not work. As an example, IBM Plex Mono&amp;rsquo;s full otf can be &lt;a href="https://github.com/IBM/plex/releases/tag/v6.3.0"&gt;downloaded&lt;/a&gt; here.&lt;/p&gt;
&lt;h2 id="step-2-identify-the-alternative-character-sets-you-want"&gt;
Step 2: identify the alternative character sets you want
&lt;a class="heading-anchor" href="#step-2-identify-the-alternative-character-sets-you-want" aria-label="Link to Step 2: identify the alternative character sets you want"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Upload your font to &lt;a href="https://wakamaifondue.com"&gt;wakamaifondue.com&lt;/a&gt;. You&amp;rsquo;ll get a neat summary of your font and all the available features variants.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/blog/freeze-alt-char-open-type-font/wakamaifondue-plex-mono.webp" alt="wakamaifondue-plex-mono.webp"&gt;&lt;/p&gt;
&lt;h2 id="step-3-use-pyftfeatfreeze"&gt;
Step 3: use pyftfeatfreeze
&lt;a class="heading-anchor" href="#step-3-use-pyftfeatfreeze" aria-label="Link to Step 3: use pyftfeatfreeze"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/twardoch/fonttools-opentype-feature-freezer"&gt;pyftfeatfreeze&lt;/a&gt; is a fantastic open source tool that allows you to create a new version of your font with the specific set of alternative characters chosen.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the entire set of commands I used to generate my variant of IBM Plex Mono:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#!/bin/zsh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;fontSuffix&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;KG&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# create a new directory for your font&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p &lt;span style="color:#e6db74"&gt;&amp;#34;IBM-Plex-Mono-&lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# change to directory that has original otf font&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd IBM-Plex-Mono
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install the tool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pipx install opentype-feature-freezer
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Pick the right combination of stylistic characters&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# By default IBM Plex Mono ships:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Regular - styled g; styled a (with caps)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Italic - plain g ; plain a (without cap)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# I prefer keeping it all consistent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# so I change my italic fonts to also have the styled versions of g/a&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In the non-italic versions of IBM Plex Mono, change&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ss03 - slashed 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f in &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;fd -HI ibmplexmono -e otf -E &lt;span style="color:#e6db74"&gt;&amp;#39;*Italic.otf&amp;#39;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyftfeatfreeze -f &lt;span style="color:#e6db74"&gt;&amp;#34;ss03&amp;#34;&lt;/span&gt; -R &lt;span style="color:#e6db74"&gt;&amp;#34;Mono/Mono &lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -v $f &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;f%.otf&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.kg.otf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# In the italicized versions of IBM Plex Mono, change&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ss01 - use stylistic variant of a (with the hat)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ss02 - use g with a cap&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ss03 - slashed 0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f in &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;fd -HI Italic.otf -e otf&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; pyftfeatfreeze -f &lt;span style="color:#e6db74"&gt;&amp;#34;ss03,ss01,ss02&amp;#34;&lt;/span&gt; -R &lt;span style="color:#e6db74"&gt;&amp;#34;Mono/Mono &lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; -v $f &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;f%.otf&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;.kg.otf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cleanup, rename and open the directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mv *.kg.otf ../&lt;span style="color:#e6db74"&gt;&amp;#34;IBM-Plex-Mono-&lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd ../&lt;span style="color:#e6db74"&gt;&amp;#34;IBM-Plex-Mono-&lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f in *.kg.otf; &lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mv $f &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$f&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; | sed s/&lt;span style="color:#e6db74"&gt;&amp;#34;\.kg&amp;#34;&lt;/span&gt;/&lt;span style="color:#e6db74"&gt;&amp;#34;-&lt;/span&gt;$fontSuffix&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;/&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;open .
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve created a repo&lt;/p&gt;
&lt;p&gt;Double click the fonts and you have a customized version of IBM Plex Mono installed!&lt;/p&gt;
&lt;h2 id="bonus"&gt;
Bonus
&lt;a class="heading-anchor" href="#bonus" aria-label="Link to Bonus"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you just want the same version of IBM Plex Mono that I use, install it from my &lt;a href="https://github.com/kaushikgopal/homebrew-tools"&gt;homebrew tap&lt;/a&gt; directly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew tap kaushikgopal/tools
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install --cask font-ibm-plex-mono-kg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;which I continue to use as my preferred monospace font for programming.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/freeze-alt-char-open-type-font/</guid><pubDate>Mon, 27 Nov 2023 01:41:42 GMT</pubDate></item><item><title>
Toggle window snapping with Keyboard Maestro</title><link>https://kau.sh/blog/toggle-move-resize-window-keyboard-maestro/</link><description>
&lt;p&gt;Shopify CEO Tobi Lütke in a tweet last year mentioned why he uses the &lt;a href="https://twitter.com/tobi/status/1479092213959254017?s=20"&gt;Rectangle app&lt;/a&gt; even through Raycast has some of these options already. Rectangle allows you to &amp;ldquo;toggle&amp;rdquo; the window to different positions.&lt;/p&gt;
&lt;p&gt;I was inspired by this and wanted to be able to do the same. But in keeping with my theme to &lt;a href="https://kau.sh/blog/simplify"&gt;Simplify&lt;/a&gt;, I didn&amp;rsquo;t want to add a new tool to my arsenal.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.stairways.com/action/kmdiscount?REF6JZA"&gt;Keyboard Maestro&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; to the rescue!&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="https://github.com/kaushikgopal/km-macros/raw/master/screenshots/window-snap-toggling.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;This set of macros allow you to toggle between a few states:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Toggle to the left ½ ¼ ⅔ of the screen (with the same shortcut)&lt;/li&gt;
&lt;li&gt;Toggle to the right ½ ¼ ⅔ of the screen&lt;/li&gt;
&lt;li&gt;Toggle full screen and back&lt;/li&gt;
&lt;li&gt;Toggle to the center of the screen and back&lt;/li&gt;
&lt;li&gt;Toggle vertically&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="https://github.com/kaushikgopal/km-macros/blob/master/Window%20Movers%20Toggle%20Macros.kmmacros"&gt;Download the Keyboard Maestro Macro group here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;referral link&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/toggle-move-resize-window-keyboard-maestro/</guid><pubDate>Sun, 16 Jul 2023 19:22:07 GMT</pubDate></item><item><title>
Supercharge Kscript with Github Copilot</title><link>https://kau.sh/blog/kscript-copilot/</link><description>
&lt;p&gt;One of the gifts we got from &lt;a href="https://github.com/Kotlin/kotlin-script-examples/blob/master/jvm/main-kts/MainKts.md#usage"&gt;Kotlin 1.3.70&lt;/a&gt; was the ability to effortlessly run a kscript (Kotlin script) from the command line. All you need to do is add the suffix &lt;code&gt;.main.kts&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Seriously, this is all you need to do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install kotlin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#e6db74"&gt;&amp;#39;#!/usr/bin/env kotlin&amp;#39;&lt;/span&gt; &amp;gt; hello-world.main.kts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;chmod +x hello.main.kts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;./hello.main.kts
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 💥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this 0 friction setup, there&amp;rsquo;s little reason for me to ever write a bash script again. I get to write my script in Kotlin — a modern &amp;amp; beautiful language. But slap on &lt;a href="https://github.com/features/copilot"&gt;Github Copilot&lt;/a&gt; and I think we&amp;rsquo;ve unlocked the holy grail of quick scripting.&lt;/p&gt;
&lt;p&gt;To prove my point, I recorded a video live coding a script tackling a moderately involved task. Here&amp;rsquo;s what I wanted to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Take a directory path as argument&lt;/li&gt;
&lt;li&gt;Make sure it&amp;rsquo;s valid and has at least one .md (markdown) file in it&lt;/li&gt;
&lt;li&gt;Search the &lt;a href="https://gohugo.io/content-management/front-matter/"&gt;front matter&lt;/a&gt; for the &amp;ldquo;categories&amp;rdquo; array&lt;/li&gt;
&lt;li&gt;pluck all the categories from all the files&lt;/li&gt;
&lt;li&gt;de-duplicate, sort and print them out&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The entire thing took less than 4½ minutes. This is including me figuring out how to get autocomplete working in my IDE, restarting it etc.&lt;/p&gt;
&lt;p&gt;Sure I could try to wield a one-line command and do the same thing for this use case but it&amp;rsquo;s so much nicer in Kotlin.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/Tr2YBmecdw4?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;rsquo;re intrigued, feel free to checkout my &lt;a href="https://github.com/kaushikgopal/kotlin-scripts"&gt;github repo of kscripts&lt;/a&gt;. I try to tackle a variety of common use cases in there&lt;/p&gt;
&lt;p&gt;&lt;em&gt;P.S: The text snippet I use in there &lt;code&gt;kscript;;&lt;/code&gt; basically gives me a starter &lt;a href="https://github.com/kaushikgopal/kotlin-scripts/blob/master/0.hello.main.kts"&gt;hello world template&lt;/a&gt;. Not cheating, I promise.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/kscript-copilot/</guid><pubDate>Sun, 07 May 2023 01:25:34 GMT</pubDate></item><item><title>
The magic that is Zakir (👂🏽) and his Tabla</title><link>https://kau.sh/blog/ustad-zakir-magic/</link><description>
&lt;p&gt;One of my resolutions this year was to start picking up the Tabla again&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; courtesy a not so gentle nudge from my supportive partner.&lt;/p&gt;
&lt;p&gt;This has obviously led me to scouring Youtube for videos of the great maestros. &lt;a href="https://en.wikipedia.org/wiki/Zakir_Hussain_%28musician%29"&gt;Ustad Zakir Hussain&lt;/a&gt; is one of them.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a few videos from my collection with some context explaining why he&amp;rsquo;s a gift to the world of music.&lt;/p&gt;
&lt;h3 id="what-is-the-tabla"&gt;
What is the Tabla?
&lt;a class="heading-anchor" href="#what-is-the-tabla" aria-label="Link to What is the Tabla?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you want to get a quick peek at this supremely delicate Indian percussion instrument here&amp;rsquo;s a starter video.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/vipORh_YJCc?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Zakir (👂🏽) is worshipped like a rockstar in the Indian classical music scene. A video of literally the start of the concert where the audience goes nuts.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/URmMPYoH2-8?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id="zakir--helping-the-sound-guy-out"&gt;
Zakir (👂🏽) helping the sound guy out
&lt;a class="heading-anchor" href="#zakir--helping-the-sound-guy-out" aria-label="Link to Zakir (👂🏽) helping the sound guy out"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/9lfMz6Y1-5M?rel=0}&amp;start=180&amp;end=223"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;After tapping the stage he says &amp;ldquo;Low-Mids Thoda Kam Karo&amp;rdquo; (reduce the low-mids a little). Just from hearing the acoustics of the stage he&amp;rsquo;s able to tell the sound guy to reduce the &lt;a href="https://www.masteringthemix.com/blogs/learn/understanding-the-different-frequency-ranges"&gt;low-mids&lt;/a&gt;. You have to be a master of sound to be able to do that.&lt;/p&gt;
&lt;h3 id="zakir--messing-with-the-sound-guy"&gt;
Zakir (👂🏽) messing with the sound guy
&lt;a class="heading-anchor" href="#zakir--messing-with-the-sound-guy" aria-label="Link to Zakir (👂🏽) messing with the sound guy"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/QE1HkrZTeIY?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Doesn&amp;rsquo;t get flustered or annoyed. His child like curiosity with the moment gets him to use it as a prop for his show.&lt;/p&gt;
&lt;h3 id="respect-for-other-maestros"&gt;
Respect for other maestros
&lt;a class="heading-anchor" href="#respect-for-other-maestros" aria-label="Link to Respect for other maestros"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;He is without doubt considered one of the grand masters of the Tabla even by most of his peers. But he doesn&amp;rsquo;t let it get to his head. Here&amp;rsquo;s how he reacts when he meets some of his contemporaries.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/IwLr95Y4oIo?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Pandit &lt;a href="https://en.wikipedia.org/wiki/Yogesh_Samsi"&gt;Yogesh Samsi&lt;/a&gt; btw learned under the tutelage of Zakir&amp;rsquo;s(👂🏽) father — Ustad &lt;a href="https://en.wikipedia.org/wiki/Alla_Rakha"&gt;Allah Rakha Khan&lt;/a&gt; .&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; Bonus content: A touching moment between Pandit Yogesh Samsi and Ustad Allah Rakha.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/dlu5PXMNdJM?rel=0}&amp;start=1110&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;rsquo;s one with another great Tablist of our time — Pandit&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; &lt;a href="https://en.wikipedia.org/wiki/Anindo_Chatterjee"&gt;Anindo Chatterjee&lt;/a&gt;.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/BMMUj9QDyG0?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Another one of those tender moments right at the start. Read the text after the video for context.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/6sHi-DK-Gc8?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Singer Hariharan as a mark of respect says &amp;ldquo;Ijazat ?&amp;rdquo; (roughly translating to &amp;ldquo;permission&amp;rdquo; in Urdu). While the vocalist is usually the primary performer, the accompanying percussion artist here is one of the most knowledgeable souls in Indian classical music. Zakir (👂🏽) without missing a beat responds &amp;ldquo;Bismillah&amp;rdquo; which translates to &amp;ldquo;by the grace of God&amp;rdquo;, implying he has no permission to give.&lt;/p&gt;
&lt;h3 id="ok-so-whats-with-the-"&gt;
Ok, so what&amp;rsquo;s with the 👂🏽?
&lt;a class="heading-anchor" href="#ok-so-whats-with-the-" aria-label="Link to Ok, so what’s with the 👂🏽?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/F_K3G37Vb_A?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;This comes from an age old practice in India. Addressing teachers, masters or mentors with their names is considered forward and disrespectful - especially true in Indian classical music. So every time the pupil mentions the teacher&amp;rsquo;s name or plays their composition, they instinctively touch their ear as a mark of respect and an ask of forgiveness if they don&amp;rsquo;t do the same levels of justice to the teacher&amp;rsquo;s composition.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;While I don&amp;rsquo;t share much publicly about my classical music background, after coding it&amp;rsquo;s the activity I&amp;rsquo;ve spent most of my life pursuing.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Yes they&amp;rsquo;re a family of percussion wizards and before Zakir, his father was considered one of the greatest Tabla players of our generation.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Ustad is a title for musical maestros that practice Islam and Pandit for those Hinduism. Countries were divided on account of the differences between these two religions; but not in music ♥️.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/ustad-zakir-magic/</guid><pubDate>Sat, 06 May 2023 02:10:55 GMT</pubDate></item><item><title>
This year I shall Simplify</title><link>https://kau.sh/blog/simplify/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="simplify.webp"
alt="A Deiter Rams-esque rendering of a radio with the word Simplify on it"
width="450"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I&amp;rsquo;m always trying out new tools to make my life easier. In a cruel twist of irony however, I&amp;rsquo;ve found myself customizing and maintaining multiple tools leading to unnecessary complexity and a hit in productivity.&lt;/p&gt;
&lt;p&gt;So this year, I&amp;rsquo;ve made a resolution to simplify and eliminate redundant tools. Nothing is safe from the chopping block!&lt;/p&gt;
&lt;p&gt;Here are some of the major transitions I&amp;rsquo;ve made or plan to make, and I&amp;rsquo;ll be sharing more about each of them in upcoming posts.&lt;/p&gt;
&lt;h2 id="things-3--obsidian-notes"&gt;
Things 3 → Obsidian Notes
&lt;a class="heading-anchor" href="#things-3--obsidian-notes" aria-label="Link to Things 3 → Obsidian Notes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This one surprised me the most. I switched from &lt;a href="https://culturedcode.com/things/"&gt;Things 3&lt;/a&gt; as my Todo app and &lt;a href="https://obsidian.md"&gt;Obsidian&lt;/a&gt; for my notes to using Obsidian for both. While Things 3 has a beautiful UI, I discovered that I could use Obsidian as effectively (if not more) to get things done with the addition of one plugin. Stay tuned for a detail write up on this one.&lt;/p&gt;
&lt;h2 id="iterm--terminal"&gt;
iTerm → Terminal
&lt;a class="heading-anchor" href="#iterm--terminal" aria-label="Link to iTerm → Terminal"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Next, I switched from iTerm to Terminal. This transition was easy since I wasn&amp;rsquo;t relying too heavily on iTerm customizations anyway. The big reason I used iTerm was mostly just for horizaontal split tabs. I dusted off my &lt;a href="https://github.com/tmux/tmux/wiki"&gt;tmux&lt;/a&gt; skills and now am back to just using Terminal.app.&lt;/p&gt;
&lt;h2 id="fish--zsh"&gt;
fish → zsh
&lt;a class="heading-anchor" href="#fish--zsh" aria-label="Link to fish → zsh"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Moving from &lt;a href="https://fishshell.com"&gt;fish&lt;/a&gt; to &lt;a href="https://en.wikipedia.org/wiki/Z_shell"&gt;zsh&lt;/a&gt; — on the other hand — was the most challenging transition since I had years of accumulated scripts and functions that I had to painstakingly port over to zsh. However, I&amp;rsquo;m happy I made the change since zsh is the default on a new Mac, making it easier to use across different devices.&lt;/p&gt;
&lt;h2 id="alfred--keyboard-maestro--pastebot--raycast"&gt;
Alfred + Keyboard Maestro (+ Pastebot) → Raycast
&lt;a class="heading-anchor" href="#alfred--keyboard-maestro--pastebot--raycast" aria-label="Link to Alfred &amp;#43; Keyboard Maestro (&amp;#43; Pastebot) → Raycast"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;As an avid user of &lt;a href="https://www.alfredapp.com"&gt;Alfred&lt;/a&gt; and &lt;a href="https://www.keyboardmaestro.com/main/"&gt;Keyboard Maestro&lt;/a&gt;, I was hesitant to switch to &lt;a href="https://www.raycast.com"&gt;Raycast&lt;/a&gt;. But after watching a video on its capabilities, I realized that Raycast could replace several of the tools I was using. I was particularly impressed by how easy it was to expand its functionality and add new snippets or quick URL searches.&lt;/p&gt;
&lt;p&gt;Keyboard Maestro is going to be around for those quick automation macros but I&amp;rsquo;m going to try and not have it running constantly. More for one-off automation jobs.&lt;/p&gt;
&lt;h2 id="vscode--neovim--jetbrains"&gt;
VSCode → (Neo)Vim (&amp;amp; Jetbrains)
&lt;a class="heading-anchor" href="#vscode--neovim--jetbrains" aria-label="Link to VSCode → (Neo)Vim (&amp;amp; Jetbrains)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;While I initially switched to VSCode because of its excellent plugins, I found myself using Jetbrains editors like Intellij and Android Studio more frequently. Additionally, I&amp;rsquo;m already super comfortable with Vim, so the few times I would use VSCode for text editing, there was no good reason not to just use Vim. With the introduction of Intellij &amp;ldquo;light mode&amp;rdquo;, I found myself just using Jetbrains editors more often. So, I&amp;rsquo;m slowly weaning myself off of VSCode.&lt;/p&gt;
&lt;h3 id="not-just-software"&gt;
Not just Software!
&lt;a class="heading-anchor" href="#not-just-software" aria-label="Link to Not just Software!"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m also applying the principle of simplification to my hardware and day-to-day physical setup. However, I don&amp;rsquo;t substitute a tool that performs well just because I have another that does something else. The key is to find a balance and ensure that using a tool brings joy and value.&lt;/p&gt;</description><guid>https://kau.sh/blog/simplify/</guid><pubDate>Sat, 22 Apr 2023 22:53:13 GMT</pubDate></item><item><title>
Tootbot for Mastodon</title><link>https://kau.sh/blog/toot-bot-post-mastodon-automatically-kotlin-script/</link><description>
&lt;p&gt;Every time I create a new post in this blog I like to &lt;a href="https://mastodon.kau.sh"&gt;toot&lt;/a&gt; or &lt;a href="https://twitter.kau.sh"&gt;tweet&lt;/a&gt; about it. Instead of manually doing this, I have my Mac Mini run a little shell script on a cron job, that does this automatically for me.&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://github.kau.sh/tootbot"&gt;download the source for this script over at github&lt;/a&gt;. The way it works is pretty simple:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download my latest blog feed as a json file.&lt;/li&gt;
&lt;li&gt;Check for any new blog posts.&lt;/li&gt;
&lt;li&gt;Compares with a local CSV file that records previous toots.&lt;/li&gt;
&lt;li&gt;Posts new toots to Mastodon&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Running the script is trivially easy (and it&amp;rsquo;s all in Kotlin) so it&amp;rsquo;s easy to &lt;a href="https://github.com/kaushikgopal/tootbot/blob/master/tootbot.main.kts#L41"&gt;follow the logic&lt;/a&gt;. All you need to do to run this script is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install kotlin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kotlin tootbot.main.kts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! Just remember to &lt;a href="https://github.com/kaushikgopal/tootbot#script-variables"&gt;update the variables&lt;/a&gt; at the top of the file.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Not doing Twitter &lt;a href="https://twitter.com/TwitterDev/status/1621026986784337922"&gt;for obvious reasons&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/toot-bot-post-mastodon-automatically-kotlin-script/</guid><pubDate>Sat, 04 Feb 2023 03:00:00 GMT</pubDate></item><item><title>
Own your online presence - Step 1 ☝️</title><link>https://kau.sh/blog/own-your-online-presence/</link><description>
&lt;p&gt;Owning your content and where you put it is crucial in the online world. The first step to establishing your corner on the internet is to choose a domain name.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;After obtaining a domain name, many people only set up a website. However, I recommend taking the next step of creating subdomain redirects for your social media handles.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="hunting-woolly-mammoth.webp"
alt="mammoth elephant being attacked"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://en.wikipedia.org/w/index.php?curid=52261934"&gt;
By Cloudinary, CC BY-SA 4.0
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;For example, when mastodon.social suffered a &lt;a href="https://mastodon.social/@Gargron/109781490892884305"&gt;DDoS&lt;/a&gt; attack, I moved to &lt;a href="https://hachyderm.io"&gt;hachyderm.io&lt;/a&gt;,&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; but the mastodon link I hand out to folks remained unchanged.&lt;/p&gt;
&lt;p&gt;How you ask? It&amp;rsquo;s &lt;a href="https://mastodon.kau.sh"&gt;mastodon.kau.sh&lt;/a&gt; and I control the redirect. I just switched it intenally from &lt;code&gt;mastodon.social/@kaushikgopal&lt;/code&gt; → &lt;code&gt;hachyderm.io/@kaush&lt;/code&gt;.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This is also why I am not terribly worried about changing my handles on Github, Twitter, LinkedIn, or YouTube, as I have redirects for all of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.kau.sh"&gt;github.kau.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.kau.sh"&gt;twitter.kau.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.kau.sh"&gt;linkedin.kau.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://youtube.kau.sh"&gt;youtube.kau.sh&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To take control of your online presence, buy your own domain, adjust your DNS settings,&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; and start using your own URLs. It&amp;rsquo;s a simple first step with a big impact.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;After &lt;a href="https://kaushikgopal.com"&gt;many&lt;/a&gt; &lt;a href="https://kaush.co"&gt;many&lt;/a&gt; &lt;a href="https://jkl.gg"&gt;attempts&lt;/a&gt;, I landed on &lt;a href="https://kau.sh"&gt;one&lt;/a&gt; that I liked. I use &lt;a href="https://namecheap.pxf.io/EaL5yX"&gt;namecheap&lt;/a&gt; (referral code)&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;It was already on my mind to be honest and I guess this was the push I
needed.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;The added benefit is that I don&amp;rsquo;t have to remember or type Mastodon&amp;rsquo;s unattractive URLs.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://developers.cloudflare.com/fundamentals/get-started/basic-tasks/manage-domains/redirect-domain/"&gt;Cloudflare makes these trivially
easy&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/own-your-online-presence/</guid><pubDate>Fri, 03 Feb 2023 03:13:13 GMT</pubDate></item><item><title>
Share your macOS audio with multiple multiple bluetooth headphones or Airpods</title><link>https://kau.sh/blog/stream-audio-multiple-headphones-mac/</link><description>
&lt;p&gt;When I travel on airplanes, I prefer to download content onto my 14&amp;quot; Macbook&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; so I can watch it offline during the flight. When my wife travels with me, we like catching up on some of the shows that we like to watch together.&lt;/p&gt;
&lt;p&gt;But we both have our own pair of Airpods that we&amp;rsquo;d like to use to listen to the audio together. In the wired headphones days, I&amp;rsquo;d carry a headphone splitter that we&amp;rsquo;d plug both of our wired headphones into.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/78/Headphone_mic_splitter_adapter.jpg/320px-Headphone_mic_splitter_adapter.jpg"
alt="Headphone splitter"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://commons.wikimedia.org/wiki/File:Headphone_mic_splitter_adapter.jpg"&gt;
Courtesy: Wikimedia
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;This blog post provides instructions for the equivalent solution in our wireless/bluetooth headphones world.&lt;/p&gt;
&lt;h1 id="software"&gt;
Software
&lt;a class="heading-anchor" href="#software" aria-label="Link to Software"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;m going to use Rogue Amoeba&amp;rsquo;s excellent solution &lt;a href="https://rogueamoeba.com/loopback/"&gt;Loopback&lt;/a&gt; in the below instructions. I&amp;rsquo;m pretty sure you can also use &lt;a href="https://github.com/mattingalls/Soundflower"&gt;Soundflower&lt;/a&gt;, the native &amp;ldquo;Audio MIDI setup&amp;rdquo; app or other solutions. But Loopback makes this ludicrously easy.&lt;/p&gt;
&lt;h2 id="step-1-pair-headphones-to-the-mac"&gt;
Step 1: Pair headphones to the Mac
&lt;a class="heading-anchor" href="#step-1-pair-headphones-to-the-mac" aria-label="Link to Step 1: Pair headphones to the Mac"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Pair each of the headphones individually to the Mac (as you typically would).&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="stream-audio.1.webp"
width="35%"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;After pairing the first set, you might have to disconnect them (temporarily) and proceed to pair the second set individually.&lt;/p&gt;
&lt;h2 id="step-2-create-a-virtual-multi-output-device"&gt;
Step 2: Create a Virtual multi-output device
&lt;a class="heading-anchor" href="#step-2-create-a-virtual-multi-output-device" aria-label="Link to Step 2: Create a Virtual multi-output device"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Create a new virtual device in Loopback &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="stream-audio.2.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;h2 id="step-3-add-both-headphones-to-the-output-channel"&gt;
Step 3: Add both headphones to the output channel
&lt;a class="heading-anchor" href="#step-3-add-both-headphones-to-the-output-channel" aria-label="Link to Step 3: Add both headphones to the output channel"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Gen2-2023&lt;/code&gt; is the bluetooth headphones I use in the below instructions. Add both headphones to the Output channels.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="stream-audio.3a.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;If you&amp;rsquo;re wondering how both headphones connect directly, chalk it to the wonders of the Mac. While the first headphones is connected, connect your second pair (without disconnecting the first) and they both should be connected.&lt;/p&gt;
&lt;video controls
width="50%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="stream-audio.3b.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;You probably will still only hear the output from one of the headphones, but we&amp;rsquo;re almost there.&lt;/p&gt;
&lt;h2 id="step-4-switch-mac-audio-to-new-virtual-device"&gt;
Step 4: Switch Mac audio to new virtual device
&lt;a class="heading-anchor" href="#step-4-switch-mac-audio-to-new-virtual-device" aria-label="Link to Step 4: Switch Mac audio to new virtual device"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;figure class="borderless"&gt;
&lt;div align="center"&gt;
&lt;img src="stream-audio.4.webp"
class="borderless"
width="60%"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;If you&amp;rsquo;re all aboard the Rogue Amoeba wagon, &lt;a href="https://rogueamoeba.com/soundsource/"&gt;Soundsource&lt;/a&gt; is another app they maintain, that makes it trivially easy to switch audio sources. For someone who keeps switching workstations, podcasts etc. this is delightful software.&lt;/em&gt;&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="stream-audio.5.gif"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id="step-5-play--enjoy"&gt;
Step 5: Play &amp;amp; Enjoy
&lt;a class="heading-anchor" href="#step-5-play--enjoy" aria-label="Link to Step 5: Play &amp;amp; Enjoy"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="stream-audio.6.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;Loopback also allows you to control volumes separately which is a godsend when you have two picky people.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;An iPad would probably be better but I remain a iPhone + Macboook only kind of guy.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;This would be a &amp;ldquo;Multi-Output device&amp;rdquo; in the Audio MIDI app but Loopback simplifies these distinctions.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/stream-audio-multiple-headphones-mac/</guid><pubDate>Sun, 29 Jan 2023 05:27:58 GMT</pubDate></item><item><title>
2022 Retro</title><link>https://kau.sh/blog/2022/</link><description>
&lt;h1 id="looking-back-at-2022"&gt;
Looking back at 2022
&lt;a class="heading-anchor" href="#looking-back-at-2022" aria-label="Link to Looking back at 2022"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;🎉 Bought a new home 🤯 2022-02-22&lt;/li&gt;
&lt;li&gt;Joined Caper as Head of Android&lt;/li&gt;
&lt;li&gt;🙈 Got Covid&lt;/li&gt;
&lt;li&gt;Put &lt;a href="https://kau.sh/blog/productivity"&gt;productivity system&lt;/a&gt; in place&lt;/li&gt;
&lt;li&gt;Setup &lt;a href="log/2022/07/#mac-mini-is-back-online"&gt;Mac Mini server&lt;/a&gt;, Tailscale &amp;amp; Homebridge&lt;/li&gt;
&lt;li&gt;Introduced Henry 2.0 &amp;amp; major updates to the blog&lt;/li&gt;
&lt;li&gt;Moved domain name over to kau.sh&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="2022-goals"&gt;
2022 Goals
&lt;a class="heading-anchor" href="#2022-goals" aria-label="Link to 2022 Goals"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;✅ Moved from goofy jkl.gg → kau.sh&lt;/li&gt;
&lt;li&gt;Make home smarter
&lt;ul&gt;
&lt;li&gt;✅ Automated blind shades&lt;/li&gt;
&lt;li&gt;✅ Clean network topology (LAN in all rooms)&lt;/li&gt;
&lt;li&gt;Smart Alarm&lt;/li&gt;
&lt;li&gt;Video Camera feed inside&lt;/li&gt;
&lt;li&gt;✅ Unify all of existing systems with Homebridge&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Resume Fragmented&lt;/li&gt;
&lt;li&gt;Learn to play two songs on the Piano&lt;/li&gt;
&lt;li&gt;Learning Kotlin Coroutines by example&lt;/li&gt;
&lt;li&gt;Revive TrueTime from the dead (Kotlinify + new algorithm)&lt;/li&gt;
&lt;li&gt;Health &amp;amp; Fitness
&lt;ul&gt;
&lt;li&gt;get to 170lbs (started the year at 185lbs)&lt;/li&gt;
&lt;li&gt;go from 36inches → 32inches&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="2023-goals"&gt;
2023 Goals
&lt;a class="heading-anchor" href="#2023-goals" aria-label="Link to 2023 Goals"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;(Hobby) Fix &amp;amp; start playing my Tabla&lt;/li&gt;
&lt;li&gt;(Health) Play Tennis once a week&lt;/li&gt;
&lt;li&gt;(Learning) Read 6 books this year&lt;/li&gt;
&lt;li&gt;(Programming) Revive TrueTime from the dead (Kotlin-ify + new algorithm)&lt;/li&gt;
&lt;li&gt;(Programming) Learning Kotlin Coroutines by example&lt;/li&gt;
&lt;li&gt;(Programming) Henry 2.0 updates&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/2022/</guid><pubDate>Sun, 01 Jan 2023 07:00:00 GMT</pubDate></item><item><title>
A few Kotlin constructs</title><link>https://kau.sh/blog/important-kotlin-constructs/</link><description>
&lt;!-- Also add kotlin version number that introduced it --&gt;
&lt;p&gt;A few Kotlin constructs have been introduced into the language over time. I wrote this post as a personal/public service advisory to remind us of their significance.&lt;/p&gt;
&lt;figure class="full-bleed"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2022/kotlin-header.webp"
class="full-bleed"
alt="Kotlin header image"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Would love to credit img owner
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#1-fun-interface-sam"&gt;1. fun interface (SAM)&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#11-vs-function-types"&gt;1.1. (vs) function types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#2-type-alias"&gt;2. type alias&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#3-import-alias"&gt;3. import alias&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#4-value-class"&gt;4. value class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#5-data-object"&gt;5. data object&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/important-kotlin-constructs/#revisions"&gt;Revisions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="1-fun-interface-sam"&gt;
1. fun interface (SAM)
&lt;a class="heading-anchor" href="#1-fun-interface-sam" aria-label="Link to 1. fun interface (SAM)"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Many languages (like Java) did not initially treat functions as &lt;a href="https://en.wikipedia.org/wiki/First-class_citizen"&gt;first-class citizens&lt;/a&gt;. They would emulate this functionality by using an interface with exactly one abstract method (a.k.a Single Abstract Method or SAM) interfaces.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;interface&lt;/span&gt; OnClickListener {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// single abstract function
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onClick&lt;/span&gt;(view: View)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Button&lt;/span&gt;.setOnClickListenerXXX(listener: OnClickListener) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// listener then invoked at some point
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; listener.onClick(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When &lt;code&gt;fun interface&lt;/code&gt; is used like the above, we typically must pass an instance of an object that implements this SAM interface.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; clickListener = &lt;span style="color:#66d9ef"&gt;object&lt;/span&gt;: OnClick {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onClick&lt;/span&gt;(view: Button) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.makeText(context, &lt;span style="color:#e6db74"&gt;&amp;#34;Button 1 Clicked&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.LENGTH_SHORT).show()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// You can now &amp;#34;pass this function&amp;#34; around
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;myButton.setOnClickListenerXXX(clickListener)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In practice, modern JVMs allow a far less verbose version of the above via lambdas:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myButton.setOnClickListenerXXX { view &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.makeText(view.context, &lt;span style="color:#e6db74"&gt;&amp;#34;testing 1&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.LENGTH_SHORT).show()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;introduced in Kotlin 1.4&lt;/li&gt;
&lt;li&gt;official &lt;a href="https://kotlinlang.org/docs/fun-interfaces.html"&gt;docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;purpose is to achieve &lt;a href="https://en.wikipedia.org/wiki/First-class_function"&gt;first-class functions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="11-vs-function-types"&gt;
1.1. (vs) function types
&lt;a class="heading-anchor" href="#11-vs-function-types" aria-label="Link to 1.1. (vs) function types"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Kotlin treats functions as first-class citizens and bakes the concept of &amp;ldquo;function types&amp;rdquo; right into the language.&lt;/p&gt;
&lt;p&gt;Function types and interfaces &lt;a href="https://www.reddit.com/r/Kotlin/comments/s35x3t/comment/htlpmv9/"&gt;are not the same thing&lt;/a&gt; but serve a similar purpose (treat functions as first-class citizens). The book Effective Kotlin does good justice in pointing out the &lt;a href="https://kt.academy/article/ek-function-types"&gt;nuance between these two concepts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At a time when lambdas were not properly supported with most modern JVMs, function types were the elegant alternative. This makes it confusing today (the existence of two similar concepts) because they can appear very similar in usage. Let&amp;rsquo;s compare the two:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// -------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// definition via fun interface
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// -------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;interface&lt;/span&gt; OnClickListener {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onClick&lt;/span&gt;(view: View)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Button&lt;/span&gt;.setOnClickListenerXXX(listener: OnClickListener) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; listener.onClick(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// -------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// definition via fun type
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// -------------------------
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Button&lt;/span&gt;.setOnClickListenerYYY(listener: (View) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; Unit) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; listener.invoke(&lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The snippet &lt;code&gt;(View): Unit&lt;/code&gt; is Kotlin allowing you to natively define a function as a type (function type? 🤯).&lt;/p&gt;
&lt;p&gt;In practice, you&amp;rsquo;ll find no difference in how the above two snippets are used&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myButton.setOnClickListenerXXX { view &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do something with the view...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.makeText(view.context, &lt;span style="color:#e6db74"&gt;&amp;#34;testing 1&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.LENGTH_SHORT).show()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;myButton.setOnClickListenerYYY { view &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do something with the view...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.makeText(view.context, &lt;span style="color:#e6db74"&gt;&amp;#34;testing 2&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;Toast&lt;/span&gt;.LENGTH_SHORT).show()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;!-- [^1]: which you probably see all the time, but never realized was a "function type" --&gt;
&lt;p&gt;My 2 cents: &lt;strong&gt;just use function types when possible.&lt;/strong&gt;&lt;/p&gt;
&lt;h1 id="2-type-alias"&gt;
2. type alias
&lt;a class="heading-anchor" href="#2-type-alias" aria-label="Link to 2. type alias"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://kotlinlang.org/docs/type-aliases.html"&gt;Type aliases&lt;/a&gt; provide alternative names for existing types. It&amp;rsquo;s conceptually straightforward and particularly useful when used with longer function type definitions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; MyHandler = (Int, String) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; Unit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// now just use MyHandler as your type definition
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// instead of tediously typing (Int, String) -&amp;gt; Unit every time
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; myHandler: MyHandler = { intValue, stringValue &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;introduced in Kotlin 1.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="3-import-alias"&gt;
3. import alias
&lt;a class="heading-anchor" href="#3-import-alias" aria-label="Link to 3. import alias"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The interesting thing with &lt;code&gt;type alias&lt;/code&gt; is that you aren&amp;rsquo;t limited to using it just for function types. You can use it to &amp;ldquo;redefine&amp;rdquo; &lt;em&gt;any&lt;/em&gt; type (or even shorten awkward long class names):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; DropdownView = Spinner &lt;span style="color:#75715e"&gt;// 🙄
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; AVD = AnimatedVectorDrawable
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But this is not the way 🙅. Kotlin has a nicer way to do this called &amp;ldquo;import alias&amp;rdquo;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; android.widget.Spinner &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; DropdownView
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; android.graphics.drawable.AnimatedVectorDrawable &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; AVD
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;lateinit&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; myDropDown: DropdownView
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;lateinit&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; myAVD: AVD
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another important use of &lt;code&gt;import alias&lt;/code&gt; is when you&amp;rsquo;re trying to disambiguate between multiple types with the same name. Say you have a type defined with a generic name but you also have a protobuf library autogenerating a different class with the exact same name. It&amp;rsquo;s possible to use both types within the same class by disambiguating them using &lt;code&gt;import alias&lt;/code&gt; &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; java.sql.Date &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; SqlDate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;import&lt;/span&gt; java.util.Date &lt;span style="color:#66d9ef"&gt;as&lt;/span&gt; JavaDate
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; date1: SqlDate = SqlDate(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; date2: JavaDate = JavaDate()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="4-value-class"&gt;
4. value class
&lt;a class="heading-anchor" href="#4-value-class" aria-label="Link to 4. value class"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://quickbirdstudios.com/blog/kotlin-value-classes/"&gt;This article&lt;/a&gt; explains why we need value classes (and why we should stop using &lt;code&gt;type alias&lt;/code&gt; or &lt;code&gt;data class&lt;/code&gt; in these situations).&lt;/p&gt;
&lt;p&gt;This is the gist:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth1&lt;/span&gt;(userId: Int, pin: Int): Boolean
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; u: Int = &lt;span style="color:#ae81ff"&gt;909&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; p: Int = &lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth1(u, p)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth1(p, u) &lt;span style="color:#75715e"&gt;// ⚠️ no compilation error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See the problem here? Both userid and pin are of type &lt;code&gt;Int&lt;/code&gt; but serve different intents. Wouldn&amp;rsquo;t it be nice if the compiler told us that we were passing the wrong fields? You can solve this with some good engineering and &lt;code&gt;data class&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;data&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserId&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; field: Int)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;data&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Pincode&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; field: Int)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth2&lt;/span&gt;(userId: UserId, pin: Pincode): Boolean
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; u = UserId(&lt;span style="color:#ae81ff"&gt;909&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; p = Pincode(&lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth2(u, p)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth2(p, u) &lt;span style="color:#75715e"&gt;// 🛑 error (✅)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This &amp;ldquo;works&amp;rdquo; but has one big problem: &lt;strong&gt;data class allocations are expensive&lt;/strong&gt;. With the &lt;code&gt;Int&lt;/code&gt; primitive usage we&amp;rsquo;re dealing with the stack whereas with data class objects we enter heap territory.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;We could use &lt;code&gt;type alias&lt;/code&gt; and avoid the allocation cost. But we run into the original issue viz. no compilation error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; UserId = Int
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;typealias&lt;/span&gt; Pincode = Int
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth3&lt;/span&gt;(userId: Int, pin: Int): Boolean
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; u = UserId(&lt;span style="color:#ae81ff"&gt;909&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; p = Pincode(&lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth3(u3, p3)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth3(p3, u3) &lt;span style="color:#75715e"&gt;// ⚠️ no compilation error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To solve this issue, Kotlin introduced the concept of &amp;ldquo;value classes&amp;rdquo; in version 1.5:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@JvmInline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;UserId&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; field: Int)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@JvmInline&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;value&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Pincode&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; field: Int)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;auth4&lt;/span&gt;(userId: UserId, pin: Pincode): Boolean
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; u = UserId(&lt;span style="color:#ae81ff"&gt;909&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; p = Pincode(&lt;span style="color:#ae81ff"&gt;1234&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth4(u, p)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;auth4(p, u) &lt;span style="color:#75715e"&gt;// 🛑 error (✅)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is similar to &lt;code&gt;data class&lt;/code&gt; but the difference is that underneath the hood, there&amp;rsquo;s no allocation costs 💪. So why not just use &lt;code&gt;value class&lt;/code&gt; always instead of &lt;code&gt;data class&lt;/code&gt;? Not so fast Scott, &lt;code&gt;value class&lt;/code&gt; can only take in a single primitive &lt;code&gt;field&lt;/code&gt; so there&amp;rsquo;s very legitimate reasons when you should be using a &lt;code&gt;data class&lt;/code&gt; instead.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;introduced in Kotlin 1.5&lt;/li&gt;
&lt;li&gt;previously called “inline class”&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@JvmInline&lt;/code&gt; is temporary and will be removed with Project Valhalla (&lt;a href="https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.md#project-valhalla"&gt;seriously&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="5-data-object"&gt;
5. data object
&lt;a class="heading-anchor" href="#5-data-object" aria-label="Link to 5. data object"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Have you ever tried to print the singleton &lt;code&gt;object&lt;/code&gt; during debugging?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;object&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BruceLee&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;println(&lt;span style="color:#e6db74"&gt;&amp;#34;Object Bruce Lee is &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${BruceLee}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Object Bruce Lee is BruceLee@37f8bb67
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;yuck!&lt;/p&gt;
&lt;p&gt;Experimental since 1.7.20 and stable since 1.8.0 Kotlin introduces the concept of &amp;ldquo;data object&amp;rdquo;. Think of it as tacking on a nicer &lt;code&gt;.toString()&lt;/code&gt; to your Singleton.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kt" data-lang="kt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;data&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;object&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;BruceLee&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;println(&lt;span style="color:#e6db74"&gt;&amp;#34;Object Bruce Lee is &lt;/span&gt;&lt;span style="color:#e6db74"&gt;${BruceLee}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Object Bruce Lee is BruceLee
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the gory details, you can read the &lt;a href="https://github.com/Kotlin/KEEP/pull/316/files"&gt;KEEP&lt;/a&gt; or &lt;a href="https://github.com/Kotlin/KEEP/issues/317"&gt;proposal discussion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My thanks to &lt;a href="https://www.donnfelker.com/"&gt;Donn&lt;/a&gt; for reviewing this post.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id="revisions"&gt;
Revisions
&lt;a class="heading-anchor" href="#revisions" aria-label="Link to Revisions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;/u/Boza_s6 &lt;a href="https://www.reddit.com/r/androiddev/comments/z6bxti/comment/iy2nk1t/"&gt;pointed out&lt;/a&gt; an error in my implication that &lt;code&gt;String&lt;/code&gt; would be allocated in the stack. Strings are &lt;a href="https://stackoverflow.com/a/2099311"&gt;not primitives&lt;/a&gt; so I switched the example to &lt;code&gt;Int&lt;/code&gt; for accuracy.&lt;/li&gt;
&lt;li&gt;/u/smieszne &lt;a href="https://www.reddit.com/r/Kotlin/comments/z69lnr/comment/iy2nqf9/"&gt;pointed out&lt;/a&gt; that my original example using ImprovedBruceLee as the data object would not output &amp;ldquo;BruceLee&amp;rdquo; but rather &amp;ldquo;ImprovedBruceLee&amp;rdquo;. As clever as my original example was 🙄, I resorted to just using the class name BruceLee for clarity.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;without having to type the full package name over and over&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;To learn more about stack vs heap memory allocation and why stack allocation is less expensive read &lt;a href="https://www.baeldung.com/java-stack-heap"&gt;https://www.baeldung.com/java-stack-heap&lt;/a&gt;&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/important-kotlin-constructs/</guid><pubDate>Sun, 27 Nov 2022 12:12:29 GMT</pubDate></item><item><title>
What does $0=$2 in awk do? learn awk</title><link>https://kau.sh/blog/awk-1-oneliner-dollar-explanation/</link><description>
&lt;p&gt;The trick to understanding awk in all its terse glory is to understand its defaults. I made a &lt;a href="https://kau.sh/blog/screencast-1-build-awk-jekyll-permalink-migration/"&gt;screencast&lt;/a&gt; explaining how &lt;a href="https://www.gnu.org/software/gawk/manual/gawk.html"&gt;awk&lt;/a&gt; works by deconstructing a script I&amp;rsquo;d previously written for this blog .&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; In this post we&amp;rsquo;ll look at deconstructing awk&amp;rsquo;s defaults so we can understand all those one-liner scripts stack overflow solutions throw your way.&lt;/p&gt;
&lt;h1 id="the-example"&gt;
The example
&lt;a class="heading-anchor" href="#the-example" aria-label="Link to The example"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I have a file that contains the version info for my apps and I&amp;rsquo;d like to extract the first version number in there:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-gradle" data-lang="gradle"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// appVersion.gradle
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; baseCode &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; appVersion &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: &lt;span style="color:#e6db74"&gt;&amp;#34;21.091.420&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code: baseCode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: &lt;span style="color:#e6db74"&gt;&amp;#34;20.090.300&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code: baseCode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// I want to pluck 21.091.420 from this file
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="the-first-solution-meh"&gt;
The first solution (meh)
&lt;a class="heading-anchor" href="#the-first-solution-meh" aria-label="Link to The first solution (meh)"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Some quick googling revealed this &lt;a href="https://unix.stackexchange.com/a/148289"&gt;stack overflow solution&lt;/a&gt; which gets us close:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 21.091.420&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 20.090.300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I only require the first number though so a quick way&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to do this would just be:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;head&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;n&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 21.091.420&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h1 id="the-problem-with-solution-1"&gt;
The problem with solution 1
&lt;a class="heading-anchor" href="#the-problem-with-solution-1" aria-label="Link to The problem with solution 1"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;awk is powerful and to reach out to &lt;code&gt;head&lt;/code&gt; for that last teeny tiny mile seemed sacrilegious. I want this solution to be pure awk.&lt;/li&gt;
&lt;li&gt;What the heck does that incantation &lt;code&gt;gawk '$0=$2'&lt;/code&gt; do? &lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="the-basics"&gt;
The basics
&lt;a class="heading-anchor" href="#the-basics" aria-label="Link to The basics"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s try to take that script apart piece by piece:&lt;/p&gt;
&lt;h2 id="default-input-field-delimiter"&gt;
default input field delimiter
&lt;a class="heading-anchor" href="#default-input-field-delimiter" aria-label="Link to default input field delimiter"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# input field delimiter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you don&amp;rsquo;t specify the input field delimiter, awk sensibly defaults to the space character. Let&amp;rsquo;s try some examples:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34; &amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# kind&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;,&amp;#34;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print $2}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- no output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how the line is split into numbered &amp;ldquo;segments&amp;rdquo; where &lt;code&gt;$1&lt;/code&gt;, &lt;code&gt;$2&lt;/code&gt;, &lt;code&gt;$3&lt;/code&gt; hold the first three words in our example respectively. &lt;strong&gt;&lt;code&gt;$0&lt;/code&gt; represent the entire line&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="default-syntax"&gt;
default syntax
&lt;a class="heading-anchor" href="#default-syntax" aria-label="Link to default syntax"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you watched my &lt;a href="https://kau.sh/blog/screencast-1-build-awk-jekyll-permalink-migration/"&gt;screencast&lt;/a&gt; you&amp;rsquo;ll remember that awk&amp;rsquo;s general syntax is as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;awk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; BEGIN { a1; a2; a3; } # optional # runs once before input
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;lt;pattern1&amp;gt; { a1; a2; a3; } # action block (mandatory) # run if pattern1 matches
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; &amp;lt;pattern2&amp;gt; { a1; a2; a3; } # action block (optional) # run if pattern2 matches
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt; END { a4; a6; } # optional # run once after input
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;filename&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Most awk one-liners typically don&amp;rsquo;t use the begin &amp;amp; end blocks.&lt;/p&gt;
&lt;p&gt;So looking back at my simple one-liner:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39; { print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↓&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# action block ✅&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;🛑 ✋ but wait, what&amp;rsquo;s going on with the original one-liner 👇?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 🤔&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# is this a &amp;lt;pattern&amp;gt;?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# is this an action block?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For this, we need to understand how the awk &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; recognition works:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# general syntax&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;lt;pattern&amp;gt; { a1; a2; a3; }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;0 { print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# forcing result of &amp;lt;pattern&amp;gt; match as 0 (false)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# no output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1 { print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;2 { print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;3 { print $2 }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# forcing result of &amp;lt;pattern&amp;gt; match as 3 / non-0 (true)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output for all the above --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# kind&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So the way that &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; condition matching works is if awk sees 0 &lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; the pattern match condition is &amp;ldquo;false&amp;rdquo; and awk ignores the action block. Anything &amp;gt; 0 and awk treats the condition as &amp;ldquo;true&amp;rdquo; and executes the action block. Ok back to the one-liner:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# is this a valid pattern?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ✅ we&amp;#39;re getting some non-0 value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# cause things are being printed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# is this an action block? 🤔&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So &lt;code&gt;$0=$2&lt;/code&gt; is coming back with a result of &amp;gt; 0 and some invisible default is being executed. Progress&amp;hellip; but still many questions.&lt;/p&gt;
&lt;h2 id="default-action"&gt;
default action
&lt;a class="heading-anchor" href="#default-action" aria-label="Link to default action"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s try some commands. Notice the output for each of them:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;0 {print $0}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;0 {print}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;0&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# no output&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1 {print $0}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1 {print}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Hello kind world&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So when the &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; match is false (0) nothing is printed and when it is 1 then the default is to just print the entire line (&lt;code&gt;$0&lt;/code&gt;). In fact you don&amp;rsquo;t have to specify anything and awk assumes you want to &lt;code&gt;print $0&lt;/code&gt; by default.&lt;/p&gt;
&lt;h2 id="variable-reassignment"&gt;
variable reassignment
&lt;a class="heading-anchor" href="#variable-reassignment" aria-label="Link to variable reassignment"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;You know how we glorify immutability with most programming? &lt;code&gt;awk&lt;/code&gt; ain&amp;rsquo;t having any of that.&lt;/p&gt;
&lt;p&gt;You can mutate the heck out of anything. You can mutate the current line before you even run an action on it. Check this piece of code out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print $0&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$1&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$2&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$3}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Hello kind world &amp;lt;-&amp;gt; Hello &amp;lt;-&amp;gt; kind &amp;lt;-&amp;gt; world&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑ ↑ ↑ ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# $0 $1 $2 $3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;echo&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;Hello kind world&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;|&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;1 {$0=&amp;#34;hijack&amp;#34;; print $0&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$1&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$2&amp;#34; &amp;lt;-&amp;gt; &amp;#34;$3}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# hijack &amp;lt;-&amp;gt; hijack &amp;lt;-&amp;gt; &amp;lt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ↑ ↑ ↑ ↑&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# $0 $1 🙅 🙅&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Even before the action block is executed you can reassign the entire line&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="number-of-fields"&gt;
number of fields
&lt;a class="heading-anchor" href="#number-of-fields" aria-label="Link to number of fields"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s the last piece that should help bring this all together. Given this file again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-gradle" data-lang="gradle"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// appVersion.gradle
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; baseCode &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;def&lt;/span&gt; appVersion &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: &lt;span style="color:#e6db74"&gt;&amp;#34;21.091.420&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code: baseCode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; product&lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; name: &lt;span style="color:#e6db74"&gt;&amp;#34;20.090.300&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; code: baseCode
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print NF &amp;#34;: &amp;#34;$0}&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 4: def baseCode = 30001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 4: def appVersion = [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3: product-1 : [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2: name: &amp;#34;21.091.420&amp;#34;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2: code: baseCode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: ],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2: product-2: [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2: name: &amp;#34;20.090.300&amp;#34;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 2: code: baseCode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: ],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NF&lt;/code&gt; in awk stands for number of fields.&lt;/li&gt;
&lt;li&gt;awk takes the &lt;a href="https://kau.sh/blog/awk-1-oneliner-dollar-explanation/#default-input-field-delimiter"&gt;input field separator&lt;/a&gt; and stores the &amp;ldquo;number of fields&amp;rdquo; discovered in that variable.&lt;/li&gt;
&lt;li&gt;Notice above that when there&amp;rsquo;s an empty line NF is 0 since there&amp;rsquo;s no separator (or content) in that line.&lt;/li&gt;
&lt;li&gt;On the second line there&amp;rsquo;s 4 tokens (each separated by space)&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="the-first-solution-again"&gt;
The first solution (again)
&lt;a class="heading-anchor" href="#the-first-solution-again" aria-label="Link to The first solution (again)"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;All right, let&amp;rsquo;s do this one last time.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 21.091.420&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 20.090.300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What&amp;rsquo;s happening here is a beautiful symphony of awk defaults stacking on top of each other.&lt;/p&gt;
&lt;p&gt;We first reassign the variable holding the entire line ($0) to $2. Remember that $2 holds the second word/token after splitting the original content in $0 with the input field separator &lt;code&gt;&amp;quot;&lt;/code&gt;. This should help point out the resulting fields with the new field separator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; gawk -F&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;{print NF &amp;#34;: &amp;#34;$0}&amp;#39;&lt;/span&gt; appVersion.gradle
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: def baseCode = 30001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: def appVersion = [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: product-1 : [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3: name: &amp;#34;21.091.420&amp;#34;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: code: baseCode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: ],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 0:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: product-2: [&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3: name: &amp;#34;20.090.300&amp;#34;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: code: baseCode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 1: ],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;First line of this file is empty, so $2 is 0 and $0 is assigned to 0.&lt;/li&gt;
&lt;li&gt;Second line of this file has only one segment (since there&amp;rsquo;s no &lt;code&gt;&amp;quot;&lt;/code&gt;) and is stored in $1. $2 is again 0 and $0 is assigned to 0.&lt;/li&gt;
&lt;li&gt;Sixth line has 3 tokens, $2 is &lt;code&gt;21.091.420&lt;/code&gt; and is assigned to $0.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The original command should make sense now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;$0=$2&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 21.091.420&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 20.090.300&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;&amp;lt;pattern&amp;gt;&lt;/code&gt; match condition provided to awk is the output value of &lt;code&gt;$0=$2&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;From the previous command output we noticed that for the first, second lines up until the sixth line, was all 0.&lt;/li&gt;
&lt;li&gt;when awk sees 0 it ignores the action and there&amp;rsquo;s no print&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The sixth line is the first time we encounter a non-0 value, so the action block is executed.
&lt;ul&gt;
&lt;li&gt;what is the default action? print $0&lt;/li&gt;
&lt;li&gt;what is $0?
&lt;ul&gt;
&lt;li&gt;the second token or segment $2 (after field separator &lt;code&gt;&amp;quot;&lt;/code&gt;) which is &lt;code&gt;21.091.420&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;💥&lt;/p&gt;
&lt;p&gt;This is really such a gorgeous piece of code. Clever and poetic.&lt;/p&gt;
&lt;h1 id="the-final-solution"&gt;
The final solution
&lt;a class="heading-anchor" href="#the-final-solution" aria-label="Link to The final solution"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;If you&amp;rsquo;re curious how I came up with my own solution, I made it a little less clever, more verbose and hopefully now simpler to understand:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-awk" data-lang="awk"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;gawk&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;F&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;NF==3 {print $2; exit}&amp;#39;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;appVersion&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -- output --&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 21.091.420&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Go forth and awk.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;If you want to understand the fundamentals of awk first, I recommend that screencast.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;a.k.a &lt;em&gt;practical&lt;/em&gt; and the one I&amp;rsquo;d recommend to others on a time crunch.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;down the 🐰 hole we go&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;In awk land 0 = false, anything &amp;gt; 0 is true&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/awk-1-oneliner-dollar-explanation/</guid><pubDate>Sat, 24 Sep 2022 07:22:16 GMT</pubDate></item><item><title>
Operating efficiently at scale</title><link>https://kau.sh/blog/operating-efficiently-scale-coinbase/</link><description>
&lt;blockquote&gt;
&lt;p&gt;Great companies maintain their insurgent mindset, for fear of becoming complacent and irrelevant over time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Push decision making down to single-threaded DRIs&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Single-threaded is tech jargon that simply means solely focused on a single area. The single threaded DRI is the most senior person whose only job is to run a given product or initiative, this will typically be a product management or engineering leader.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Leverage shared services to minimize duplication&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Done wrong, shared services can slow down and frustrate product teams. But when they work well, they can create amazing synergies between products, and deeper product integration.
Product teams should not be required to use a half baked shared service. But once a shared service is mature, all products may be required to use it.&lt;/p&gt;
&lt;p&gt;&amp;hellip; When it becomes clear that we are duplicating effort or creating an inconsistent user experience across our products, services need to graduate into clearly decoupled services that any product can leverage.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Organize teams into small pods&lt;/strong&gt;
Small teams are more efficient.
&amp;hellip; We’re beginning to deploy a new concept that we call “pods” to create more structure around the appropriate size of a team.&lt;/p&gt;
&lt;p&gt;Pods also need to have a focus, and a north star metric that ties into the overall company metrics.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Ship products not slide decks&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Maintain an insurgent mindset&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Ultimately, a lot of this comes down to retaining the founder mentality inside the company and acting like owners.&lt;/p&gt;
&lt;/blockquote&gt;</description><guid>https://kau.sh/blog/operating-efficiently-scale-coinbase/</guid><pubDate>Tue, 23 Aug 2022 06:23:32 GMT</pubDate></item><item><title>
What a Mac mini &amp; Tailscale enables</title><link>https://kau.sh/blog/mac-mini-tailscale-benefits-tips-vpn-vps/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/commons/4/4d/Tailscale-Logo-Black.svg"
alt="Tailscale logo"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I&amp;rsquo;d like to show you how I use Tailscale and a Mac mini in my office to achieve some nifty things.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://tailscale.com/"&gt;Tailscale&lt;/a&gt; is marketed as a zero-config VPN built on top of WireGuard that securely connects devices and manages firewall rules etc. While all of that might be true, think of it this way: Tailscale allows you to securely connect to a machine at home from anywhere on the internet. This includes your mobile phone, tablet, or laptop.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Tailscale allows you to connect to a machine at home from anywhere on the internet (securely) from your phone/tablet/laptop.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="basic-idea"&gt;
Basic idea
&lt;a class="heading-anchor" href="#basic-idea" aria-label="Link to Basic idea"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;For me, that “machine” is a souped-up Mac mini sitting on an office shelf at home that is never turned off. This Mac mini is the linchpin of my setup, really.&lt;/p&gt;
&lt;p&gt;I install&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; Tailscale on this Mac mini and all the other devices I wish to connect from. With the magic of Tailscale ✨, all these devices are now connected like they were on the same network! If that hasn&amp;rsquo;t sunk in:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I could literally connect to the &lt;a href="https://homebridge.io/"&gt;Homebridge&lt;/a&gt; server on my Mac mini in California from an Android phone while traveling in India.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let’s talk about some services you could run leveraging this setup.&lt;/p&gt;
&lt;h2 id="private-blog-server"&gt;
Private blog server
&lt;a class="heading-anchor" href="#private-blog-server" aria-label="Link to Private blog server"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I use the static website generator &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; (&amp;amp; &lt;a href="https://kau.sh/blog/henry-hugo-theme/"&gt;Henry&lt;/a&gt;) for &lt;a href="https://kau.sh/"&gt;this site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hugo has this nifty feature called &lt;a href="https://gohugo.io/getting-started/usage/#livereload"&gt;live reload&lt;/a&gt; when you run it on a local machine. If I change any part of my website,&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; it instantly shows me an up-to-date preview. I typically edit my &amp;ldquo;draft&amp;rdquo; blog posts like this, so I can see the changes right away. But you don&amp;rsquo;t run your real website like this.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Gosh, if only I had a private server that only I could connect to on the internet.&lt;/p&gt;
&lt;p&gt;With Tailscale on my Mac mini, I now do.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For the technically curious: I edit files directly on a folder on the mini using VSCode.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; I then run a hugo server&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; which watches for changes in this folder. Hugo picks it up and serves a live preview on a private port on the Mac mini. And because this is just a &amp;ldquo;local&amp;rdquo; address &amp;amp; port (courtesy Tailscale), I can connect to it from any of my devices.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m sipping coffee in hipster cafes editing my site, then clinging on to dear life in a Muni bus, reading that draft on a phone browser. Magical!&lt;/p&gt;
&lt;h2 id="freshrss-rss-feed-server"&gt;
FreshRSS (RSS feed) server
&lt;a class="heading-anchor" href="#freshrss-rss-feed-server" aria-label="Link to FreshRSS (RSS feed) server"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/RSS"&gt;RSS&lt;/a&gt; is old but remarkable technology. I use it as my throttled way of consuming the internet (blog posts, youtube videos, podcasts, etc.).&lt;/p&gt;
&lt;p&gt;I previously used &lt;a href="https://feedly.com/"&gt;Feedly&lt;/a&gt; but then switched to iCloud.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; But &lt;strong&gt;iCloud is dog slow&lt;/strong&gt; for this. So I spent a few hours one weekend installing &lt;a href="https://freshrss.org/"&gt;FreshRSS&lt;/a&gt; on my Mac mini. I now have an open source, super fast RSS service accessible (to me) from anywhere on the internet.&lt;/p&gt;
&lt;h2 id="homebridge"&gt;
Homebridge
&lt;a class="heading-anchor" href="#homebridge" aria-label="Link to Homebridge"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;After purchasing a home recently, I set out on a mission to smartify all the things. Lights, doorbells, smart locks, window shades, garages, security cameras, alarm systems, and even the washing machine!&lt;/p&gt;
&lt;p&gt;I use a &lt;a href="https://homebridge.io/"&gt;Homebridge&lt;/a&gt; server on the Mac mini to connect all these devices. One of the problems though is you can typically only access homebridge from within the same wifi network (which is usually a good thing). But occasionally, I&amp;rsquo;d love to turn off my light switches or lower the shades as I rush to the airport. Turn on Tailscale, and I can connect to the Homebridge server directly from my phone.&lt;/p&gt;
&lt;h2 id="plex-media-server"&gt;
Plex media server
&lt;a class="heading-anchor" href="#plex-media-server" aria-label="Link to Plex media server"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://www.plex.tv/"&gt;Plex&lt;/a&gt; is a media server that allows you to &amp;ldquo;stream&amp;rdquo; content from a local disk or hard drive to any network. This includes (massive) family photo albums, high-quality Blu-ray rips&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt; of &lt;a href="https://kau.sh/blog/movie-rating-system/"&gt;my favorite movies&lt;/a&gt;, audio music that isn&amp;rsquo;t on Spotify from artists I&amp;rsquo;ve worked with, etc.&lt;/p&gt;
&lt;p&gt;Plex, in theory, allows you to expose this media server to the internet directly (and this is how most people use it). For obvious reasons, I prefer not exposing this server to the internet, even with authentication. Instead, I now connect to this Plex server via Tailscale. I can stream it on my phone and then just Airplay/Google Cast it to a hotel TV.&lt;/p&gt;
&lt;p&gt;Tailscale is magical technology. It feels like the iPhone in a VPN world of flip-phones.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I&amp;rsquo;ll keep updating this post as I come up with more use cases.&lt;/em&gt;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;The installation really is &amp;ldquo;zero-config&amp;rdquo;. I still don&amp;rsquo;t understand how easy it is to install it. I&amp;rsquo;m not even going to talk about it in this post cause you can just look at the official instructions.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;like css styles or markdown post content&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;this would require a &amp;ldquo;server&amp;rdquo; that&amp;rsquo;s costly + requires more resources. The whole point of Hugo is to &amp;ldquo;statically&amp;rdquo; generate pages that are much lighter.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;via ssh&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;that runs inside a docker container&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;free &amp;amp; no ads&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;that obviously fell out of a truck&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/mac-mini-tailscale-benefits-tips-vpn-vps/</guid><pubDate>Sat, 20 Aug 2022 20:55:35 GMT</pubDate></item><item><title>
My new programming font - Recursive</title><link>https://kau.sh/blog/recursive/</link><description>
&lt;p&gt;I&amp;rsquo;m always looking for legible, high-quality monospaced fonts for programming. I
started with &lt;a href="https://kau.sh/blog/ibm-plex-mono/"&gt;IBM Plex Mono&lt;/a&gt;, dabbled with a few others
like &lt;a href="https://input.djr.com/"&gt;Input Mono&lt;/a&gt;, even built a few from source like
&lt;a href="https://kau.sh/blog/build-iosevka-font-mac-os/"&gt;Iosevka&lt;/a&gt;, fell in and out of love with
&lt;a href="https://developer.apple.com/fonts/"&gt;various&lt;/a&gt;
&lt;a href="https://www.monolisa.dev/"&gt;others&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In early 2020, the foundry ArrowType released
&lt;a href="https://recursive.design"&gt;&lt;strong&gt;Recursive&lt;/strong&gt;&lt;/a&gt; which has now become my daily driver.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./recursive.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;What really got me hooked with Recursive — besides it being a gorgeous typeface
— was the fact that it was &lt;a href="https://github.com/arrowtype/recursive"&gt;open source&lt;/a&gt;
and you could
&lt;a href="https://github.com/arrowtype/recursive"&gt;build it from source yourself&lt;/a&gt; (like
&lt;a href="../build-iosevka-font-mac-os/"&gt;Iosevka&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Here are the &lt;a href="https://github.com/kaushikgopal/recursive-code-config"&gt;customizations&lt;/a&gt; I&amp;rsquo;ve made for myself:&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="./recursive-kg.webp"
alt="Recursive custom settings"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id="download-recursive"&gt;
Download Recursive
&lt;a class="heading-anchor" href="#download-recursive" aria-label="Link to Download Recursive"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I could keep extolling the virtues of this font but if you&amp;rsquo;re curious to just
download and try it, here&amp;rsquo;s a couple of ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the &lt;a href="https://www.recursive.design/"&gt;main website&lt;/a&gt; and hit the big blue
&amp;ldquo;Get&amp;rdquo; button.&lt;/li&gt;
&lt;li&gt;If you prefer my variant and are on MacOS:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install --cask kaushikgopal/tools/font-recursive-kg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or just
&lt;a href="https://github.com/kaushikgopal/recursive-code-config/releases/tag/v5.1.085"&gt;download it here&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/recursive/</guid><pubDate>Fri, 19 Aug 2022 07:00:00 GMT</pubDate></item><item><title>
Yearn for the vast and endless sea</title><link>https://kau.sh/blog/build-a-ship/</link><description>
&lt;blockquote&gt;
&lt;p&gt;If you want to build a ship, don’t drum up the men to gather wood, divide the work, and give orders. Instead, teach them to yearn for the vast and endless sea.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Carl_Sagan"&gt;Antoine de Saint-Exupéry&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Lessons for Engineering Managers in Software.&lt;/p&gt;</description><guid>https://kau.sh/blog/build-a-ship/</guid><pubDate>Sun, 14 Aug 2022 07:00:00 GMT</pubDate></item><item><title>
High quality vector icons from Yoolk Ninja</title><link>https://kau.sh/blog/free-vector-app-icons-yoolk-ninja/</link><description>
&lt;figure class="rounded"&gt;
&lt;div align="center"&gt;
&lt;img src="yoolk-icons.webp"
class="rounded"
alt="Sample icons from Yoolk"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Icons by Yoolk
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Stumbled upon Yoolk&amp;rsquo;s website the other day. What a treasure trove of high quality icons. There&amp;rsquo;s even a &lt;a href="https://github.com/lucky-yoolk/icons-flat-osx"&gt;github repo&lt;/a&gt; for some of these.&lt;/p&gt;</description><guid>https://kau.sh/blog/free-vector-app-icons-yoolk-ninja/</guid><pubDate>Fri, 29 Jul 2022 02:56:42 GMT</pubDate></item><item><title>
Keyboard Maestro as a Text Expander replacement</title><link>https://kau.sh/blog/keyboard-maestro-replace-text-expander/</link><description>
&lt;figure class="borderless"&gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/keyboard-maestro.webp"
class="borderless"
alt="keyboard maestro icon"
width="300"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://yoolk.ninja/"&gt;
icon by Yoolk
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;If you&amp;rsquo;ve &lt;a href="https://daringfireball.net/linked/2022/07/25/textexpander-raises-41-million"&gt;recently&lt;/a&gt; needed a text expander utility, might I suggest the trusty &lt;a href="http://www.stairways.com/action/kmdiscount?REF6J"&gt;Keyboard Maestro&lt;/a&gt;.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I switched from TextExpander → Alfred quite some time back. Somewhat recently, I again switched from Alfred → KM for a couple of reasons:&lt;/p&gt;
&lt;h2 id="1-one-tool-to-rule-them-all"&gt;
1. One tool to rule them all
&lt;a class="heading-anchor" href="#1-one-tool-to-rule-them-all" aria-label="Link to 1. One tool to rule them all"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Between work &amp;amp; home, I use multiple computers these days. Having to sync + maintain each of these different utilities across multiple computers is becoming tedious.&lt;/p&gt;
&lt;p&gt;Keyboard Maestro is a mainstay in my productivity toolbelt and isn&amp;rsquo;t going anywhere. Might as well double down on it for the things it does well.&lt;/p&gt;
&lt;h2 id="2-fixing-app-quirks"&gt;
2. Fixing app quirks
&lt;a class="heading-anchor" href="#2-fixing-app-quirks" aria-label="Link to 2. Fixing app quirks"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;One of the big problems I found with utilities like Alfred is that the text expansion would activate too quickly. Consequently, the expansion wouldn&amp;rsquo;t work in certain apps (like VSCode).&lt;/p&gt;
&lt;p&gt;With Keyboard Maestro though, you can add a micro delay,&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; ensuring the snippets work flawlessly everywhere.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="km-textexpand-emoji.webp"
alt="keyboard maestro macro for an emoji text expansion"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;You can also limit the scope to just a few apps if you&amp;rsquo;re worried this delay will slow you down in non-problematic apps.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="3-mix-text-expansion-with-other-macros"&gt;
3. Mix text expansion with other macros
&lt;a class="heading-anchor" href="#3-mix-text-expansion-with-other-macros" aria-label="Link to 3. Mix text expansion with other macros"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Building on that, with Keyboard Maestro, you&amp;rsquo;re not limited to just text replacement.&lt;/p&gt;
&lt;p&gt;You can execute full-fledged macros with your text expansion. Here&amp;rsquo;s an example of how I add checklists in Google Docs.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/km-gdoc-checklist.webp"
alt="keyboard maestro macro for inserting checklist in google docs"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href="http://www.stairways.com/action/kmdiscount?REF6J"&gt;Keyboard Maestro&lt;/a&gt; remains one of the most powerful utilities for the Mac.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Referral link&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I use 0.5s&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;It puzzles me that they haven&amp;rsquo;t assigned a default keyboard shortcut for this already.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/keyboard-maestro-replace-text-expander/</guid><pubDate>Wed, 27 Jul 2022 07:00:00 GMT</pubDate></item><item><title>
Magic enter command for (fish/zsh) shell</title><link>https://kau.sh/blog/magic-enter-shell/</link><description>
&lt;p&gt;Two of the most common commands I run when I &lt;code&gt;cd&lt;/code&gt; into a directory are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git status&lt;/code&gt; (which i do through &lt;a href="https://kau.sh/blog/git-number"&gt;git-number&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ls -lh&lt;/code&gt; (aliased automatically as &lt;code&gt;ll&lt;/code&gt; through fish)&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;If this directory is not a git repo, then I usually am thinking of command 2.&lt;/li&gt;
&lt;li&gt;If however it is a git repository then
&lt;ul&gt;
&lt;li&gt;I&amp;rsquo;d like to know if any files have been changed in this directory and&lt;/li&gt;
&lt;li&gt;which ones specifically (which is command 1).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wouldn&amp;rsquo;t it be nice if I could just hit the ↩ (enter key) and have this automatically happen?&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="demo-magic-enter.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;h2 id="solution-for-fish-users"&gt;
Solution for fish users
&lt;a class="heading-anchor" href="#solution-for-fish-users" aria-label="Link to Solution for fish users"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re using &lt;a href="https://fishshell.com/"&gt;fish&lt;/a&gt;, I got you covered:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# filename: fish/conf.d/magic-enter.fish&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; magic-enter-cmd --description &lt;span style="color:#e6db74"&gt;&amp;#34;Issue git status or ls on hitting enter in a dir&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set -l cmd ll
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set -l is_git_repository &lt;span style="color:#f92672"&gt;(&lt;/span&gt;fish -c &lt;span style="color:#e6db74"&gt;&amp;#34;git rev-parse --is-inside-work-tree &amp;gt;&amp;amp;2&amp;#34;&lt;/span&gt; 2&amp;gt;| grep true&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#75715e"&gt;# Special variable indicating git.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set -l in_root_folder &lt;span style="color:#f92672"&gt;(&lt;/span&gt;fish -c &lt;span style="color:#e6db74"&gt;&amp;#34;git rev-parse --show-toplevel &amp;gt;&amp;amp;2&amp;#34;&lt;/span&gt; 2&amp;gt;| grep &lt;span style="color:#f92672"&gt;(&lt;/span&gt;pwd&lt;span style="color:#f92672"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set -l repo_has_changes &lt;span style="color:#f92672"&gt;(&lt;/span&gt;git status -s --ignore-submodules&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dirty&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$is_git_repository&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$in_root_folder&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$repo_has_changes&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set cmd git-number
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; echo $cmd
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; magic-enter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; set -l cmd &lt;span style="color:#f92672"&gt;(&lt;/span&gt;commandline&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; test -z &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$cmd&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commandline -r &lt;span style="color:#f92672"&gt;(&lt;/span&gt;magic-enter-cmd&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commandline -f suppress-autosuggestion
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; commandline -f execute
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bind &lt;span style="color:#ae81ff"&gt;\r&lt;/span&gt; magic-enter
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="solution-for-zsh-users"&gt;
Solution for zsh users
&lt;a class="heading-anchor" href="#solution-for-zsh-users" aria-label="Link to Solution for zsh users"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you&amp;rsquo;re using &lt;a href="https://zsh.sourceforge.io"&gt;zsh&lt;/a&gt;, I adapted the &lt;a href="https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins/magic-enter"&gt;native solution&lt;/a&gt; like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# filename: magic-enter.plugin.zsh&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Default commands&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;: &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;MAGIC_ENTER_GIT_COMMAND:=&lt;span style="color:#e6db74"&gt;&amp;#34;git-number&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#75715e"&gt;# run when in a git repository&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;: &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;MAGIC_ENTER_OTHER_COMMAND:=&lt;span style="color:#e6db74"&gt;&amp;#34;ll&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#75715e"&gt;# run anywhere else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;magic-enter&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Only run MAGIC_ENTER commands when in PS1 and command line is empty&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#User_002dDefined-Widgets&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[[&lt;/span&gt; -n &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$BUFFER&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$CONTEXT&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; !&lt;span style="color:#f92672"&gt;=&lt;/span&gt; start &lt;span style="color:#f92672"&gt;]]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; is_git_repository&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git rev-parse --is-inside-work-tree&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; in_repo_root_folder&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git rev-parse --show-toplevel&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; repo_has_changes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;git status -s --ignore-submodules&lt;span style="color:#f92672"&gt;=&lt;/span&gt;dirty&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$is_git_repository&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; true &lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$in_repo_root_folder&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; $PWD &lt;span style="color:#f92672"&gt;]&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#f92672"&gt;[&lt;/span&gt; ! -z &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$repo_has_changes&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;]&lt;/span&gt;; &lt;span style="color:#66d9ef"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; BUFFER&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$MAGIC_ENTER_GIT_COMMAND&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; BUFFER&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$MAGIC_ENTER_OTHER_COMMAND&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Wrapper for the accept-line zle widget (run when pressing Enter)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# If the wrapper already exists don&amp;#39;t redefine it&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;((&lt;/span&gt; ! &lt;span style="color:#e6db74"&gt;${&lt;/span&gt;+functions[_magic-enter_accept-line]&lt;span style="color:#e6db74"&gt;}&lt;/span&gt; &lt;span style="color:#f92672"&gt;))&lt;/span&gt; &lt;span style="color:#f92672"&gt;||&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$widgets&lt;span style="color:#e6db74"&gt;[accept-line]&amp;#34;&lt;/span&gt; in
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Override the current accept-line widget, calling the old one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user:*&lt;span style="color:#f92672"&gt;)&lt;/span&gt; zle -N _magic-enter_orig_accept-line &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;widgets[accept-line]#user:&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; _magic-enter_accept-line&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; magic-enter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zle _magic-enter_orig_accept-line -- &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$@&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# If no user widget defined, call the original accept-line widget&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; builtin&lt;span style="color:#f92672"&gt;)&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; _magic-enter_accept-line&lt;span style="color:#f92672"&gt;()&lt;/span&gt; &lt;span style="color:#f92672"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; magic-enter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; zle .accept-line
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;}&lt;/span&gt; ;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;esac&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zle -N accept-line _magic-enter_accept-line
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remember to source this in your &lt;code&gt;.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;source magic-enter.plugin.zsh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="revisions"&gt;
Revisions
&lt;a class="heading-anchor" href="#revisions" aria-label="Link to Revisions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Make a change to only check git status in a root folder.&lt;/li&gt;
&lt;li&gt;Reddit &lt;code&gt;/u/colemaker360&lt;/code&gt; &lt;a href="https://www.reddit.com/r/commandline/comments/w75msb/comment/ihiub1s/"&gt;reminded me&lt;/a&gt; that the &lt;code&gt;^/dev/null&lt;/code&gt; redirect strategy is now &lt;a href="https://github.com/fish-shell/fish-shell/issues/4394#issuecomment-366098782"&gt;deprecated&lt;/a&gt; with fish 3.&lt;/li&gt;
&lt;li&gt;Redditor &lt;code&gt;/u/jblondreddit&lt;/code&gt; &lt;a href="https://www.reddit.com/r/fishshell/comments/w75nwt/comment/ihjpicp/"&gt;pointed out&lt;/a&gt; that porcelain can be a slow.&lt;/li&gt;
&lt;li&gt;Add solution for zsh users&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/magic-enter-shell/</guid><pubDate>Sat, 23 Jul 2022 19:50:31 GMT</pubDate></item><item><title>
Manipulating images &amp; PDFs using CLI commands</title><link>https://kau.sh/blog/terminal-cli-image-pdf-conversion/</link><description>
&lt;p&gt;Whenever I need to convert, merge, or combine images or PDF files, I pull out my Terminal and attempt doing it first with CLI (command line interface) commands. Over time I&amp;rsquo;ve built an arsenal of CLI commands that 9/10 times does the trick faster than any other program.&lt;/p&gt;
&lt;h4 id="prerequisites"&gt;
Prerequisites
&lt;a class="heading-anchor" href="#prerequisites" aria-label="Link to Prerequisites"&gt;#&lt;/a&gt;
&lt;/h4&gt;
&lt;p&gt;Most of the image manipulation commands require &lt;a href="https://imagemagick.org/index.php"&gt;imagemagick&lt;/a&gt; and the PDF ones require &lt;a href="https://poppler.freedesktop.org/"&gt;poppler&lt;/a&gt;. Both of which are just a homebrew install away for macOS:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# image utilities&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install &lt;span style="color:#e6db74"&gt;&amp;#34;imagemagick&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# pdf utilities&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install &lt;span style="color:#e6db74"&gt;&amp;#34;poppler&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="merging"&gt;
Merging
&lt;a class="heading-anchor" href="#merging" aria-label="Link to Merging"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="merge-images-horizontallyvertically"&gt;
Merge images horizontally/vertically
&lt;a class="heading-anchor" href="#merge-images-horizontallyvertically" aria-label="Link to Merge images horizontally/vertically"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## horizontal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert +append image_1.png image_2.png merged
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## vertical&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -append image_1.png image_2.png merged // vertical
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# using montage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# slightly better as it maintains transparency within images&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## horizontal&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;montage 0_index.png 1_post.png -background none -tile 2x1 -geometry +0+0 PNG32:out.png
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;## vertical&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;montage 0_index.png 1_post.png -background none -tile 1x2 -geometry +0+0 PNG32:out.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="merge-images-and-pdfs"&gt;
Merge images and PDFs
&lt;a class="heading-anchor" href="#merge-images-and-pdfs" aria-label="Link to Merge images and PDFs"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert 1.png 2.pdf merged.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="merge-pdfs"&gt;
Merge PDFs
&lt;a class="heading-anchor" href="#merge-pdfs" aria-label="Link to Merge PDFs"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# pdfunite ships with poppler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pdfunite output-1.pdf output-2.pdf output-3.pdf merged-1-3.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pdfunite output-4.pdf output-5.pdf merged-4-5.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# using native tools that comes with the macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/System/Library/Automator/Combine PDF Pages.action/Contents/Resources/join.py&amp;#34;&lt;/span&gt; -o merged.pdf source1.pdf source2.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# using ghostscript [also works with encrypted PDFs]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gs -q -dNOPAUSE -dBATCH -sDEVICE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;pdfwrite -sOutputFile&lt;span style="color:#f92672"&gt;=&lt;/span&gt;merged.pdf source1.pdf source2.pdf source3.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="splitting"&gt;
Splitting
&lt;a class="heading-anchor" href="#splitting" aria-label="Link to Splitting"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="split-multi-page-pdf-into-single-page-pdfs"&gt;
Split multi-page PDF into single page PDFs
&lt;a class="heading-anchor" href="#split-multi-page-pdf-into-single-page-pdfs" aria-label="Link to Split multi-page PDF into single page PDFs"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# pdfseparate &amp;amp; pdfunite ship with poppler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# split input.pdf into output-1.pdf output-2.pdf ... etc.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pdfseparate input.pdf output-%d.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# split input.pdf from page 2 till the end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pdfseparate -f &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt; input.pdf output-%d.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="replacing-single-pages-in-pdf"&gt;
Replacing single pages in PDF
&lt;a class="heading-anchor" href="#replacing-single-pages-in-pdf" aria-label="Link to Replacing single pages in PDF"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A common usecase is to swap out a single page in a pdf. &lt;code&gt;poppler&lt;/code&gt; doesn&amp;rsquo;t have a
readymade command for this, instead i just use &lt;code&gt;pdfseparate&lt;/code&gt; + &lt;code&gt;pdfunite&lt;/code&gt; for
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-fish" data-lang="fish"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# input has 10 pages; special has 3 pages
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# replace pages 5,6,7 in input with special pages
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;pdfseparate&lt;/span&gt; input.pdf input-%d.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;pdfseparate&lt;/span&gt; special.pdf special-%d.pdf
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# using fish shell
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; i &lt;span style="color:#66d9ef"&gt;in&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;seq&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;&lt;span style="color:#f92672"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;pdfunite&lt;/span&gt; input-$i
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="resizingcompression"&gt;
Resizing/Compression
&lt;a class="heading-anchor" href="#resizingcompression" aria-label="Link to Resizing/Compression"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="by--or-boundary"&gt;
By % or boundary
&lt;a class="heading-anchor" href="#by--or-boundary" aria-label="Link to By % or boundary"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -resize 60% src_img.png src_img_reduced.png
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -resize 128x128 src_img.gif src_img_reduced.gif
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Doesn&amp;rsquo;t overwrite&lt;/li&gt;
&lt;li&gt;60% is good for documents&lt;/li&gt;
&lt;li&gt;75% is acceptable image quality (though you probably want higher)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="by-keeping-aspect-ratio--max-dimension"&gt;
By keeping aspect ratio &amp;amp; max dimension
&lt;a class="heading-anchor" href="#by-keeping-aspect-ratio--max-dimension" aria-label="Link to By keeping aspect ratio &amp;amp; max dimension"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sips -Z &lt;span style="color:#ae81ff"&gt;800&lt;/span&gt; input.png --out output.png
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Z maintain aspect ratio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 800 max height and width to be used&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="in-directory"&gt;
In directory
&lt;a class="heading-anchor" href="#in-directory" aria-label="Link to In directory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# resize all images in directory&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; f in *.jpeg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; convert -resize 60% &lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;$f&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; dest/src_img_reduced.png
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="converting"&gt;
Converting
&lt;a class="heading-anchor" href="#converting" aria-label="Link to Converting"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="dng--jpg"&gt;
DNG → JPG
&lt;a class="heading-anchor" href="#dng--jpg" aria-label="Link to DNG → JPG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sips ships natively with macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sips -Z &lt;span style="color:#ae81ff"&gt;3072&lt;/span&gt; -s format jpeg input.dng --out output.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Z maintain aspect ratio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -s format jpeg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Overwrites&lt;/li&gt;
&lt;li&gt;Aggressively reduces&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="png--jpg"&gt;
PNG → JPG
&lt;a class="heading-anchor" href="#png--jpg" aria-label="Link to PNG → JPG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sips ships natively with macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sips -s format png input.jpg --out output.jpeg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="heic--jpg"&gt;
HEIC → JPG
&lt;a class="heading-anchor" href="#heic--jpg" aria-label="Link to HEIC → JPG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sips ships natively with macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sips -s format jpeg input.heic --out output.jpeg
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="pdf--jpg"&gt;
PDF → JPG
&lt;a class="heading-anchor" href="#pdf--jpg" aria-label="Link to PDF → JPG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -density &lt;span style="color:#ae81ff"&gt;150&lt;/span&gt; -quality &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; -flatten -sharpen 0x1.0 -trim input.pdf output.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 3rd page of pdf&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert input.pdf&lt;span style="color:#f92672"&gt;[&lt;/span&gt;2&lt;span style="color:#f92672"&gt;]&lt;/span&gt; output.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert all pages (2 means 2 digits will show on page count)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert input.pdf output-%02d.jpg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# pdftoppm ships with poppler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# output is a prefix&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# 150 is DPI&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pdftoppm -jpeg -r &lt;span style="color:#ae81ff"&gt;150&lt;/span&gt; input.pdf output
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="jpg--pdf"&gt;
JPG → PDF
&lt;a class="heading-anchor" href="#jpg--pdf" aria-label="Link to JPG → PDF"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert 1.jpg 2.jpg merged.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="jpg--a4-paper-size-pdf"&gt;
JPG → (A4 paper size) PDF
&lt;a class="heading-anchor" href="#jpg--a4-paper-size-pdf" aria-label="Link to JPG → (A4 paper size) PDF"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -density &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt; input.jpeg -background white &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; -page a4 &lt;span style="color:#75715e"&gt;# -page Letter&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; output.pdf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="jpg--png"&gt;
JPG → PNG
&lt;a class="heading-anchor" href="#jpg--png" aria-label="Link to JPG → PNG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# sips ships natively with macOS&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sips -s format png input.jpg --out output.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="png--webp"&gt;
PNG → WEBP
&lt;a class="heading-anchor" href="#png--webp" aria-label="Link to PNG → WEBP"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install webp utility&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install webp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cwebp input.png -o output.webp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cwebp -q &lt;span style="color:#ae81ff"&gt;75&lt;/span&gt; input.png -o output.webp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# -q 75 refers to the compression rate.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="svg--webp"&gt;
SVG → WEBP
&lt;a class="heading-anchor" href="#svg--webp" aria-label="Link to SVG → WEBP"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -background none -density &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt; input.svg output.webp
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="svg--png"&gt;
SVG → PNG
&lt;a class="heading-anchor" href="#svg--png" aria-label="Link to SVG → PNG"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -background none -density &lt;span style="color:#ae81ff"&gt;1000&lt;/span&gt; -resize 48x is_logo_location.svg is_logo_location.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="png--gif"&gt;
PNG → GIF
&lt;a class="heading-anchor" href="#png--gif" aria-label="Link to PNG → GIF"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -delay &lt;span style="color:#ae81ff"&gt;5&lt;/span&gt; -loop &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; *.png output.gif
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="movmp4--gif"&gt;
MOV/MP4 → GIF
&lt;a class="heading-anchor" href="#movmp4--gif" aria-label="Link to MOV/MP4 → GIF"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# convert ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir frames
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# mov or m4v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ffmpeg -i input.mov -vf scale=320:-1,format=rgb8,format=rgb24 -r 10 frames/ffout%3d.png&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ffmpeg -i input.mov -vf scale&lt;span style="color:#f92672"&gt;=&lt;/span&gt;320:-1 -r &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt; frames/ffout%3d.png
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;convert -delay &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt; -loop &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; frames/ffout*.png frames/output.gif
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# scale [width in px]:-1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# r framerate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="manipulating"&gt;
Manipulating
&lt;a class="heading-anchor" href="#manipulating" aria-label="Link to Manipulating"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="remove-transparency"&gt;
Remove transparency
&lt;a class="heading-anchor" href="#remove-transparency" aria-label="Link to Remove transparency"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# flattens png to a solid color&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# mogrify ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mogrify -background white -flatten input*.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="get-information-from-image"&gt;
Get information from image
&lt;a class="heading-anchor" href="#get-information-from-image" aria-label="Link to Get information from image"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="is-image-transparent"&gt;
Is image transparent?
&lt;a class="heading-anchor" href="#is-image-transparent" aria-label="Link to Is image transparent?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# identify ships with imagemagick&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;identify -format &lt;span style="color:#e6db74"&gt;&amp;#39;%[channels]&amp;#39;&lt;/span&gt; foo.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="list-image-dimensions"&gt;
List image dimensions
&lt;a class="heading-anchor" href="#list-image-dimensions" aria-label="Link to List image dimensions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;identify -format &lt;span style="color:#e6db74"&gt;&amp;#39;&amp;#34;%w %h&amp;#39;&lt;/span&gt; foo.png
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;I&amp;rsquo;ll keep adding more as I discover new use cases.&lt;/em&gt;&lt;/p&gt;
&lt;!-- adding pdf watermark to pdfs
https://talk.automators.fm/t/adding-file-names-to-pdfs-in-macos/12770/5 --&gt;</description><guid>https://kau.sh/blog/terminal-cli-image-pdf-conversion/</guid><pubDate>Fri, 22 Jul 2022 03:40:45 GMT</pubDate></item><item><title>
Saving the world from code</title><link>https://kau.sh/blog/saving-the-world-from-code/</link><description>
&lt;p&gt;A good friend of mine who&amp;rsquo;s worked on some pretty pivotal hardware/software integrations for our time told me: &amp;ldquo;it feels like a miracle when I see the code I wrote drive physical bits. I almost can&amp;rsquo;t believe it. No seriously… how does this even work?&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If this sentiment resonates with you to some level, you will love reading this article:&lt;/p&gt;
&lt;h2 id="the-coming-software-apocalypse"&gt;
&lt;a href="https://www.theatlantic.com/technology/archive/2017/09/saving-the-world-from-code/540393/"&gt;The coming software apocalypse&lt;/a&gt;
&lt;a class="heading-anchor" href="#the-coming-software-apocalypse" aria-label="Link to The coming software apocalypse"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This entire article is fascinating. It took a lot of restraint not to just pull-quote every paragraph from this article.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The software did exactly what it was told to do. In fact it did it perfectly. The reason it failed is that it was told to do the wrong thing. Software failures are failures of understanding, and of imagination.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;This is the trouble with making things out of code, as opposed to something physical. “The complexity,” as Leveson puts it, “is invisible to the eye.” &amp;hellip;“Computing is fundamentally invisible,” Gerard Berry said in his talk. “When your tires are flat, you look at your tires, they are flat. When your software is broken, you look at your software, you see nothing.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The National Highway Traffic Safety Administration enlisted software experts from NASA to perform an intensive review of Toyota’s code. After nearly 10 months, the NASA team hadn’t found evidence that software was the cause — but said they couldn’t prove it wasn’t. &amp;hellip; They showed that as little as a single bit flip — a one in the computer’s memory becoming a zero or vice versa — could make a car run out of control.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“Visual Studio is one of the single largest pieces of software in the world,” he said. “It’s over 55 million lines of code. And one of the things that I found out in this study is more than 98 percent of it is completely irrelevant. All this work had been put into this thing, but it missed the fundamental problems that people faced. And the biggest one that I took away from it was that basically people are playing computer inside their head. &amp;hellip; So the students who did well — in fact the only ones who survived at all — were those who could step through that text one instruction at a time in their head, thinking the way a computer would, trying to keep track of every intermediate calculation”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;The programmer, staring at a page of text, was abstracted from whatever it was they were actually making. &amp;hellip; “Our current conception of what a computer program is,” he said, is “derived straight from Fortran and ALGOL in the late ’50s. Those languages were designed for punch cards. That code now takes the form of letters on a screen in a language like C or Java (derivatives of Fortran and ALGOL), instead of a stack of cards with holes in it, doesn’t make it any less dead, any less indirect. &amp;hellip; the idea that people were doing important work, like designing adaptive cruise-control systems or trying to understand cancer, by staring at a text editor, was appalling&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;An alternative way of writing code:&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="https://cdn.theatlantic.com/assets/media/files/videos/bret-victor-inventing-on-principle.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;This article has forced me to revisit my disdain for WYSIWYG like editors&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One programmer who saw the talk wrote later: “Suddenly all of my tools feel obsolete.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;When John Resig saw the “Inventing on Principle” talk, he scrapped his plans for the Khan Academy programming curriculum. He wanted the site’s programming exercises to work just like Victor’s demos.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“I’m not sure that programming has to exist at all,” he told me. “Or at least software developers.” In his mind, a software developer’s proper role was to create tools that removed the need for software developers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Programmers, as a species, are relentlessly pragmatic. Tools like TLA+ reek of the ivory tower. When programmers encounter “formal methods” (so called because they involve mathematical, “formally” precise descriptions of programs), their deep-seated instinct is to recoil.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;hello layout inspector&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/saving-the-world-from-code/</guid><pubDate>Sat, 18 Jun 2022 07:00:00 GMT</pubDate></item><item><title>
My productivity system</title><link>https://kau.sh/blog/productivity/</link><description>
&lt;p&gt;Recently, I killed an entire week moving from &lt;a href="https://culturedcode.com/things/"&gt;Things (3)&lt;/a&gt; → &lt;a href="https://app.asana.com/"&gt;Asana&lt;/a&gt; → &lt;a href="https://todoist.com/"&gt;Todoist&lt;/a&gt; → Things. I won&amp;rsquo;t go into the details but I had a realization: I need to jot down how I think about my current system of productivity.&lt;/p&gt;
&lt;p&gt;Having this clearly laid out will convince future me that moving to another tool is unnecessary if I follow the existing one I have diligently. I also think it necessary to lay down tactical instructions, so that in my moments of weakness I have something easy to follow and prevent me from going on a futile productivity sojourn.&lt;/p&gt;
&lt;h1 id="challenges"&gt;
Challenges
&lt;a class="heading-anchor" href="#challenges" aria-label="Link to Challenges"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This system works for me because it addresses specific challenges that I face in getting things done. It&amp;rsquo;s worth pointing out what my challenges are:&lt;/p&gt;
&lt;h2 id="trapped-in-burndown-continuum"&gt;
Trapped in burndown continuum
&lt;a class="heading-anchor" href="#trapped-in-burndown-continuum" aria-label="Link to Trapped in burndown continuum"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There are days I&amp;rsquo;m able to knock out tasks one after the other. I go weeks like a programmed robot knocking out tasks one after another as they percolate into my &amp;ldquo;Today&amp;rdquo; list. But I quickly burn out and am unable to go any further in this mode because I feel trapped in a continuum.&lt;/p&gt;
&lt;p&gt;I need to see the bird&amp;rsquo;s eye-view. I need to know why I&amp;rsquo;m doing what I&amp;rsquo;m doing. What are the daily tasks in service of? Am I reaching the goals I set out for myself this month or year?&lt;/p&gt;
&lt;h2 id="single-task-hang-up"&gt;
Single task hang up
&lt;a class="heading-anchor" href="#single-task-hang-up" aria-label="Link to Single task hang up"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There are days I start with 3 tasks to get done. I start working on one of the tasks but get absorbed into it that I forget about the other ones due for the same day. I barely finish the first task; and the others get pushed to tomorrow. Tomorrow comes and the story repeats.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Work expands to fill the time allotted for its completion.&lt;/p&gt;
&lt;p&gt;- Cyril Northcote Parkinson&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A sense of urgency and being conscious of time spent on a task is important.&lt;/p&gt;
&lt;h2 id="overwhelmed-with-tasks"&gt;
Overwhelmed with tasks
&lt;a class="heading-anchor" href="#overwhelmed-with-tasks" aria-label="Link to Overwhelmed with tasks"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There are days I open up my Today view and find 20 tasks to do – all moderately important and valuable to get done. I didn&amp;rsquo;t choose to do these tasks. The tasks chose me, engulfed me, invaded my Today view! So I abandon it all and do none of it 🙈.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I didn&amp;rsquo;t choose to do these tasks. The tasks chose me, engulfed me&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are other days when I wake up and see the same set of tasks over and over again. Monday, Tuesday, Wednesday … Thursday rolls over, still 20 tasks left. Little progress made and it knocks the wind out of me.&lt;/p&gt;
&lt;h2 id="what-should-i-do-next"&gt;
What should I do next?
&lt;a class="heading-anchor" href="#what-should-i-do-next" aria-label="Link to What should I do next?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There are days I open up my todo app and I just don&amp;rsquo;t know what to do next. I had ideas for getting things done this year, this month, this week&amp;hellip; but where do i start? Have I progressed on my goals at all or am I taking on tasks as they come rushing towards me again?&lt;/p&gt;
&lt;h1 id="strategy"&gt;
Strategy
&lt;a class="heading-anchor" href="#strategy" aria-label="Link to Strategy"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;We always overestimate the change that will occur in the next two years and underestimate the change that will occur in the next ten. Don&amp;rsquo;t let yourself be lulled into inaction.&lt;/p&gt;
&lt;p&gt;- Bill Gates&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This quote summarizes well the challenge I face with my productivity system and why it doesn’t work for me consistently: I focus on optimizing the daily/weekly but lose sight of the monthly/yearly.&lt;/p&gt;
&lt;h2 id="judicious-use-of-due-dates"&gt;
Judicious use of due-dates
&lt;a class="heading-anchor" href="#judicious-use-of-due-dates" aria-label="Link to Judicious use of due-dates"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;You should pick your tasks for the day. They should not come for you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To achieve this, I need to be judicious when picking a due-date/deadline for a task. Picking a date in the future means my todo app will automatically throw that task into my &amp;ldquo;Today&amp;rdquo; list in the future. I tend to get zealous and pick dates for when I think I can get the task done but when that ordained day comes around, I’m usually not ready to take on that task.&lt;/p&gt;
&lt;p&gt;Few tasks are so important so as to take up valuable future calendar real estate. Here&amp;rsquo;s a helpful heuristic:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Will you lose money if this task isn’t done?&lt;/li&gt;
&lt;li&gt;Will it affect your health?&lt;/li&gt;
&lt;li&gt;Will you be backing out of a commitment?&lt;/li&gt;
&lt;li&gt;Will it affect somebody else’s health?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unless the answer to any of the above is a yes, &lt;strong&gt;remove that due date&lt;/strong&gt;. Tuck it gently back into the backlog. If it’s important enough, it’ll crop back up again.&lt;/p&gt;
&lt;h2 id="monthly-logs"&gt;
Monthly logs
&lt;a class="heading-anchor" href="#monthly-logs" aria-label="Link to Monthly logs"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://culturedcode.com/things/"&gt;Things&lt;/a&gt; captures my daily and weekly views. This works.&lt;/p&gt;
&lt;p&gt;I track yearly progress with my &lt;a href="https://kau.sh/tags/new-year/"&gt;#new-year&lt;/a&gt; posts. This doesn’t work too well. The format itself is ok but compiling it all at the end of the year means I miss a whole bunch of intermediate progress.&lt;/p&gt;
&lt;p&gt;The missing piece was tracking monthly progress. Having the monthly check ins along with the daily + yearly ones will help me get a better view into the bigger picture with the visibility to course correct when needed.&lt;/p&gt;
&lt;p&gt;I’m planning to use my blog here to track my monthly progress. I even tweaked my theme to accommodate these special kind of posts.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="tactical-instructions"&gt;
Tactical Instructions
&lt;a class="heading-anchor" href="#tactical-instructions" aria-label="Link to Tactical Instructions"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;What does this system look like:&lt;/p&gt;
&lt;h2 id="new-year-retrospective-posts"&gt;
#new-year retrospective posts
&lt;a class="heading-anchor" href="#new-year-retrospective-posts" aria-label="Link to #new-year retrospective posts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;At the start of the year:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Track my progress against previous year’s goals.
&lt;ul&gt;
&lt;li&gt;Go through my monthly logs and track the highlights.&lt;/li&gt;
&lt;li&gt;Evaluate the lowlights and what I can do better.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;List my goals for the next year.
&lt;ul&gt;
&lt;li&gt;Create projects (or tasks) in Things for each of these.&lt;/li&gt;
&lt;li&gt;I should see these on my sidebar every time I open Things.&lt;/li&gt;
&lt;li&gt;It is ok to not achieve all of them.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="monthly-retrospective-posts"&gt;
#monthly retrospective posts
&lt;a class="heading-anchor" href="#monthly-retrospective-posts" aria-label="Link to #monthly retrospective posts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;At the end of every month:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jot down thoughts on how the month went.
&lt;ul&gt;
&lt;li&gt;Especially the unplanned highlights/lowlights.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;✅ monthly goals that got done.&lt;/li&gt;
&lt;li&gt;🙅 monthly goals that couldn&amp;rsquo;t get done.&lt;/li&gt;
&lt;li&gt;Pick goals for the next month.
&lt;ul&gt;
&lt;li&gt;Carry over previous month’s goals?
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s ok to throw them into a &amp;ldquo;someday&amp;rdquo; bucket.&lt;/li&gt;
&lt;li&gt;Carrying over a goal or task for a few months now? Think harder about throwing it to the “someday&amp;quot; list.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Need to make more progress on the yearly goals?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="things"&gt;
Things
&lt;a class="heading-anchor" href="#things" aria-label="Link to Things"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It’s a gorgeous app with top shelf UX. Stick with it. Sure Todoist has better integrations, Asana allows multiple sub-projects but not valuable enough to shift tools.&lt;/p&gt;
&lt;p&gt;If you think of an idea, put all the context in the task and toss it into the Inbox view immediately. Don&amp;rsquo;t waste time categorizing the task in the right project list or worrying how it fits in your monthly/yearly goals. Mind dump and move on.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The most effective todo system:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Every morning, write a list of the things that need to be done that day.&lt;/li&gt;
&lt;li&gt;Do them.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;At the start of the day:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clean your inbox
&lt;ul&gt;
&lt;li&gt;If you need to clarify, add more context and details to the task.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t be tempted to get them done Today.&lt;/li&gt;
&lt;li&gt;Find a proper home in a corresponding project.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Pick the 3 things you need to get done today.
&lt;ul&gt;
&lt;li&gt;Why couldn&amp;rsquo;t you finish yesterday&amp;rsquo;s tasks?&lt;/li&gt;
&lt;li&gt;Do they need to go to the Someday list?&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t add more tasks if you can&amp;rsquo;t finish the day with Today 0.&lt;/li&gt;
&lt;li&gt;Are any tasks coming up that are due?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="25mt-timers"&gt;
25mt timers
&lt;a class="heading-anchor" href="#25mt-timers" aria-label="Link to 25mt timers"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I wear an Apple watch and every time I embark on a task, I start a 25mt timer. This helps me timebox things. More importantly it gives me a sense of the time I&amp;rsquo;ve spent on a task. If i&amp;rsquo;ve completed four 25mt cycles, I need to start thinking about the other tasks.&lt;/p&gt;
&lt;p&gt;I’ll let you know how this all turns out.&lt;/p&gt;
&lt;!--
nxt Tags are like soft dude dates. Intention of getting done is tracked without commitment.
Add a next tag for soft due dates
--&gt;
&lt;p&gt;&lt;em&gt;I&amp;rsquo;ll update this post with screenshots once I have this system up and going for a few months&lt;/em&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;They’re private for now. I might make these public in the future 🤷‍♂️.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/productivity/</guid><pubDate>Tue, 31 May 2022 07:00:00 GMT</pubDate></item><item><title>
Remove Terminal.app from Alfred &amp; Spotlight</title><link>https://kau.sh/blog/remove-terminal-alfred-spotlight/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="./remove-terminal-alfred.png"
alt="Spotlight Alfred show Terminal.app and iTerm"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;I use &lt;a href="https://iterm2.com/"&gt;iTerm&lt;/a&gt; as my terminal of choice.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; It constantly annoys me when I use &lt;a href="https://www.alfredapp.com/"&gt;Alfred&lt;/a&gt; or Spotlight that the Terminal.app (native macOS) app also shows up in the search results.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is especially annoying on new computers when Alfred hasn&amp;rsquo;t learnt my preferences as Terminal.app would show up first in the results.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="options"&gt;
Options
&lt;a class="heading-anchor" href="#options" aria-label="Link to Options"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Quick googling shows two options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a Spotlight comment &lt;code&gt;alfred:ignore&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add the app to your Spotlight &amp;ldquo;privacy&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Option 1 is made a little complicated owing to the fact that you can&amp;rsquo;t add spotlight comments to native &amp;ldquo;Utilities&amp;rdquo; app on the Mac. I managed to do this but this option doesn’t work.&lt;/p&gt;
&lt;p&gt;At first, Option 2 might also seem tricky since the file picker doesn&amp;rsquo;t allow you to pick Utilities apps again. But there&amp;rsquo;s an easy workaround:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open Spotlight Preferences &amp;gt; Privacy Tab&lt;/li&gt;
&lt;li&gt;Open new Finder window &amp;amp; search for &amp;ldquo;Terminal.app&amp;rdquo;&lt;/li&gt;
&lt;li&gt;Drag Terminal.app search result to Privacy Tab on Spotlight preferences&lt;/li&gt;
&lt;li&gt;type &amp;ldquo;Reload&amp;rdquo; in Alfred&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, whenever I search for &amp;ldquo;Term&amp;rdquo;, I only see &amp;ldquo;iTerm&amp;rdquo; show up in my search results.&lt;/p&gt;
&lt;h2 id="video"&gt;
Video
&lt;a class="heading-anchor" href="#video" aria-label="Link to Video"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./remove-terminal-alfred-spotlight.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Why I use iTerm is for another blog post.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/remove-terminal-alfred-spotlight/</guid><pubDate>Sun, 29 May 2022 07:00:00 GMT</pubDate></item><item><title>
Introducing Henry for Hugo</title><link>https://kau.sh/blog/henry-hugo-theme/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/henry.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Henry is a theme I first open-sourced for &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. You can read all about it &lt;a href="../henry-jekyll-theme"&gt;here&lt;/a&gt;. I&amp;rsquo;ve now recreated &lt;a href="https://github.com/kaushikgopal/henry-hugo/"&gt;Henry for Hugo&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h2 id="why-hugo"&gt;
Why Hugo?
&lt;a class="heading-anchor" href="#why-hugo" aria-label="Link to Why Hugo?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Over the last year, I&amp;rsquo;ve been learning &lt;a href="https://golang.org/"&gt;Go&lt;/a&gt; and my interest in &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; piqued. Hugo — much like Jekyll — is a static blog engine written in Go. But it&amp;rsquo;s got more features, a more active community and is &lt;em&gt;blazingly&lt;/em&gt; fast.&lt;/p&gt;
&lt;h1 id="features"&gt;
Features
&lt;a class="heading-anchor" href="#features" aria-label="Link to Features"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Many of the features I wrote as custom features for Jekyll, come built-in with Hugo. But I took the opportunity to tweak these a little further.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how they&amp;rsquo;re implemented in Hugo:&lt;/p&gt;
&lt;h2 id="customizability"&gt;
Customizability
&lt;a class="heading-anchor" href="#customizability" aria-label="Link to Customizability"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A blog theme is only useful if it is versatile enough to reflect your unique character and touch. Henry is &lt;em&gt;extremely&lt;/em&gt; customizable. With simple scss override files you can customize the entire look of your blog.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;a href="https://github.com/kaushikgopal/henry-hugo/blob/master/assets/scss/style.scss"&gt;main style.scss file&lt;/a&gt; in Henry, here&amp;rsquo;s how we import the different styles:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// themes/henry/assets/scss/style.scss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;@import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;theme&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;@import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;mixins&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;code&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;base&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;@import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;main&amp;#34;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can override&lt;a href="https://github.com/kaushikgopal/henry-hugo/tree/master/assets/scss"&gt; any of the above files&lt;/a&gt; and customize your blog&amp;rsquo;s look. For example, to change the background color you can override the &lt;code&gt;theme.scss&lt;/code&gt; file like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// my-blog/assets/scss/theme.scss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;@import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#39;../../themes/henry/assets/scss/theme&amp;#39;&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Hook to override predefined variables.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;$color-background&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;#3E4244&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$color-header-index&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lighten&lt;/span&gt;($color-background&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;%&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$color-header&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lighten&lt;/span&gt;($color-background&lt;span style="color:#f92672"&gt;,&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;60&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;%&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see a demo of the difference compare &lt;a href="https://kau.sh/blog"&gt;this blog&lt;/a&gt; with my &lt;a href="https://karthickg.com/blog"&gt;brother&amp;rsquo;s&lt;/a&gt;. Both use Henry but look pretty different.&lt;/p&gt;
&lt;h2 id="footsidenotes"&gt;
&lt;del&gt;Foot&lt;/del&gt;Sidenotes
&lt;a class="heading-anchor" href="#footsidenotes" aria-label="Link to FootSidenotes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I picked this feature up from Tufte CSS&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; - inspired by the &lt;a href="https://en.wikipedia.org/wiki/Edward_Tufte"&gt;legendary Edward Tufte&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this post on a wide screen you should notice the tips to the side of this post. Go ahead and hover your cursor over to see the sidenote highlighted. On narrower screens (like phones) the sidenotes show up as traditional footnotes. You can use the quick jump links to navigate back and forth.&lt;/p&gt;
&lt;p&gt;You can read more about this feature and its implementation in this separate &lt;a href="../jekyll-footnote-tufte-sidenote/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="image-display"&gt;
Image display
&lt;a class="heading-anchor" href="#image-display" aria-label="Link to Image display"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Henry comes with all kinds of powerful and convenient image display options. We leverage Hugo &lt;a href="https://gohugo.io/content-management/shortcodes/"&gt;shortcode&lt;/a&gt;s for this.&lt;/p&gt;
&lt;p&gt;These are all the options that Henry&amp;rsquo;s &lt;code&gt;img&lt;/code&gt; shortcode has:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;!--
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Other than src everything is optional
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; VSCode users - look for a handy snippet later in this post
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;--&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;/assets/img/henry.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; class=&amp;#34;full-bleed&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; align=&amp;#34;center&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; alt=&amp;#34;Henry for Hugo&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; caption=&amp;#34;caption for image w/o link&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attrlink=&amp;#34;https://wiki/images&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attr=&amp;#34;Courtesy: Wikipedia&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is the simplest way to embed an image in your blog post:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# or using the native markdown syntax
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](/assets/img/lawsofux.webp)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;If you want to align your image to the center, just use the &amp;ldquo;align&amp;rdquo; option:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34; align=&amp;#34;center&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34; align=&amp;#34;left&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;div align="left"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34; align=&amp;#34;right&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;div align="right"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Henry allows you to attribute your images to the right source:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attrlink=&amp;#34;https://lawsofux.com/von-restorff-effect/&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attr=&amp;#34;Courtesy: lawsofux&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://lawsofux.com/von-restorff-effect/"&gt;
Courtesy: lawsofux
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;You can also caption your images:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; caption=&amp;#34;Von Restroff Effect&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
alt="Von Restroff Effect"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Von Restroff Effect
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you want to add custom classes you can pass them through the shortcode. For example, Henry defines a &amp;ldquo;callout&amp;rdquo; style class. Let&amp;rsquo;s add that to our image like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-scss" data-lang="scss"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// my-blog/assets/scss/main_override.scss
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.callout&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;border&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt; solid &lt;span style="color:#66d9ef"&gt;gray&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;padding&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;em&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;margin-bottom&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;30&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;px&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;./henry_image_lawsofux.webp&amp;#34; class=&amp;#34;callout&amp;#34; &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="callout"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
class="callout"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I also added a special &amp;ldquo;full-bleed&amp;rdquo; class that displays your images beautifully with some css-trickery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{{&amp;lt; img src=&amp;#34;https://assets.atlasobscura.com/image.jpg&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; class=&amp;#34;full-bleed&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attrlink=&amp;#34;https://www.atlasobscura.com/articles/manhole&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; attr=&amp;#34;Atlas Obscura&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="full-bleed"&gt;
&lt;img src="https://assets.atlasobscura.com/article_images/lg/63698/image.jpg"
class="full-bleed"
alt="Japanese Manhole Art"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://www.atlasobscura.com/articles/japanese-manhole-covers"&gt;
Atlas Obscura
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id="vscode-snippet"&gt;
VSCode Snippet
&lt;a class="heading-anchor" href="#vscode-snippet" aria-label="Link to VSCode Snippet"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you are a VSCode user and would like a &lt;a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets"&gt;snippet&lt;/a&gt; to autocomplete the img shortcode, here you go:&lt;/p&gt;
&lt;script src="https://gist.github.com/kaushikgopal/f4b19ea942551811055c712d8fe529f5.js"&gt;&lt;/script&gt;
&lt;h2 id="related-posts"&gt;
Related posts
&lt;a class="heading-anchor" href="#related-posts" aria-label="Link to Related posts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;All the way at the bottom of this post, you’ll notice a &amp;ldquo;related posts&amp;rdquo; section. In there, I’d like to show you more blog posts on the same topic that you might find interesting.&lt;/p&gt;
&lt;p&gt;Hugo has related posts built in as a feature. It&amp;rsquo;s also far more customizable than the one I built with the Jekyll version of Henry. Hugo&amp;rsquo;s documentation on configuring this feature can be a little tricky though, so here&amp;rsquo;s how you configure it the &amp;ldquo;Henry&amp;rdquo; way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;[&lt;span style="color:#a6e22e"&gt;related&lt;/span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;includeNewer&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;threshold&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;toLower&lt;/span&gt; = &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [[&lt;span style="color:#a6e22e"&gt;related&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;indices&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;tags&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;weight&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [[&lt;span style="color:#a6e22e"&gt;related&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;indices&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;categories&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;weight&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [[&lt;span style="color:#a6e22e"&gt;related&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;indices&lt;/span&gt;]]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; = &lt;span style="color:#e6db74"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;weight&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Say your blog &lt;code&gt;yaml&lt;/code&gt; header looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Introducing Henry - a Jekyll theme&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tags&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;jekyll, blogging]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Henry will search for similar posts that have the tag &amp;ldquo;jekyll&amp;rdquo; or &amp;ldquo;blogging&amp;rdquo; and show them in the related section at the bottom of every post. It&amp;rsquo;s that simple.&lt;/p&gt;
&lt;h2 id="external-urls-daring-fireball-style"&gt;
External URLs (Daring Fireball style)
&lt;a class="heading-anchor" href="#external-urls-daring-fireball-style" aria-label="Link to External URLs (Daring Fireball style)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;John Gruber of &lt;a href="https://daringfireball.net/"&gt;Daring Fireball&lt;/a&gt; fame pioneered a style of blogging where you share a link to another website but supplement it with commentary of your own.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./henry_dflink.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;The &lt;code&gt;»&lt;/code&gt; points you directly to the external website while clicking the title directly takes you to your internal version of the post containing the commentary.&lt;/p&gt;
&lt;p&gt;All you need is an &lt;code&gt;externalLink&lt;/code&gt; at the top of your post and Henry takes care of the rest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Fragmented Podcast - Episode #2 : Android Studio&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;externalLink&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http://fragmentedpodcast.com/episodes/2/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="canonical-urls"&gt;
Canonical URLs
&lt;a class="heading-anchor" href="#canonical-urls" aria-label="Link to Canonical URLs"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Sometimes I write blog posts for my company&amp;rsquo;s tech blog or a Medium account. If I want to give the post additional lift and surface it to my own followers, I like to additionally post the content to my own blog here. It&amp;rsquo;s a good idea to also host content at a place that you own and control.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But you have to be a little careful about copy pasting content in more than one place on the internet. Google might ding your search engine ranking for duplicating content. The kosher way to do this is using &amp;ldquo;canonical urls&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;With Henry, I can now copy paste the content from elsewhere, add the canonical url in my post and have it attributed correctly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Hackathon vote tabulation using Google Forms &amp;amp; Kotlin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;canonicalUrl &lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://tech.instacart.com/free-hackathon...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Should my company or Medium ever decide to bring my content down, I can remove the &lt;code&gt;canonical_url&lt;/code&gt; reference from the top of my post and rest peacefully knowing that my content will always be available here.&lt;/p&gt;
&lt;h2 id="alternate-urls-aliases"&gt;
Alternate URLs (aliases)
&lt;a class="heading-anchor" href="#alternate-urls-aliases" aria-label="Link to Alternate URLs (aliases)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Wouldn&amp;rsquo;t it be nice if I could link to the same blog post with easier slugs or URLs? I find the slug or title of a blog post limiting at times. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;https://kau.sh/blog/jekyll-footnote-tufte-sidenote/
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This works if you&amp;rsquo;re pasting a hyper link. But when i&amp;rsquo;m talking to folks, I&amp;rsquo;d like to be able to say &amp;ldquo;hey! go to my blog and add &lt;code&gt;/sidenotes&lt;/code&gt; to check that feature out&amp;rdquo;. Now try these URLs out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/sidenotes/"&gt;https://kau.sh/blog/sidenotes/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/sn/"&gt;https://kau.sh/blog/sn/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I had to build this feature custom with Jekyll but Hugo again has it built in as &amp;ldquo;aliases&amp;rdquo;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Jekyll footnotes as Edward Tufte inspired sidenotes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;aliases&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/sn/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/sidenotes/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Think of this feature as a &lt;code&gt;bitly&lt;/code&gt; or link shortener for your blog!&lt;/p&gt;
&lt;h2 id="draft-badges"&gt;
Draft badges
&lt;a class="heading-anchor" href="#draft-badges" aria-label="Link to Draft badges"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Hugo natively supports the draft feature pretty well.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;title: Jekyll footnotes as Edward Tufte inspired sidenotes
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;layout: post
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;draft: true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This draft post will only show up when you run the local server.&lt;/p&gt;
&lt;p&gt;This is great but my brother mentioned that when you start to accumulate a lot of drafts locally, it becomes confusing to differentiate between the published posts and local drafts. Wouldn’t it be nice if you had a “draft” badge of sorts to make the drafts jump out?&lt;/p&gt;
&lt;p&gt;Henry at your service:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/henry_drafts.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h2 id="rss-feeds"&gt;
RSS feeds
&lt;a class="heading-anchor" href="#rss-feeds" aria-label="Link to RSS feeds"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is one of the places where I feel Jekyll had an edge over Hugo. Jekyll generated well strucured atom feeds by default. Hugo&amp;rsquo;s RSS 2.0 feed was a tad bit limiting.&lt;/p&gt;
&lt;p&gt;I spent a lot of time tweaking the RSS feeds to play nice so you should be covered with Henry in Hugo as well.&lt;/p&gt;
&lt;h2 id="resources"&gt;
Resources
&lt;a class="heading-anchor" href="#resources" aria-label="Link to Resources"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="installation-instructions"&gt;
Installation instructions
&lt;a class="heading-anchor" href="#installation-instructions" aria-label="Link to Installation instructions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You can find &lt;a href="https://github.com/kaushikgopal/henry-hugo#getting-started"&gt;installation instructions on Github&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="reporting-bugsfeature-requestsquestions"&gt;
Reporting bugs/Feature requests/questions
&lt;a class="heading-anchor" href="#reporting-bugsfeature-requestsquestions" aria-label="Link to Reporting bugs/Feature requests/questions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you have noticed something off, want to ask questions or have bugs to file, please file them over in the &lt;a href="https://github.com/kaushikgopal/henry-hugo/issues"&gt;Github issues&lt;/a&gt;. I try to stay on top of that list.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not comfortable with Github you can hit me up on twitter &lt;a href="https://twitter.com/kaushikgopal"&gt;@kaushikgopal&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="supporting-henry"&gt;
Supporting Henry
&lt;a class="heading-anchor" href="#supporting-henry" aria-label="Link to Supporting Henry"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m fortunate to have a job that pays me to do work that I love. I&amp;rsquo;m not in need of the financial assistance right now.&lt;/p&gt;
&lt;p&gt;But If you&amp;rsquo;re feeling generous please donate to a charity you believe in and send me a link. That&amp;rsquo;ll inspire me more than anything else to keep working on Henry.&lt;/p&gt;
&lt;h3 id="do-you-use-henry"&gt;
Do you use Henry?
&lt;a class="heading-anchor" href="#do-you-use-henry" aria-label="Link to Do you use Henry?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Feel free to &lt;a href="https://github.com/kaushikgopal/henry-hugo#henry-in-the-wild"&gt;update this README.md&lt;/a&gt; if you use Henry. It&amp;rsquo;ll mean a lot to see Henry being used by creators in the wild.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;A set of tips and suggestions that help style web articles more legibly.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I learnt this the hard way when the startup I worked for (Wedding Party) shuttered down. It had a burgeoning tech blog with a lot of my earliest Android content. We were acquihired and the domain had to be given away. Alas! My posts were gone. Never again, I thought to myself.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/henry-hugo-theme/</guid><pubDate>Sun, 17 Oct 2021 21:14:04 GMT</pubDate></item><item><title>
Introducing Henry for Jekyll</title><link>https://kau.sh/blog/henry-jekyll-theme/</link><description>
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/henry.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Henry is a theme I first open-sourced for &lt;a href="http://jekyllrb.com/"&gt;Jekyll&lt;/a&gt;. I&amp;rsquo;ve now recreated &lt;a href="https://github.com/kaushikgopal/henry-hugo/"&gt;Henry for Hugo&lt;/a&gt; after migrated this blog to Hugo. You can &lt;a href="https://kau.sh/blog/henry-hugo-theme/"&gt;read about it here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;ve used Jekyll as my blog engine here and meticulously tweaked it over the years to support a bunch of features. Many folks have asked me if I would ever put my theme up for sale or distribute it more widely.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m happy to announce that it&amp;rsquo;s now available for free and &lt;a href="https://github.com/kaushikgopal/henry-jekyll"&gt;open source&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Introducing &lt;a href="https://github.com/kaushikgopal/henry-jekyll"&gt;Henry&lt;/a&gt; - a &lt;a href="https://github.com/jekyll/"&gt;Jekyll&lt;/a&gt; theme with a gorgeous reading experience, chock-full of features.&lt;/p&gt;
&lt;h1 id="features"&gt;
Features
&lt;a class="heading-anchor" href="#features" aria-label="Link to Features"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="customizability"&gt;
Customizability
&lt;a class="heading-anchor" href="#customizability" aria-label="Link to Customizability"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A blog theme is only useful if it is versatile enough to reflect your unique character and touch. Henry is &lt;strong&gt;extremely&lt;/strong&gt; customizable.&lt;/p&gt;
&lt;p&gt;With simple &lt;code&gt;scss&lt;/code&gt; override files you can customize the entire look of your blog. I&amp;rsquo;ve made sure the overrides get layered in, on top of Henry&amp;rsquo;s base CSS making it easy to quickly add your own customizations. Take a look:&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="https://kau.sh/videos/content/henry_customization.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;To see a demo of the difference, compare &lt;a href="https://kau.sh/blog"&gt;this blog&lt;/a&gt; (what you&amp;rsquo;re currently reading) with my &lt;a href="https://karthickg.com/blog"&gt;brother&amp;rsquo;s&lt;/a&gt;. Both use Henry.&lt;/p&gt;
&lt;p&gt;You can push the boundaries by adding independent pages and nifty scripting. Take a look at this sample &lt;a href="https://karthickg.com/blog/portfolio/"&gt;portfolio&lt;/a&gt; page I&amp;rsquo;ve started building for my brother&amp;rsquo;s blog.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="footsidenotes"&gt;
&lt;del&gt;Foot&lt;/del&gt;Sidenotes
&lt;a class="heading-anchor" href="#footsidenotes" aria-label="Link to FootSidenotes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I picked this feature up from Tufte CSS&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; - inspired by the legendary Edward Tufte.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this post on a wide screen you should notice the sidenote tips above. Go ahead and hover your cursor over it to see the sidenote highlighted. If you&amp;rsquo;re on a narrower screen (or phone) the sidenote morphs into a traditional footnote with quick jump links.&lt;/p&gt;
&lt;p&gt;You can read more about this feature and its implementation in this &lt;a href="../sidenotes/"&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="image-display"&gt;
Image display
&lt;a class="heading-anchor" href="#image-display" aria-label="Link to Image display"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;With Henry, you can display images in a variety of ways.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-md" data-lang="md"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;![](/assets/img/lawsofux.webp)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="callout"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
class="callout"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Or use HTML directly with customized css tags:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;img&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;tac&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;/assets/img/lawsofux.webp&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-sass" data-lang="sass"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#f92672"&gt;Added&lt;/span&gt; &lt;span style="color:#f92672"&gt;this&lt;/span&gt; &lt;span style="color:#f92672"&gt;to&lt;/span&gt; &lt;span style="color:#f92672"&gt;my&lt;/span&gt; &lt;span style="color:#f92672"&gt;main_override&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;.scss&lt;/span&gt; &lt;span style="color:#f92672"&gt;file&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;.tac&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt; &lt;/span&gt;&lt;span style="color:#a6e22e"&gt;{&lt;/span&gt;&lt;span style="color:#960050;background-color:#1e0010"&gt; &lt;/span&gt;&lt;span style="color:#a6e22e"&gt;margin&lt;/span&gt;&lt;span style="color:#f92672"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;auto&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="callout"&gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/henry_image_lawsofux.webp"
class="callout"
alt="Von Restroff Effect"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://lawsofux.com/von-restorff-effect/"&gt;
Courtesy: lawsofux
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;Henry comes with a beautiful &amp;ldquo;full-bleed&amp;rdquo; class:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;img&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;full-bleed&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;src&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;https://xxxx/63698/image.jpg&amp;#34;&lt;/span&gt; /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;figure class="full-bleed"&gt;
&lt;img src="https://assets.atlasobscura.com/article_images/lg/63698/image.jpg"
class="full-bleed"
alt="Japanese Manhole Art"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a href="https://www.atlasobscura.com/articles/japanese-manhole-covers"&gt;
Atlas Obscura
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id="related-posts"&gt;
Related posts
&lt;a class="heading-anchor" href="#related-posts" aria-label="Link to Related posts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;All the way at the bottom of this post, you&amp;rsquo;ll notice a &amp;ldquo;related posts&amp;rdquo; section. In there, I&amp;rsquo;d like to show you more blog posts on the same topic that you might find interesting. Other blog engines have plugins that run inscrutable algorithms to come up with such a list. Sometimes it works, while other times it&amp;rsquo;s embarrassingly wrong.&lt;/p&gt;
&lt;p&gt;I wanted to make the linking and relevance logic simpler. With Henry you simply add relevant tags for a blog post in its header:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Introducing Henry - a Jekyll theme&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;tags&lt;/span&gt;: [&lt;span style="color:#ae81ff"&gt;jekyll, blogging]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now Henry will search for similar blog posts that have the tag &amp;ldquo;jekyll&amp;rdquo; or &amp;ldquo;blogging&amp;rdquo; and show them in the related section. It&amp;rsquo;s that simple.&lt;/p&gt;
&lt;h2 id="external-urls-daring-fireball-style"&gt;
External URLs (Daring Fireball style)
&lt;a class="heading-anchor" href="#external-urls-daring-fireball-style" aria-label="Link to External URLs (Daring Fireball style)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;John Gruber of &lt;a href="https://daringfireball.net/"&gt;Daring Fireball&lt;/a&gt; fame pioneered a style of blogging where you share an interesting link to another website but add commentary to the post giving it more context.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./henry_dflink.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;The &lt;code&gt;»&lt;/code&gt; points you directly to the external website while clicking the primary title takes you to your own post containing the commentary.&lt;/p&gt;
&lt;p&gt;Add an &lt;code&gt;external_url&lt;/code&gt; to the top of your post and Henry takes care of everything.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;Fragmented Podcast - Episode #2 : Android Studio&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;externalLink&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;http://fragmentedpodcast.com/episodes/2/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="canonical-urls"&gt;
Canonical URLs
&lt;a class="heading-anchor" href="#canonical-urls" aria-label="Link to Canonical URLs"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Sometimes I write blog posts for my company&amp;rsquo;s tech blog or a Medium account. If I want to give the post additional lift and surface it to my own followers, I like to additionally post the content to my own blog here. It&amp;rsquo;s good idea to always have your content hosted at a place that you own and control.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;But you have to be a little careful about copy pasting content in more than one place on the internet, cause Google might ding your search engine ranking for duplicating content. The kosher way to do this is using &amp;ldquo;canonical urls&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;With Henry, I can now copy paste the content from elsewhere, add the canonical url in my post and have it attributed correctly.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Hackathon vote tabulation using Google Forms &amp;amp; Kotlin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;canonical_url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;https://tech.instacart.com/free-hackathon...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Should the other place decide to bring my content down, I can remove the &lt;code&gt;canonical_url&lt;/code&gt; reference and rest peacefully knowing that my content will always be available here.&lt;/p&gt;
&lt;h2 id="alternate-urls"&gt;
Alternate URLs
&lt;a class="heading-anchor" href="#alternate-urls" aria-label="Link to Alternate URLs"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Wouldn&amp;rsquo;t it be nice if I could link to the same blog post with easier slugs or URLs? I find the slug or title of a blog post limiting at times. Here&amp;rsquo;s an example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://kau.sh/blog/jekyll-footnote-tufte-sidenote/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works if you&amp;rsquo;re pasting a hyper link. But when i&amp;rsquo;m talking to folks, I&amp;rsquo;d like to be able to say &amp;ldquo;hey! go to my blog and add &lt;code&gt;/sidenotes&lt;/code&gt; to check that feature out&amp;rdquo;. Now try these URLs out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://kau.sh/blog/sidenotes/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;https://kau.sh/blog/sn/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;They all redirect to my original post. This is how I get that done with Henry:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;title&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Jekyll footnotes as Edward Tufte inspired sidenotes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;layout&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;post&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;aliases&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/sn/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;/sidenotes/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Think of this feature as a &lt;code&gt;bitly&lt;/code&gt; or link shortener for your blog!&lt;/p&gt;
&lt;h2 id="draft-badges"&gt;
Draft badges
&lt;a class="heading-anchor" href="#draft-badges" aria-label="Link to Draft badges"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Jekyll natively supports &lt;a href="https://jekyllrb.com/docs/posts/#drafts"&gt;&amp;ldquo;drafts&amp;rdquo;&lt;/a&gt; - posts that are work in progress or still in the ideas phase.&lt;/p&gt;
&lt;p&gt;You put them in a special &lt;code&gt;_drafts&lt;/code&gt; folder and those draft posts only show up when you launch your server locally. But they will never get published to &amp;ldquo;production&amp;rdquo; or your public website.&lt;/p&gt;
&lt;p&gt;This is great but my brother mentioned that when you start to accumulate a lot of drafts locally, it becomes confusing to differentiate between the published posts and local drafts. Wouldn&amp;rsquo;t it be nice if you had a &amp;ldquo;draft&amp;rdquo; badge of sorts to make the drafts jump out?&lt;/p&gt;
&lt;p&gt;Henry at your service:&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/henry_drafts.webp"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;h2 id="quick-setup"&gt;
Quick setup
&lt;a class="heading-anchor" href="#quick-setup" aria-label="Link to Quick setup"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A common complaint against Jekyll is that it can take time to get up and running with the right set of gems and ruby installations.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve created &lt;a href="https://www.docker.com/resources/what-container"&gt;Docker containers&lt;/a&gt; for Henry that allow you to get up and running pretty quickly on any machine.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;!--
Here's a video of me setting up Jekyll with Henry:
&lt;video controls muted playsinline loop&gt;
&lt;source src="https://kau.sh/assets/videos/posts/2021/henry_customization.mp4" type="video/mp4"&gt;
&lt;/video&gt;
--&gt;
&lt;p&gt;You also get a bunch of other quality of life (blogging) improvements like live reload and browser refresh, single command to launch locally or deploy to your server.&lt;/p&gt;
&lt;p&gt;Detailed instructions are in the &lt;a href="https://github.com/kaushikgopal/henry-jekyll#getting-started"&gt;Github README&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="rss-feeds"&gt;
RSS feeds
&lt;a class="heading-anchor" href="#rss-feeds" aria-label="Link to RSS feeds"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Tack on &lt;code&gt;/feed.xml&lt;/code&gt; to the end of your blog url and you have an atom feed generated.&lt;/p&gt;
&lt;p&gt;Henry comes configured with the &lt;a href="https://github.com/jekyll/jekyll-feed"&gt;jekyll-feed&lt;/a&gt; plugin for easy use. It allows a plethora of customizations like custom feeds for categories, post limits and &lt;a href="https://github.com/jekyll/jekyll-feed#usage"&gt;many others&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="resources"&gt;
Resources
&lt;a class="heading-anchor" href="#resources" aria-label="Link to Resources"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="reporting-bugs"&gt;
Reporting bugs
&lt;a class="heading-anchor" href="#reporting-bugs" aria-label="Link to Reporting bugs"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you have noticed something off or have bugs to file, please file them as &lt;a href="https://github.com/kaushikgopal/henry-jekyll/issues"&gt;Github issues&lt;/a&gt;. I try to stay on top of that list.&lt;/p&gt;
&lt;h3 id="feature-requests-or-questions"&gt;
Feature requests or questions
&lt;a class="heading-anchor" href="#feature-requests-or-questions" aria-label="Link to Feature requests or questions"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Want to know if Henry can support a feature, or will support one? Please use the &lt;a href="https://github.com/kaushikgopal/henry-jekyll/discussions"&gt;Github discussions page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not comfortable with Github you can hit me up &lt;a href="https://twitter.com/kaushikgopal"&gt;@kaushikgopal&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="supporting-henry"&gt;
Supporting Henry
&lt;a class="heading-anchor" href="#supporting-henry" aria-label="Link to Supporting Henry"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;m fortunate to have a job that pays me to do work that I love. I&amp;rsquo;m not in need of the financial assistance right now.&lt;/p&gt;
&lt;p&gt;But If you&amp;rsquo;re feeling generous please donate to a charity you believe in and send me a link. That&amp;rsquo;ll inspire me more than anything else to keep working on Henry.&lt;/p&gt;
&lt;h3 id="do-you-use-henry"&gt;
Do you use Henry?
&lt;a class="heading-anchor" href="#do-you-use-henry" aria-label="Link to Do you use Henry?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Feel free to &lt;a href="https://github.com/kaushikgopal/henry-jekyll/blob/main/README.md#henry-in-the-wild"&gt;update this README.md&lt;/a&gt; if you use Henry. It&amp;rsquo;ll mean a lot to see Henry being used by creators in the wild.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;He&amp;rsquo;s one of the sharpest product people I know and is currently on the market. Hit him up if you&amp;rsquo;re interested.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;A set of tips and suggestions that help style web articles more legibly.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;I learnt this the hard way when the startup I worked for (Wedding Party) shuttered down. It had a burgeoning tech blog with a lot of my earliest Android content. We were acquihired and the domain had to be given away. Alas! My posts were gone. Never again, I thought to myself.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;No more worrying about installing the right version of Ruby or Jekyll or a specific gem.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/henry-jekyll-theme/</guid><pubDate>Sat, 20 Mar 2021 07:00:00 GMT</pubDate></item><item><title>
How to rate a movie</title><link>https://kau.sh/blog/movie-rating-system/</link><description>
&lt;p&gt;Given how many movies and TV shows we&amp;rsquo;ve been watching these days, it&amp;rsquo;s useful to have a personal rating system. I&amp;rsquo;ve used and tweaked this scale over time and it&amp;rsquo;s worked pretty well for me.&lt;/p&gt;
&lt;h1 id="how-to-use-this-system"&gt;
How to use this system
&lt;a class="heading-anchor" href="#how-to-use-this-system" aria-label="Link to How to use this system"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The scale ranges from 0-5&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; but coming up with a specific number off the top of your head is tricky. That&amp;rsquo;s where the associated descriptions comes in handy.&lt;/p&gt;
&lt;p&gt;Consider a movie and see if &lt;em&gt;how you feel about the movie&lt;/em&gt;, matches with the associated descriptions. I&amp;rsquo;ve found that by asking these same set of questions it&amp;rsquo;s easier to rate movies more consistently.&lt;/p&gt;
&lt;p&gt;The idea here is to bring consistency in the way &lt;em&gt;you&lt;/em&gt; rate movies. Different folks have different tastes so don&amp;rsquo;t expect a 4 on your scale to match with a 4 on somebody else&amp;rsquo;s.&lt;/p&gt;
&lt;p&gt;But if more folks start to get consistent with their rating, it&amp;rsquo;s easier to identify those with similar tastes and ask them for their recommendations.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h1 id="rating-system"&gt;
Rating system
&lt;a class="heading-anchor" href="#rating-system" aria-label="Link to Rating system"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="05"&gt;
0.5
&lt;a class="heading-anchor" href="#05" aria-label="Link to 0.5"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This movie doesn&amp;rsquo;t exist to me. It&amp;rsquo;s so bad that I will not even mention its existence to other people.&lt;/p&gt;
&lt;h2 id="1-bad"&gt;
1 (bad)
&lt;a class="heading-anchor" href="#1-bad" aria-label="Link to 1 (bad)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I watched this movie and I regret it.&lt;/li&gt;
&lt;li&gt;I feel like I&amp;rsquo;ve been robbed off my time.&lt;/li&gt;
&lt;li&gt;I don&amp;rsquo;t recommend anyone watch it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="2-meh"&gt;
2 (meh)
&lt;a class="heading-anchor" href="#2-meh" aria-label="Link to 2 (meh)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I watched this movie.&lt;/li&gt;
&lt;li&gt;I don&amp;rsquo;t regret it per say.&lt;/li&gt;
&lt;li&gt;But I wouldn&amp;rsquo;t recommend to anyone else.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="3-like"&gt;
3 (like)
&lt;a class="heading-anchor" href="#3-like" aria-label="Link to 3 (like)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This movie was decent and I&amp;rsquo;m glad I watched it.&lt;/li&gt;
&lt;li&gt;It deserves to be watched at least once, so I would recommend this to others.&lt;/li&gt;
&lt;li&gt;But I personally wouldn&amp;rsquo;t watch this movie all over again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="4-really-like"&gt;
4 (really like)
&lt;a class="heading-anchor" href="#4-really-like" aria-label="Link to 4 (really like)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This movie was really good.&lt;/li&gt;
&lt;li&gt;This would go in my list of recommended movies for the year.&lt;/li&gt;
&lt;li&gt;Heck, I&amp;rsquo;d like to watch this movie again sometime.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="45-amazing"&gt;
4.5 (amazing)
&lt;a class="heading-anchor" href="#45-amazing" aria-label="Link to 4.5 (amazing)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This move was so good that I want to buy a copy of it.&lt;/li&gt;
&lt;li&gt;I would sit my friends down and watch this movie again with them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5-flawless"&gt;
5 (flawless)
&lt;a class="heading-anchor" href="#5-flawless" aria-label="Link to 5 (flawless)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This movie is a work of art and essential watching for our age.&lt;/li&gt;
&lt;li&gt;I want to see the director&amp;rsquo;s cut so I can enjoy it more.&lt;/li&gt;
&lt;li&gt;Every one should watch this movie at least once in their lifetime.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="lets-try-this-on-a-couple-of-movies"&gt;
Let&amp;rsquo;s try this on a couple of movies:
&lt;a class="heading-anchor" href="#lets-try-this-on-a-couple-of-movies" aria-label="Link to Let’s try this on a couple of movies:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="wonder-woman-1984--2"&gt;
&lt;a href="https://en.wikipedia.org/wiki/Wonder_Woman_1984"&gt;Wonder Woman 1984&lt;/a&gt; : 2
&lt;a class="heading-anchor" href="#wonder-woman-1984--2" aria-label="Link to Wonder Woman 1984 : 2"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="ww84.png"
alt="Wonder Woman 1984"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I watched the highly anticipated Wonder Woman as it debuted on HBO Max. I don&amp;rsquo;t regret watching it but I wouldn&amp;rsquo;t recommend it to anyone. You could spend that time watching a much better movie instead.&lt;/p&gt;
&lt;h3 id="the-midnight-sky--25"&gt;
&lt;a href="https://en.wikipedia.org/wiki/The_Midnight_Sky"&gt;The Midnight Sky&lt;/a&gt; : 2.5
&lt;a class="heading-anchor" href="#the-midnight-sky--25" aria-label="Link to The Midnight Sky : 2.5"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/en/3/35/The_Midnight_Sky_poster.png"
alt="The Midnight Sky"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I watched this movie cause Netflix pushed it as one of their most watched movie for 2020.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; I wasn&amp;rsquo;t terribly impressed.&lt;/p&gt;
&lt;p&gt;My first response to the movie was a resounding meh! I wouldn&amp;rsquo;t go around recommending this movie to my friends but I don&amp;rsquo;t regret watching it. So we&amp;rsquo;re off to a solid 2 again.&lt;/p&gt;
&lt;p&gt;I will say though if you have absolutely nothing else to watch and time on your hands then I wouldn&amp;rsquo;t &lt;em&gt;not&lt;/em&gt; recommend it. Since I&amp;rsquo;m feeling generous I&amp;rsquo;d bump this movie to a 2.5.&lt;/p&gt;
&lt;h3 id="golden-eye--3"&gt;
&lt;a href="https://en.wikipedia.org/wiki/GoldenEye"&gt;Golden Eye&lt;/a&gt; : 3
&lt;a class="heading-anchor" href="#golden-eye--3" aria-label="Link to Golden Eye : 3"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="https://image.tmdb.org/t/p/w500/bFzjdy6ucvNlXmJwoSoYfufV6lP.jpg"
alt="Golden Eye"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;This was my first Bond movie on the big screen so there&amp;rsquo;s definitely a strong sense of nostalgia here. Compared to the other Brosnan Bond movies, I&amp;rsquo;d say this stands out as one of the better ones.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t know if I&amp;rsquo;d go back and watch this whole movie again but I&amp;rsquo;m glad I saw it and wouldn&amp;rsquo;t mind recommending this to folks trying to catch up on their Bond history.&lt;/p&gt;
&lt;h3 id="ocean--4"&gt;
&lt;a href="https://en.wikipedia.org/wiki/Ocean%27s_Eleven"&gt;Ocean&amp;rsquo;s 11&lt;/a&gt; : 4
&lt;a class="heading-anchor" href="#ocean--4" aria-label="Link to Ocean : 4"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="https://image.tmdb.org/t/p/w500/v5D7K4EHuQHFSjveq8LGxdSfrGS.jpg"
alt="Golden Eye"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I love a good heist movie and Ocean&amp;rsquo;s 11 definitely redefined the genre. If this movie is running on cable or TV, I&amp;rsquo;d probably just land up watching it again. This is the kind of movie I&amp;rsquo;d catch myself YouTubing for clips now and then but I don&amp;rsquo;t think I would go so far as to buy myself a copy.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; It&amp;rsquo;s exactly what I would deem a 4 in my books.&lt;/p&gt;
&lt;h3 id="spider-man--into-the-spider-verse--45"&gt;
&lt;a href="https://en.wikipedia.org/wiki/Spider-Man:_Into_the_Spider-Verse"&gt;Spider Man : Into the Spider Verse&lt;/a&gt; : 4.5
&lt;a class="heading-anchor" href="#spider-man--into-the-spider-verse--45" aria-label="Link to Spider Man : Into the Spider Verse : 4.5"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/en/f/fa/Spider-Man_Into_the_Spider-Verse_poster.png"
alt="Spider Man : Into the Spider Verse"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;As soon as I watched this movie in the theater, I came back and pre-ordered a digital copy. So I knew we&amp;rsquo;re off to a strong 4.5.&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;If my friends haven&amp;rsquo;t watched this movie already (owing to preconceptions about it being a movie just for kids - superhero cartoons and all) this is the first movie I make them watch. Maps to how I feel about 4.5 movies.&lt;/p&gt;
&lt;h3 id="whiplash--5"&gt;
&lt;a href="https://en.wikipedia.org/wiki/Whiplash_%282014_film%29"&gt;Whiplash&lt;/a&gt; : 5
&lt;a class="heading-anchor" href="#whiplash--5" aria-label="Link to Whiplash : 5"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;figure &gt;
&lt;img src="https://upload.wikimedia.org/wikipedia/en/0/01/Whiplash_poster.jpg"
alt="Whiplash"
width="250"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;I get goosebumps every time I watch this movie. I love music so I might be a little biased but that&amp;rsquo;s how it works on my scale. The acting, the editing, the cinematography, the color grading. The editing is so tight in this movie that it makes you feel like you&amp;rsquo;re watching a boxing match. One for the ages!&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="faq" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-help-circle"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"&gt;&lt;/path&gt;&lt;path d="M12 17h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
thoughts?
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;Try this scale out the next time you watch a movie and let me know what you think!&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;mostly because Netflix (in the early days) and some other popular recommendation systems like MovieLens use this range. Multiply by 2 if you need a scale of 10.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I generally recommend movies &amp;gt;= 3 to my friends and it&amp;rsquo;s worked out pretty well.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;and of course it had George Clooney.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&amp;hellip; in theory. I bought the entire Ocean&amp;rsquo;s set when it was sale on a Cyber Monday deal.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;I want to point out - there exists 4.5 movies that I haven&amp;rsquo;t bought a copy of. So don&amp;rsquo;t take this as a rule but more a strong indicator.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/movie-rating-system/</guid><pubDate>Sun, 17 Jan 2021 07:00:00 GMT</pubDate></item><item><title>
2021</title><link>https://kau.sh/blog/2021/</link><description>
&lt;p&gt;From my &lt;a href="../2020"&gt;2020&lt;/a&gt; post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2019 has been the year that’s primed me the most for life changes. I imagine 2020 will the be the one where a lot of these changes materialize.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;hellip; that&amp;rsquo;s cute.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this in the future and suprised why, I suggest this &lt;a href="https://www.netflix.com/title/81332175"&gt;Netflix mockumentary&lt;/a&gt;. &lt;a href="https://www.imdb.com/title/tt1598778/"&gt;Contagion&lt;/a&gt; style blazing dumpster fire year aside, it’s been hard to find the motivation to write this post. But reading my &lt;a href="https://kau.sh/tags/new-year/"&gt;previous years’ posts&lt;/a&gt;, I’m usually grateful past me took the time to capture my thinking, priorities and general frame of reference.&lt;/p&gt;
&lt;p&gt;So power on, I shall.&lt;/p&gt;
&lt;h1 id="2020-recap"&gt;
2020 Recap:
&lt;a class="heading-anchor" href="#2020-recap" aria-label="Link to 2020 Recap:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="fragmented"&gt;
Fragmented
&lt;a class="heading-anchor" href="#fragmented" aria-label="Link to Fragmented"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://fragmentedpodcast.com/"&gt;Fragmented&lt;/a&gt; remains my proudest labor of love but I’m not kidding about the labor part of this statement. We&amp;rsquo;re taking it much slower on Fragmented releasing as and when, our lives allow us. But with 2021 rolling around, the hope is that we can kick things back up atleast from the current dormancy. Stay tuned.&lt;/p&gt;
&lt;h2 id="jklgg"&gt;
jkl.gg
&lt;a class="heading-anchor" href="#jklgg" aria-label="Link to jkl.gg"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Lot of changes here. I moved my primary domain and assets from kaush.co&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; to jkl.gg.&lt;/p&gt;
&lt;p&gt;This blog saw a lot of tweaks and customizations. The joke goes: most people with custom blog setups spend more time customizing their blog than writing posts. This was true for me but I actually enjoy the process of customizing my blog. In addition to (pleasantly) reminding me of my WebDev days, it also helps me learn and understand my UX skills better.&lt;/p&gt;
&lt;p&gt;Posts like the &lt;a href="../sidenotes/"&gt;one I wrote on sidenotes&lt;/a&gt;&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; come from this process. Another change that was probably not evident was the entire CSS refactor of this blog. A lot of &lt;code&gt;display:grid&lt;/code&gt; is now being used. No post on this but in the process of this refactor, I&amp;rsquo;ve actually started work on a minimal jekyll theme for podcasts .&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="mechanical-keyboards-and-hacking"&gt;
Mechanical keyboards and hacking
&lt;a class="heading-anchor" href="#mechanical-keyboards-and-hacking" aria-label="Link to Mechanical keyboards and hacking"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Productivity has always been a theme for this blog. This year I played (and spent) a lot with Mechanical Keyboards. It was a fun experiment, learning to flash and customize keyboards. Eventually settled with &lt;a href="../hacking-your-keyboard/"&gt;tricking out existing apple keyboards&lt;/a&gt; with karabiner and goku.&lt;/p&gt;
&lt;h2 id="lot-of-tv"&gt;
Lot of TV
&lt;a class="heading-anchor" href="#lot-of-tv" aria-label="Link to Lot of TV"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I watched a lot of TV. This is more than my usual self, which is to say a &lt;em&gt;heck of a lot&lt;/em&gt; more TV. As someone who&amp;rsquo;s known in friend circles as the guy who watches a lot of TV, obviously I&amp;rsquo;m asked for recommendations all the time.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve spent a lot of time in the process looking at Movie recommendation services and just rating systems in general. I plan to write a lot about that this year.&lt;/p&gt;
&lt;h1 id="random-learnings-from-2020"&gt;
Random learnings from 2020
&lt;a class="heading-anchor" href="#random-learnings-from-2020" aria-label="Link to Random learnings from 2020"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;a href="https://rachelbythebay.com/w/2020/02/09/horizonta/"&gt;Blog post on the Dangers of shipping software at the last minute&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Heroes don&amp;rsquo;t fall from the sky. They&amp;rsquo;re just ordinary people who step forward&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://castro.fm/episode/zuYUjM#12:48"&gt;Episode of The Daily&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Katz: &amp;hellip;and one thing that always stuck with me is how persuadable @dhh was when presented with actual solutions that people created to solve actual problems they encountered in real apps.&lt;/p&gt;
&lt;p&gt;DHH: SHOW ME THE BEFORE/AFTER CODE! It reduces all these abstract debates to the realm of the concrete. 80% of disagreements disappear when you look at real code vs make-believe arguments&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/wycats/status/1229807579380899840?s=21"&gt;Yehuda Katz&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;“They made their own clouds, brings to mind the people who built the great Gothic cathedrals. Knowing they’d be long dead before their work was finished, trusting their great-grandchildren would lay the final stones. We’ve lost that kind of generational thinking on Earth. Here, you see it in everything they do.”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Expanse Season 4 Episode 2&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We operate at the whim of this world&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Joe MacNally&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Adapt what is useful, reject what is useless, and add what is specifically your own.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bruce Lee courtesy &lt;a href="https://twitter.com/artishevskym/status/1231283086907858944?s=21"&gt;this tweet&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Even, when I&amp;rsquo;m disappointing you, I will tell you honestly why&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://inessential.com/2020/03/13/on_alternate_app_icons_for_netnewswire_f"&gt;Brent Simmons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1 id="parting-thoughts"&gt;
Parting thoughts
&lt;a class="heading-anchor" href="#parting-thoughts" aria-label="Link to Parting thoughts"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Recuperate, Regroup and Resume. Let that be the chanting slogan.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;kaush.co still redirects here.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;read this post on a browser if you&amp;rsquo;re on RSS.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Fragmented probably won&amp;rsquo;t use this but a newer podcast I&amp;rsquo;m helping get started, might 🤫.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/2021/</guid><pubDate>Fri, 01 Jan 2021 07:00:00 GMT</pubDate></item><item><title>
iPhones, Pixels and lazy Android developers</title><link>https://kau.sh/blog/iphones-pixels-lazy-android-developers/</link><description>
&lt;h1 id="the-bentwits"&gt;
The Bentwits
&lt;a class="heading-anchor" href="#the-bentwits" aria-label="Link to The Bentwits"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;So Ben Thompson&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; recently
&lt;a href="https://twitter.com/benthompson/status/1302185265524617217"&gt;tweeted&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve been using Android for the last couple of weeks, and honestly, the core OS is pretty good!&lt;/p&gt;
&lt;p&gt;The big problem is that Android apps are garbage relative to iOS apps.
If developers actually care about pushing back against Apple they should give a damn. They don&amp;rsquo;t.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He then went on to attribute the garbage quality of Android apps to developer
laziness. This &lt;em&gt;understandably&lt;/em&gt;
&lt;a href="https://twitter.com/ZacSweers/status/1302082690179629057?s=20"&gt;infuriated&lt;/a&gt; some of us #AndroidDev
unleashing the droid rage. To Ben&amp;rsquo;s credit, he has
&lt;a href="https://twitter.com/benthompson/status/1302185265524617217?s=20"&gt;since deleted&lt;/a&gt;
the specific tweet calling out developer laziness.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to spend time dissecting his tweets.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; We are all human and
occasionally tweet dumb things. But there&amp;rsquo;s a shred of truth nestled in that
starting tweet that just got me thinking.&lt;/p&gt;
&lt;h1 id="iphone-vs-pixel"&gt;
iPhone vs Pixel
&lt;a class="heading-anchor" href="#iphone-vs-pixel" aria-label="Link to iPhone vs Pixel"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Serendipitously, I&amp;rsquo;ve been
&lt;a href="https://twitter.com/kaushikgopal/status/1298692928081027072?s=20"&gt;thinking&lt;/a&gt; and &lt;a href="https://twitter.com/kaushikgopal/status/1298692084954587136?s=20"&gt;tweeting&lt;/a&gt;
about similar things .&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ll summarize the obvious points so we can get those out of the way:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;iOS does better with &lt;strong&gt;privacy&lt;/strong&gt; overall. It&amp;rsquo;s &lt;a href="https://www.androidcentral.com/apple-may-have-ditched-encrypted-backups-google-hasnt"&gt;not always a slam
dunk&lt;/a&gt;
but all things considered the Android experience is
&lt;a href="https://www.cnet.com/news/more-than-1000-android-apps-harvest-your-data-even-after-you-deny-permissions/"&gt;worse&lt;/a&gt; on &lt;a href="https://media.ccc.de/v/35c3-9941-how_facebook_tracks_you_on_android/"&gt;this&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=15141077"&gt;front&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;iPhone hardware is better than Pixel hardware especially when it comes to integration
with bluetooth accessories like the Apple Watch, AirPods, CarPlay etc.&lt;/li&gt;
&lt;li&gt;Camera is dicey. &lt;em&gt;I think&lt;/em&gt; the Pixel shoots better still photos than the
iPhone. The computational photography voodoo that Google does on pictures
after the fact makes the photos more appealing to my eyes. iPhones still
have the better Camera (hardware) though which is abundantly clear when
you try to shoot videos.&lt;/li&gt;
&lt;li&gt;Siri is a steaming pile of garbage. It&amp;rsquo;s a crying shame how bad it is given the
better hardware. Google Assistant alone makes me switch back to my Pixel
time and again.&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But there&amp;rsquo;s one important point that came up from this imbroglio that
has been hard to shake off &amp;hellip;and wherein lies the more interesting argument:&lt;/p&gt;
&lt;h1 id="which-phone-has-the-better-apps"&gt;
Which phone has the better apps?
&lt;a class="heading-anchor" href="#which-phone-has-the-better-apps" aria-label="Link to Which phone has the better apps?"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;In my &lt;a href="https://twitter.com/kaushikgopal/status/1298692928081027072?s=20"&gt;original
tweet&lt;/a&gt; I was
careful to say the iPhone has better &amp;ldquo;3rd party&amp;rdquo; apps.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a fair question to ask retorting that position: Give me examples of apps
that exist on both Android and iOS where the iOS experience is better?&lt;/p&gt;
&lt;p&gt;Sure, I can give examples&lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt; where it&amp;rsquo;s true most of the time but my point is
subtly different. If I rephrase that question however, it helps make my
point better:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Give me the best &amp;ldquo;insert category&amp;rdquo; app on the iPhone and on the Pixel. Which
one do you think works better?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s try that for a few categories:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Give me the best Todo list app on the iPhone and Pixel. Which one do you think
works better? (See &lt;a href="https://culturedcode.com/things/"&gt;Things&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Give me the best RSS feed reader app on the iPhone and Pixel. Which one do you
think works better? (See &lt;a href="https://reederapp.com/"&gt;Reeder&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Give me the best music making app on the iPhone and Pixel. Which one do you think
works better? (See &lt;a href="https://www.apple.com/ios/garageband/"&gt;GarageBand&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some will immediately jump in defense saying this is subjective.&lt;/p&gt;
&lt;p&gt;Sure&amp;hellip; Maybe? I&amp;rsquo;m open to being challenged here. But I build Android apps for a
living and I mostly use an iPhone today as my daily driver, so take it for what
that&amp;rsquo;s worth. Also chatting with other (dev) friends, I&amp;rsquo;ve heard I&amp;rsquo;m not alone.&lt;/p&gt;
&lt;h2 id="so-why-are-iphone-apps-better"&gt;
So why are iPhone apps better?
&lt;a class="heading-anchor" href="#so-why-are-iphone-apps-better" aria-label="Link to So why are iPhone apps better?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="android-tools-are-worse"&gt;
Android Tools are worse?
&lt;a class="heading-anchor" href="#android-tools-are-worse" aria-label="Link to Android Tools are worse?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Actually it&amp;rsquo;s the opposite! After dabbling with some iOS development, I can
confidently say Android Studio is &lt;del&gt;like a glass of ice water&lt;/del&gt; much better
than Xcode.&lt;sup id="fnref:6"&gt;&lt;a href="#fn:6" class="footnote-ref" role="doc-noteref"&gt;6&lt;/a&gt;&lt;/sup&gt; I also think Google as a company listens more closely and cares
about improving the developer experience. Many of their recent changes have been
in direct response to developer outcries.&lt;/p&gt;
&lt;p&gt;How many iOS developers can say the same of Apple?&lt;/p&gt;
&lt;h3 id="android-devs-are-lazy"&gt;
Android devs are lazy?
&lt;a class="heading-anchor" href="#android-devs-are-lazy" aria-label="Link to Android devs are lazy?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Abso-fucking-lutely not.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d even go so far as to argue that Android
developers care way more about clean architecture and good engineering practices
than iOS developers. I mean I&amp;rsquo;m biased here but consider the fact that Android
devs are typically early adopters of industry best
practices like &lt;a href="https://github.com/ReactiveX"&gt;Rx&lt;/a&gt;, MVI/Unidirectional State Flow,
Reactive UIs etc. You&amp;rsquo;ll find chatter, curiosity and first implementations
about these topics much earlier in the Android development community. But
here&amp;rsquo;s the rub:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;iOS devs didn&amp;rsquo;t have to care about these things.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="ios-devs-didnt-have-to-care"&gt;
iOS devs didn&amp;rsquo;t have to care
&lt;a class="heading-anchor" href="#ios-devs-didnt-have-to-care" aria-label="Link to iOS devs didn’t have to care"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You want to know why Android devs jumped on Rx? Because every time you
hang for a few extra seconds on the main thread you would get hit with a gnarly
&lt;a href="https://developer.android.com/topic/performance/vitals/anr"&gt;ANR&lt;/a&gt;. iPhones were
built to make single threaded processing blazingly fast. You can execute
database calls on the main thread and not worry for the most part. Try that on
an Android phone.&lt;/p&gt;
&lt;p&gt;You want to know why Android devs have heated week long discussions about
Dependency Injection and Dagger? Because we can&amp;rsquo;t use constructors in our
classes! iOS devs think we&amp;rsquo;re crazy cause our ViewControllers don&amp;rsquo;t just take in
our dependencies.&lt;/p&gt;
&lt;p&gt;You want to know why Android devs experiment and come up with all kinds of
different architectures to build apps? Because Google for the longest time
intentionally chose not to have an opinion. This changed in &lt;strong&gt;2017&lt;/strong&gt;. You know
how long Android has been around? since 2005. 12 years, if you&amp;rsquo;re doing the math.&lt;/p&gt;
&lt;p&gt;I also don&amp;rsquo;t need to reiterate the trope around Android being fragmented.&lt;sup id="fnref:7"&gt;&lt;a href="#fn:7" class="footnote-ref" role="doc-noteref"&gt;7&lt;/a&gt;&lt;/sup&gt; While
we would spend nights battling platform specific bugs, our iOS counterparts
could spend their time focusing on silky smooth UIs. They had the luxury of
perfecting and polishing (and good on them for doing that). &lt;code&gt;minSdk 14&lt;/code&gt; was
never a thing for iOS devs.&lt;/p&gt;
&lt;p&gt;So yes, we might have better tools courtesy Android Studio but make no mistake
it is easier to build higher quality apps on iOS than it is on Android.&lt;/p&gt;
&lt;h2 id="one-more-thing"&gt;
One more thing
&lt;a class="heading-anchor" href="#one-more-thing" aria-label="Link to One more thing"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s also this other big factor that accounts for better third party apps:
iOS users are more willing to spend money through the app ecosystem. Android
may have the bigger market share today but &lt;a href="https://www.prnewswire.com/news-releases/iphone-users-spend-101-every-month-on-tech-purchases-nearly-double-of-android-users-according-to-a-survey-conducted-by-slickdeals-300739582.html?c=n"&gt;more money is spent through iPhones&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If iPhone users are willing to spend more money, companies &lt;strong&gt;and
indie developers&lt;/strong&gt; are going to spend more time and resources building iOS apps
instead of Android apps. When more time and resources are spent instead on iOS
apps it follows that iOS is likely to have the better apps.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s harder technically to build a better Android app but Google is doing its
part to close that gap quickly. But unless Android users actually start
spending more money through apps, iOS is going to have the lead.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;A journalist who is otherwise a very thoughtful and astute tech reporter.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;This was a fleeting -i can&amp;rsquo;t sleep during corona so imma just tweet- oopsie. Definitely not one the finer Stratechery moments we&amp;rsquo;ve come to expect.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;The Pixel experience is the best experience for Android. Yes, I know Samsung hardware can be pretty good. When a non-bastardized Android experience comes to Samsung, I&amp;rsquo;ll switch in a heartbeat.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;and this is to a 3 year old Pixel 2. My Pixel 4A is coming end of this month.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;and burn bridges with my good Android dev friends who work at these companies.&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;Both Kotlin and Swift are amazing. So they have a negligible impact in this discussion.&amp;#160;&lt;a href="#fnref:6" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;Heck, I started a &lt;a href="https://fragmentedpodcast.com/"&gt;podcast&lt;/a&gt; with the same name.&amp;#160;&lt;a href="#fnref:7" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/iphones-pixels-lazy-android-developers/</guid><pubDate>Sun, 06 Sep 2020 07:00:00 GMT</pubDate></item><item><title>
Technical leadership and glue work</title><link>https://kau.sh/blog/leadership-and-glue-work/</link><description>
&lt;p&gt;If you&amp;rsquo;re in tech and have been thinking about your work
and role in your company, I highly encourage you to watch this talk by &lt;a href="https://twitter.com/whereistanya"&gt;Tanya
Reilly&lt;/a&gt; &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Setting myself a reminder to rewatch this again 6 months from now.&lt;/em&gt;&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/KClAPipnKqw?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Such a phenomenal speaker and powerful story telling! Follow her &lt;a href="https://twitter.com/whereistanya"&gt;@whereistanya&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/leadership-and-glue-work/</guid><pubDate>Fri, 28 Aug 2020 07:00:00 GMT</pubDate></item><item><title>
Jekyll footnotes as Edward Tufte inspired sidenotes</title><link>https://kau.sh/blog/jekyll-footnote-tufte-sidenote/</link><description>
&lt;p&gt;I&amp;rsquo;ve been looking into &lt;a href="https://edwardtufte.github.io/tufte-css/"&gt;Tufte
CSS&lt;/a&gt; recently. Tufte CSS -inspired by
the teachings of the legendary Edward Tufte&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;- provides suggestions and
tools to style web articles for improved legibility.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve started to incorporate some of those principles here while still trying to keep the authenticity of my original design. Most of these changes have been in the realm of CSS however the &lt;a href="https://edwardtufte.github.io/tufte-css/#sidenotes"&gt;sidenotes&lt;/a&gt; feature was a slightly trickier beast.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;I&amp;rsquo;ve implemented this feature in my blog theme &amp;ldquo;&lt;a href="https://kau.sh/blog/henry-hugo-theme/"&gt;Henry&lt;/a&gt;&amp;rdquo; for both &lt;a href="https://github.com/kaushikgopal/henry-hugo"&gt;Hugo&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/kaushikgopal/henry-jekyll"&gt;Jekyll&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Jekyll kramdown comes built in with a &lt;a href="https://kramdown.gettalong.org/syntax.html#footnotes"&gt;footnotes
feature&lt;/a&gt; that I&amp;rsquo;ve used here for some time. It works pretty well for the most part. The only problem though is -as the names suggests- it displays the notes all the way at the bottom of the articles near the footer.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./1_show_old_footnote.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;Edward Tufte instead suggests the use of &amp;ldquo;sidenotes&amp;rdquo;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sidenotes are like footnotes, except they don’t force the reader to jump their eye to the bottom of the page, but instead display off to the side in the margin.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So my objective was to use the existing kramdown pipes and generate the footnotes, but display them off to the side in the margin instead of at the bottom.&lt;/p&gt;
&lt;p&gt;Pulling this off is tricky with pure CSS but is baby talk for javascript. So I pulled out my trusty old jQuery&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; chops and got this fun side project done.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
source
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;You can find the &lt;a href="https://github.com/kaushikgopal/henry-hugo/blob/master/assets/js/sidenotes.js"&gt;js source code here&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;As is the case with these things though, the devil is in the details. I got the basic functionality down in about an hour but adding the finishing touches took more testing and improving. Here&amp;rsquo;s some of the ones I had to tackle:&lt;/p&gt;
&lt;h2 id="image-loading-throws-the-position-off"&gt;
Image loading throws the position off:
&lt;a class="heading-anchor" href="#image-loading-throws-the-position-off" aria-label="Link to Image loading throws the position off:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Since jQuery loads eagerly, it typically wouldn&amp;rsquo;t wait for the images to load
before calculating the position. This would cause the position to be off
sometimes.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./2_image_throws_off_position.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;This is a one-line fix in jQuery:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(window).&lt;span style="color:#a6e22e"&gt;on&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;load&amp;#34;&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// executes after everything in the DOM loads
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="resizing-the-browser-throws-position-off"&gt;
Resizing the browser throws position off:
&lt;a class="heading-anchor" href="#resizing-the-browser-throws-position-off" aria-label="Link to Resizing the browser throws position off:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Since most folks read blog posts on their phones and tablets these days, there&amp;rsquo;s
a much smaller chance they&amp;rsquo;re resizing their browser. But if you&amp;rsquo;re on a laptop
(as I am most of the time) then resizing your browser will completely throw the
positioning off again.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./3_resizing_throws_off_position.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;While there might exist more optimum solutions to fix this, I went with the
simpler approach of just detecting window resize changes and just reading the
sidenotes again:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(window).&lt;span style="color:#a6e22e"&gt;resize&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loadSidenotes&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ww&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$fnli&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fncount&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$fn&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I try to optimize this a little by &amp;ldquo;caching&amp;rdquo; the previous size and only
reloading the notes again if I detect a difference.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-js" data-lang="js"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(window).&lt;span style="color:#a6e22e"&gt;resize&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;new_ww&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;.wrapper&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;outerWidth&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;new_ww&lt;/span&gt; &lt;span style="color:#f92672"&gt;===&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ww&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// console.log(&amp;#34; XXX -- RESIZE -- XXX &amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;ww&lt;/span&gt; &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;new_ww&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;$&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;.sidenote&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;remove&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;loadSidenotes&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ww&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$fnli&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;fncount&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;$fn&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Again, this is probably not the most optimum solution but&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I don&amp;rsquo;t imagine people resizing their browser window that often&lt;/li&gt;
&lt;li&gt;there aren&amp;rsquo;t that many footnotes per post&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I opted for a simpler more understandable solution (than a
performant one) - an important software engineering learning that hasn&amp;rsquo;t failed
me so far.&lt;/p&gt;
&lt;p&gt;I also realized that my responsive CSS needed some tweaks, so had to tighten
that up too.&lt;/p&gt;
&lt;h2 id="remove-footnotes-from-the-bottom"&gt;
Remove footnotes from the bottom
&lt;a class="heading-anchor" href="#remove-footnotes-from-the-bottom" aria-label="Link to Remove footnotes from the bottom"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If the sidenotes land up displaying successfully, then I actually go the extra
mile and remove the footnotes from the bottom of the post. While I don&amp;rsquo;t &lt;em&gt;have&lt;/em&gt;
to do this, I think removing cruft and keeping it clean with just the essential
content helps the reader know when they&amp;rsquo;re done.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./5_removing_sidenotes.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;h2 id="on-hover-highlight-the-sidenote"&gt;
On hover highlight the sidenote
&lt;a class="heading-anchor" href="#on-hover-highlight-the-sidenote" aria-label="Link to On hover highlight the sidenote"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;By default I didn&amp;rsquo;t want the sidenote to really jump out and distract from the
post content. So by default they show up in a slightly muted color. But if you highlight the super script link ,&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; it should brighten the sidenote.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./4_highlight_sidenotes.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;h2 id="optimize-for-mobile-sizes"&gt;
Optimize for mobile sizes
&lt;a class="heading-anchor" href="#optimize-for-mobile-sizes" aria-label="Link to Optimize for mobile sizes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Now this is great for larger displays like tablets and laptops, but for smaller
displays like most mobile phones, the whole sidenote thing doesn&amp;rsquo;t work as well.
So in these cases, I only show the border marker indicating that a sidenote exists. I also don&amp;rsquo;t mute the color as the border is less distracting.&lt;/p&gt;
&lt;p&gt;In addition, I keep the original treatment and clicking the link takes you to
the bottom (as before). A future optimization would be to go all Tufte on this and displaying the sidenote on clicking. This should be easy enough to implement with jQuery.&lt;/p&gt;
&lt;video controls
width="100%"
autoplay
muted
loop
playsinline&gt;
&lt;source src="./6_mobile_shownotes.mp4" type="video/mp4"&gt;
&lt;span&gt;&lt;/span&gt;
&lt;/video&gt;
&lt;p&gt;This was a fun side project but sweating the details takes more time than you
would otherwise assume. What do you think? Would love to &lt;a href="https://twitter.com/kaushikgopal"&gt;hear your thoughts or suggestions on making this better&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;noted for his writings on information design and as a pioneer in the field of data visualization&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Yes, I know jQuery is old school and the youngins probably use fancier alternatives today but I love me some jQuery and it gets the job done.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;If you&amp;rsquo;re still reading this, thank you. As a sign of gratitude this is a dummy sidenote to make it easier for you to test my sidenote display.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/jekyll-footnote-tufte-sidenote/</guid><pubDate>Tue, 21 Jul 2020 07:00:00 GMT</pubDate></item><item><title>
Am I Yak-Shaving or Bikeshedding?</title><link>https://kau.sh/blog/yak-shaving-bike-shedding/</link><description>
&lt;p&gt;If you&amp;rsquo;re a software engineer trying to be snarky, it&amp;rsquo;s important to get these
terms right for maximum effect.&lt;/p&gt;
&lt;h2 id="what-is-yak-shaving"&gt;
What is Yak-Shaving?
&lt;a class="heading-anchor" href="#what-is-yak-shaving" aria-label="Link to What is Yak-Shaving?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Shaving a Yak&amp;rdquo; means performing a seemingly endless series of small tasks
that must be completed before the next step in the project can move forward.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id="is-this-a-good-thing-or-a-bad-thing"&gt;
Is this a good thing or a bad thing?
&lt;a class="heading-anchor" href="#is-this-a-good-thing-or-a-bad-thing" aria-label="Link to Is this a good thing or a bad thing?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;It depends. Yak Shaving is sometimes very much necessary. It&amp;rsquo;s &amp;ldquo;bad&amp;rdquo; only when
done unnecessarily. It&amp;rsquo;s &lt;em&gt;unfortunate&lt;/em&gt; when necessary but might not be a bad
thing at all under certain circumstances.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://seths.blog/2005/03/dont_shave_that/"&gt;Seth Godin&amp;rsquo;s blog post&lt;/a&gt; on the
subject was the first time I ran across this way back when and does a good job
of explaining this concept in great detail.&lt;/p&gt;
&lt;h2 id="what-is-bikeshedding"&gt;
What is Bikeshedding?
&lt;a class="heading-anchor" href="#what-is-bikeshedding" aria-label="Link to What is Bikeshedding?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Bike shedding&amp;rdquo; means spending most of your time discussing the simple but trivial issues
rather than focusing your time on discussions around the bigger but &lt;em&gt;harder&lt;/em&gt; tasks at hand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The parable goes: a committee of members started working on the design
of a nuclear power plant. Designing a new nuclear plant is probably no joke.
Given how daunting and overwhelming this task was, the committee instead
landed up spending most of that time designing just the bike-shed outside of the
plant.&lt;/p&gt;
&lt;h3 id="is-this-a-good-thing-or-a-bad-thing-1"&gt;
Is this a good thing or a bad thing?
&lt;a class="heading-anchor" href="#is-this-a-good-thing-or-a-bad-thing-1" aria-label="Link to Is this a good thing or a bad thing?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;This is most definitely a bad thing. It&amp;rsquo;s also referred to sometimes as
&lt;a href="https://en.wikipedia.org/wiki/Law_of_triviality"&gt;&amp;ldquo;Parkinson&amp;rsquo;s law of
triviality&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I will add this caveat though: many software engineers rush discussions around
topics like &amp;ldquo;naming&amp;rdquo; to be bike shedding. I vehemently defy such attempts.
Naming is an important part of maintaining any code base. It&amp;rsquo;s one of those deceptively
simple tasks that if done wrong will bite you in the behind for years to come.&lt;/p&gt;</description><guid>https://kau.sh/blog/yak-shaving-bike-shedding/</guid><pubDate>Wed, 15 Jul 2020 07:00:00 GMT</pubDate></item><item><title>
My new programming font - Iosevka</title><link>https://kau.sh/blog/build-iosevka-font-mac-os/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tip" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-flame"&gt;&lt;path d="M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;If you&amp;rsquo;re looking to just copy-paste instructions, &lt;a href="../build-iosevka-font-mac-os/#build-iosevka-from-source-on-macos"&gt;jump to the section below&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;For the longest time, I&amp;rsquo;ve been rocking IBM Plex Mono as &lt;a href="../my-new-programming-font/"&gt;my programming font&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While it has served me well, I started to &lt;a href="https://twitter.com/kaushikgopal/status/1241864968716185600"&gt;experiment&lt;/a&gt;
with some &lt;a href="https://monolisa.dev/"&gt;newer&lt;/a&gt; monospaced typefaces to see if I could
find one that was &lt;em&gt;even more&lt;/em&gt; legible.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; That experiment quickly devolved into
a rabbit-hole evaluation of a bunch of new programming fonts.&lt;/p&gt;
&lt;h2 id="hello-iosevka"&gt;
Hello &lt;a href="https://typeof.net/Iosevka/"&gt;Iosevka&lt;/a&gt;
&lt;a class="heading-anchor" href="#hello-iosevka" aria-label="Link to Hello Iosevka"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Eventually, I landed up with the font Iosevka:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s far more legible (courtesy the extra long glyphs)&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s set pretty narrow which makes it pretty space efficient &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;It&amp;rsquo;s 100% &lt;a href="https://github.com/be5invis/Iosevka"&gt;open-source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;You can build the font from source and customize it pretty extensively&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s ligatures (&amp;hellip;or not &amp;hellip;or dig this- for specific languages!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now at the outset, I actually didn&amp;rsquo;t like Iosevka at all. It was unbearably narrow
for my taste but thankfully it also comes with an &amp;ldquo;Extended&amp;rdquo; variant. The extended
variant adds a little more breathing room between characters making it less
condensed.&lt;/p&gt;
&lt;p&gt;After I fiddled with it more and chose specific glyphs that are easier on my eyes,
I think I landed up with a version that I absolutely love:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/iosevka-demo.gif" alt="iosevka demo" title="iosevka demo"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s more legible and dare I say a little more appealing than the trusty
IBM Plex Mono .&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; It almost hearkens back to my Inconsolata days (inspired by
Gary Bernhardt&amp;rsquo;s DAS screen casts).&lt;/p&gt;
&lt;h2 id="customizing-iosevka"&gt;
Customizing Iosevka
&lt;a class="heading-anchor" href="#customizing-iosevka" aria-label="Link to Customizing Iosevka"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Iosevka allows you to pick specific glyphs for certain characters.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/iosevka-customization.png" alt="Iosevka glyph customization" title="iosevka glyph customization"&gt;&lt;/p&gt;
&lt;p&gt;If you notice the cherry-picking section in the above image, I&amp;rsquo;ve chosen slightly
different variants of &lt;code&gt;g&lt;/code&gt;, &lt;code&gt;l&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, &lt;code&gt;G&lt;/code&gt;, &lt;code&gt;Q&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt; and &lt;code&gt;%&lt;/code&gt;. If you head on
over to the &lt;a href="https://typeof.net/Iosevka/"&gt;main website&lt;/a&gt;, you can even live preview and
test a bunch of combinations.&lt;/p&gt;
&lt;h2 id="build-iosevka-from-source-on-macos"&gt;
Build Iosevka from source (on macOS)
&lt;a class="heading-anchor" href="#build-iosevka-from-source-on-macos" aria-label="Link to Build Iosevka from source (on macOS)"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Once you&amp;rsquo;ve indulged your stylistic tendencies and honed in on the style you
prefer, you can build the font right from the source. I didn&amp;rsquo;t find too many
instructions online on how to do this with a Mac, so if you&amp;rsquo;re looking to do the same,
this should save you some trouble:&lt;/p&gt;
&lt;h3 id="install-necessary-dependencies"&gt;
Install necessary dependencies
&lt;a class="heading-anchor" href="#install-necessary-dependencies" aria-label="Link to Install necessary dependencies"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You need to have &lt;a href="https://brew.sh/"&gt;homebrew&lt;/a&gt;, &lt;a href="https://pip.pypa.io/en/stable/"&gt;pip&lt;/a&gt;
and &lt;a href="https://www.npmjs.com/get-npm"&gt;npm&lt;/a&gt; already installed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;git clone git@github.com:be5invis/Iosevka.git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# it&amp;#39;s big, so it&amp;#39;ll take sometime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cd Iosevka
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install ttfautohint
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew tap caryll/tap
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install otfcc-mac64
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;pip3 install afdko &lt;span style="color:#75715e"&gt;# has otf2otc&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm install
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="build-the-font"&gt;
Build the font
&lt;a class="heading-anchor" href="#build-the-font" aria-label="Link to Build the font"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Make a copy of the sample build plan first and then edit it with your
customizations. If you take a look &lt;a href="https://gist.github.com/kaushikgopal/79a31749b33f3de3a8f11abe34cca263"&gt;at my sample build plan&lt;/a&gt;, it should become obvious how to do that.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cp private-build-plans.sample.toml private-build-plans.toml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vi private-build-plans.toml
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# make your customizations&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;npm run build -- ttf::iosevka-kg
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# fonts should be in dist/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# if you want to generate woff, eot etc.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# npm run build -- contents::iosevka-kg&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it! Open the &lt;code&gt;dist&lt;/code&gt; directory and you should find your very own version
of Iosevka.&lt;/p&gt;
&lt;p&gt;You can &lt;a href="https://github.com/kaushikgopal/Iosevka/tree/kg/custom/dist/kg/ttf"&gt;download the variant I&amp;rsquo;m currently using
here&lt;/a&gt;.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;While aesthetics played heavily into my choice before, nowadays my eyes are fierce about keeping legibility as the number one priority (a LASIK will do that to you).&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Truth be told, I&amp;rsquo;m actually not a fan of ultra-narrow fonts (looking at you Pragmata) and Iosevka is pretty darn narrow for my taste. But more on this later.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Either that or I&amp;rsquo;m just bored 😋.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/build-iosevka-font-mac-os/</guid><pubDate>Tue, 14 Apr 2020 07:00:00 GMT</pubDate></item><item><title>
My 1st screen cast: awk program from scratch</title><link>https://kau.sh/blog/screencast-1-build-awk-jekyll-permalink-migration/</link><description>
&lt;p&gt;Two big announcements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I made my first screen cast &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;ve updated the permalink title for all the blog posts here &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I wanted to make sure that external blogs linking to previous post links
wouldn&amp;rsquo;t break. So in order to do this, I had to edit all of my
previous blog posts and add a &lt;code&gt;redirect_from&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;Life is too short to be doing this manually so I whipped up an
&lt;a href="https://en.wikipedia.org/wiki/AWK"&gt;awk&lt;/a&gt; program to do
it for me in about 20 minutes. It was so much fun building the program that I
figured I should screen cast the process.&lt;/p&gt;
&lt;p&gt;Recording the screen cast and exporting a decent version of it took a lot more
than 20 minutes but I thoroughly enjoyed the process. You can watch the screen
cast in its entirety on Youtube:&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/LaHIQ2Bwvjo?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;p&gt;If you enjoyed it, please do let me know on
&lt;a href="https://twitter.com/kaushikgopal"&gt;twitter&lt;/a&gt; or in the
&lt;a href="https://www.youtube.com/watch?v=LaHIQ2Bwvjo"&gt;comments&lt;/a&gt; over at YouTube.&lt;/p&gt;
&lt;h2 id="notes-about-the-screen-cast"&gt;
Notes about the screen cast
&lt;a class="heading-anchor" href="#notes-about-the-screen-cast" aria-label="Link to Notes about the screen cast"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;I use the GNU variant of awk (&lt;a href="https://www.gnu.org/software/gawk/"&gt;gawk&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BurntSushi/ripgrep"&gt;ripgrep&lt;/a&gt; is what I use to search&lt;/li&gt;
&lt;li&gt;My shell is &lt;a href="https://fishshell.com/"&gt;fish&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Terminal theme is &lt;a href="https://draculatheme.com/pro/"&gt;Dracula Pro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The font I use of course is the venerable &lt;a href="../my-new-programming-font/"&gt;IBM Plex Mono&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="meta-notes-on-screen-cast"&gt;
Meta notes on screen cast
&lt;a class="heading-anchor" href="#meta-notes-on-screen-cast" aria-label="Link to Meta notes on screen cast"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;It took 5 takes to get the final one right&lt;/li&gt;
&lt;li&gt;I used an iPhone to record my keyboard &amp;amp; QuickTime Player to record the screen&lt;/li&gt;
&lt;li&gt;Adobe Premier Pro was my video editor of choice (I&amp;rsquo;m an Audition devotee)&lt;/li&gt;
&lt;li&gt;I don&amp;rsquo;t typically use an Apple keyboard &lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; but my darn Kinesis wouldn&amp;rsquo;t fit
comfortably in the iPhone screen (without propping the phone too high).&lt;/li&gt;
&lt;li&gt;I had both a mic and the iPhone on stands, both of which were obstructing my view
to the keyboard. This would explain why I&amp;rsquo;m kind of sloppy at times with the
keyboard navigation.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;One of &lt;a href="../2020/#2-make-5-screencasts"&gt;my goals for 2020&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;This might have messed with your RSS unread feed count. Sorry 😬&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://kinesis-ergo.com/shop/advantage2/"&gt;Kinesis Advantage Pro&lt;/a&gt; in case you were wondering&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/screencast-1-build-awk-jekyll-permalink-migration/</guid><pubDate>Tue, 10 Mar 2020 07:00:00 GMT</pubDate></item><item><title>
git-number to the command line rescue</title><link>https://kau.sh/blog/git-number/</link><description>
&lt;p&gt;If you&amp;rsquo;re a programmer these days, you probably spend a large part of your day in &lt;a href="https://git-scm.com/"&gt;git&lt;/a&gt;. If you&amp;rsquo;re a command line zealot like me, you realize the holy ways of using your Terminal app for &lt;em&gt;everything&lt;/em&gt; and aren&amp;rsquo;t seduced by fancy GUIs that only stand to dissuade you from pure unbridled productivity.&lt;/p&gt;
&lt;p&gt;With that in mind, one typically finds themselves in a position where they have a few files that have changed liked so:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/git-0.png" alt="git showing changed files"&gt;&lt;/p&gt;
&lt;p&gt;If I had to add 2 out of those 3 files,&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; typing the commands start to get a little tedious:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; git add images/content/git-0.png
:&amp;gt; git add images/content/git-1.png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sure, if you happen to use &lt;a href="https://fishshell.com/"&gt;fish&lt;/a&gt; (one of the nicest shells out there) the autocompletion helps reduce the tediousness. But it still doesn&amp;rsquo;t beat being able to add files just by a number! What if you were presented with a git status like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/git-1.png" alt="git-number showing changed files"&gt;&lt;/p&gt;
&lt;p&gt;and then simply added the files by their reference number, like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; g add 4
:&amp;gt; g add 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wouldn&amp;rsquo;t that be better? I&amp;rsquo;ve been doing this for years and trust me, it is! You probably won&amp;rsquo;t see the difference with just a few files, but if you had 10 images being commited, then being able to simply type a range makes it a breeze to commit a bunch of files!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; g add 4-10
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="installation"&gt;
Installation
&lt;a class="heading-anchor" href="#installation" aria-label="Link to Installation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So, how do you achieve something similar? Install &lt;a href="https://github.com/holygeek/git-number"&gt;git-number&lt;/a&gt; and 💥:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; brew install git-number
:&amp;gt; git-number -s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For years, I used a bunch of hand wrangled ruby files, that would help me achieve something similar but I discovered @holygeek already went through the trouble of building a much more mature version of this in Perl. Now I&amp;rsquo;m just using that version (and have gladly deleted all those scripts I had to grudgingly maintain).&lt;/p&gt;
&lt;h2 id="notes"&gt;
Notes
&lt;a class="heading-anchor" href="#notes" aria-label="Link to Notes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="remember-to-git-number-first"&gt;
Remember to git-number first
&lt;a class="heading-anchor" href="#remember-to-git-number-first" aria-label="Link to Remember to git-number first"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s important to make sure you run &lt;code&gt;git-number&lt;/code&gt; first before running your &lt;code&gt;git add &amp;lt;numeral&amp;gt;&lt;/code&gt; commands. I&amp;rsquo;ve noticed that git-number gets a little confused and tends to try issue commands on the wrong files from previous runs, if you don&amp;rsquo;t (yikes).&lt;/p&gt;
&lt;p&gt;You probably want to run git-number anyway to see the status of changed files. This replaces &lt;code&gt;git status&lt;/code&gt; which you probably would have otherwise run to see what&amp;rsquo;s changed.&lt;/p&gt;
&lt;h3 id="works-for-most-git-commands"&gt;
Works for most git commands
&lt;a class="heading-anchor" href="#works-for-most-git-commands" aria-label="Link to Works for most git commands"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I demostrated the ease of using numbers with &lt;code&gt;git add&lt;/code&gt; but git-number isn&amp;rsquo;t limited to just &lt;code&gt;add&lt;/code&gt;. It also works for most other git commands where you have to reference a filename like &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;reset&lt;/code&gt; etc.&lt;/p&gt;
&lt;h3 id="aliases-and-conveniences"&gt;
Aliases and conveniences
&lt;a class="heading-anchor" href="#aliases-and-conveniences" aria-label="Link to Aliases and conveniences"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The keenly observant would notice in the examples above that I use &lt;code&gt;g add &amp;lt;number&amp;gt;&lt;/code&gt; instead of &lt;code&gt;git-number add &amp;lt;number&amp;gt;&lt;/code&gt;. This is because I almost always want to use &lt;code&gt;git-number&lt;/code&gt; instead of &lt;code&gt;git&lt;/code&gt;. So I have an alias for conveniences:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alias g = git-number
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The good thing with git-number is that it works even if i&amp;rsquo;m not referencing a file number, so this will technically also work:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; git-number commit -m &amp;quot;This is a diligently formed commit 😇&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="but-what-about-my-auto-completions"&gt;
But what about my auto-completions?
&lt;a class="heading-anchor" href="#but-what-about-my-auto-completions" aria-label="Link to But what about my auto-completions?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The only trouble with replacing git with git-number altogether, is you might lose your auto-completions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:&amp;gt; git-number checkout kg/feat/myBr&amp;lt;Tab&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Typically when you hit the tab key (where it says &amp;lt;Tab&amp;gt; above) you would expect the different branch names to be autocompleted but since most shells are configured to autocomplete only if when they see a &amp;ldquo;git&amp;rdquo; command being used, this can pose a problem.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re using fish &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; though, this is easily &lt;a href="https://github.com/fish-shell/fish-shell/issues/6601"&gt;possible&lt;/a&gt; if you use a function&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-shell" data-lang="shell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt; g &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; --description &lt;span style="color:#e6db74"&gt;&amp;#34;abbreviation for git-number w/auto completion&amp;#34;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#ae81ff"&gt;&lt;/span&gt; --wraps git
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; switch $argv&lt;span style="color:#f92672"&gt;[&lt;/span&gt;1&lt;span style="color:#f92672"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;reb*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; git $argv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;case&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;*&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; git number $argv
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;end
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now you can go about your merry way, using &lt;code&gt;g&lt;/code&gt; as your git command alias, have autocompletion and use &lt;code&gt;git-number&lt;/code&gt; to boot.&lt;/p&gt;
&lt;p&gt;Give git-number a try. You won&amp;rsquo;t regret it.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;be a good git citizen and make meaningful commits&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;and really why aren&amp;rsquo;t you? it&amp;rsquo;s the best shell out there&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;code&gt;git-number&lt;/code&gt; isn&amp;rsquo;t happy playing with commands like &lt;code&gt;rebase&lt;/code&gt; though, so best to avoid them&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/git-number/</guid><pubDate>Wed, 19 Feb 2020 07:00:00 GMT</pubDate></item><item><title>
2020</title><link>https://kau.sh/blog/2020/</link><description>
&lt;p&gt;2019 has been the year that&amp;rsquo;s primed me the most for life changes. I imagine 2020 will the be the one where a lot of these changes materialize.&lt;/p&gt;
&lt;h1 id="2019-recap"&gt;
&lt;a href="../2019"&gt;2019&lt;/a&gt; Recap
&lt;a class="heading-anchor" href="#2019-recap" aria-label="Link to 2019 Recap"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="practice-vim-for-30-days"&gt;
Practice Vim for 30 days
&lt;a class="heading-anchor" href="#practice-vim-for-30-days" aria-label="Link to Practice Vim for 30 days"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Not only did I successfully try it for 30 days, I now use vim as my primary text editor of choice and absolutley love it. It deserves a separate post though.&lt;/p&gt;
&lt;h2 id="blogging--screencasting"&gt;
Blogging + screencasting?
&lt;a class="heading-anchor" href="#blogging--screencasting" aria-label="Link to Blogging &amp;#43; screencasting?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I wrote 12 posts last year so I&amp;rsquo;m pretty happy with the 1-post-per-month average.I&amp;rsquo;m definitely looking to ramp up this number in 2020.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t do any screencasting though. I purchased a screaming hot license for &lt;a href="https://www.telestream.net/screenflow/store.asp"&gt;ScreenFlow&lt;/a&gt; but left it unopened for the rest of the whole year 👎. &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h2 id="care-about-your-privacy"&gt;
Care about your privacy
&lt;a class="heading-anchor" href="#care-about-your-privacy" aria-label="Link to Care about your privacy"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I definitely have been more acutely aware of privacy and how my data is being collected by different services and platforms out there. The biggest change I&amp;rsquo;ve made is the iPhone X for all personal use .&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; This was an incredibly tough pill to swallow for me. I don&amp;rsquo;t intend to write about this cause it always feels like a moving target but I do have copious notes on the matter. Let me know if you&amp;rsquo;re interested and I might just convert those notes into a blog post.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve also switched to Firefox for my browsing. If you&amp;rsquo;re considering the switch for privacy reasons, make sure you&amp;rsquo;re also using the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/multi-account-containers/"&gt;Multi-Account Containers add-on&lt;/a&gt;. That coupled with the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/temporary-containers/"&gt;Temporary Containers add-on&lt;/a&gt; is what makes it a truly private browsing experience. This definitely warrants a post or screencast so look for that in the future.&lt;/p&gt;
&lt;h2 id="miscellaneous"&gt;
Miscellaneous
&lt;a class="heading-anchor" href="#miscellaneous" aria-label="Link to Miscellaneous"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I try to speak at one international conference every year (always a new talk, in a city/country I&amp;rsquo;ve never been to before). This year I had the opportunity to give &lt;a href="https://www.youtube.com/watch?v=BdGVCsHj2vU"&gt;a talk&lt;/a&gt; in Poland. This trip was the highlight of the year.&lt;/p&gt;
&lt;p&gt;I am terribly disappointed that I didn&amp;rsquo;t make my annual trip to Disneyland &lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; but I shall make amends.&lt;/p&gt;
&lt;p&gt;On completing 185 episodes of &lt;a href="https://fragmentedpodcast.com"&gt;Fragmented&lt;/a&gt; -the podcast I co-created with my buddy Donn- we decided to change things up a little. That was a big decision for us. If you&amp;rsquo;re curious you can listen to the changes here:&lt;/p&gt;
&lt;p&gt;&lt;audio style="width:100%" controls src="https://audio.simplecast.com/953654ad.mp3"&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;h1 id="2020-goals"&gt;
2020 Goals
&lt;a class="heading-anchor" href="#2020-goals" aria-label="Link to 2020 Goals"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;In &lt;a href="https://kau.sh/tags/new-year/"&gt;past years&lt;/a&gt; I&amp;rsquo;ve kept my resolutions and goals pretty generic (do more X, get more fit, sleep more etc.). I want to try and make them more specific for 2020:&lt;/p&gt;
&lt;h2 id="1-write-24-blog-posts"&gt;
1. Write 24 blog posts
&lt;a class="heading-anchor" href="#1-write-24-blog-posts" aria-label="Link to 1. Write 24 blog posts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;raising it from 1-month-a-post to 2. There&amp;rsquo;s something about jotting words down that forces a kind of mental clarity. I&amp;rsquo;m excited about this and already have a bunch of ideas for posts lined up.&lt;/p&gt;
&lt;h2 id="2-make-5-screencasts"&gt;
2. Make 5 screencasts
&lt;a class="heading-anchor" href="#2-make-5-screencasts" aria-label="Link to 2. Make 5 screencasts"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;and finally whip out that copy of ScreenFlow. I &lt;a href="https://www.destroyallsoftware.com/screencasts"&gt;love&lt;/a&gt; watching other &lt;a href="https://medium.com/@suzhinton/my-twitch-live-coding-setup-b2516672fb21"&gt;folks&lt;/a&gt; screencast. I especially like seeing how other developers use vim, Terminal shortcuts, keyboard hacks, different setups etc. I&amp;rsquo;d like to join the party. This one is going to be slightly challenging though given my utter newbness in the area.&lt;/p&gt;
&lt;h2 id="3-give-one-conference-talk"&gt;
3. Give one conference talk
&lt;a class="heading-anchor" href="#3-give-one-conference-talk" aria-label="Link to 3. Give one conference talk"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;(preferably) in a country I&amp;rsquo;ve never been to before and on a new topic. I&amp;rsquo;ve consistently been good at this &lt;a href="https://kau.sh/ppt/"&gt;for the last 5 years&lt;/a&gt; so want to keep it going.&lt;/p&gt;
&lt;h2 id="4-hit-the-gym-atleast-3-times-a-week"&gt;
4. Hit the gym atleast 3 times a week
&lt;a class="heading-anchor" href="#4-hit-the-gym-atleast-3-times-a-week" aria-label="Link to 4. Hit the gym atleast 3 times a week"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;starting Week 7 of this year 👯‍♂️. I think my body is starting to feel like it&amp;rsquo;s catching up with my age. I&amp;rsquo;ve been decently fit for the last 2 years but I want to improve on the consistency, so putting this down here as a commitment.&lt;/p&gt;
&lt;h2 id="5-watch-52-movies"&gt;
5. Watch 52 movies
&lt;a class="heading-anchor" href="#5-watch-52-movies" aria-label="Link to 5. Watch 52 movies"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;and put out some recommendations .&lt;sup id="fnref:4"&gt;&lt;a href="#fn:4" class="footnote-ref" role="doc-noteref"&gt;4&lt;/a&gt;&lt;/sup&gt; Watching movies is how I decompress. If I don&amp;rsquo;t prioritize taking care of my mental health and appropriately decompress, I burn out pretty fast. So this is almost a counter-goal to make sure I&amp;rsquo;m making the time for things that give me happiness.&lt;/p&gt;
&lt;h1 id="random-learnings-from-2019"&gt;
Random learnings from 2019
&lt;a class="heading-anchor" href="#random-learnings-from-2019" aria-label="Link to Random learnings from 2019"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This section is just a random assortment of quotes, links, articles and other things I picked up along the year that I think is worth sharing:&lt;/p&gt;
&lt;h3 id="martin-fowler-quote"&gt;
Martin Fowler quote:
&lt;a class="heading-anchor" href="#martin-fowler-quote" aria-label="Link to Martin Fowler quote:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Any fool can write code that a computer can understand. Good programmers write code that humans can understand.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Martin Fowler, 2008&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id="markdown-comments"&gt;
Markdown comments:
&lt;a class="heading-anchor" href="#markdown-comments" aria-label="Link to Markdown comments:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Apparently &lt;a href="https://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt; has &lt;a href="https://stackoverflow.com/questions/4823468/comments-in-markdown"&gt;comments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[//]: # 👏 if you got here. Shoot me an email citing you saw this comment, and i&amp;rsquo;ll send you movie recommendations!&lt;/p&gt;
&lt;h3 id="anonymous-quote"&gt;
Anonymous quote:
&lt;a class="heading-anchor" href="#anonymous-quote" aria-label="Link to Anonymous quote:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Beware the quiet man. For while others speak, he watched. And while others act, he plans. And when they finally rest… he strikes.&amp;rdquo;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;from the movie &lt;a href="https://www.imdb.com/title/tt6266538/"&gt;Vice&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h3 id="ending-conference-talks"&gt;
Ending conference talks:
&lt;a class="heading-anchor" href="#ending-conference-talks" aria-label="Link to Ending conference talks:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Asking &amp;ldquo;What questions do you have for me?&amp;rdquo; can be dramatically more effective than &amp;ldquo;Any questions?&amp;rdquo; at the end of a talk. &lt;sup id="fnref:5"&gt;&lt;a href="#fn:5" class="footnote-ref" role="doc-noteref"&gt;5&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3 id="optimize-for-trust"&gt;
Optimize for trust:
&lt;a class="heading-anchor" href="#optimize-for-trust" aria-label="Link to Optimize for trust:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://m.signalvnoise.com/6-mistakes-to-avoid-during-your-first-30-days-as-a-new-manager/"&gt;Don&amp;rsquo;t optimize for likability, optimize for trust!&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;🏁 🏁 🏁 Bring it on 2020! 🏁 🏁 🏁&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;In all fairness I did add a wavering question mark indicating this was a stretch goal.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;iOS is far from perfect but I think they fair much better than Android as it stands now. I&amp;rsquo;ve been keeping an eye out on the various breaches and instances of privacy violations and it&amp;rsquo;s become evident that iOS is the better choice when it comes to privacy today. Also, the Pixel 4 didn&amp;rsquo;t exactly leave me pining for a new Android phone, so that decision became very easy.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;especially since &lt;a href="https://disneyland.disney.go.com/destinations/disneyland/star-wars-galaxys-edge/"&gt;Star Wars: Galaxy&amp;rsquo;s Edge&lt;/a&gt; is now finally open.&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;Have you furiously searched Netflix on a Friday night as your significant other patiently waits? I have a lot of friends who do that would like to benefit from my special movie recommending powers. My contribution to this universe, if you will. I even have a specific rating system that&amp;rsquo;s not snooty or artsy-fartsy and is geared towards picking a movie to watch quickly. Coming soon in 2020.&amp;#160;&lt;a href="#fnref:4" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;From a &lt;a href="https://www.npr.org/2019/12/13/788003640/episode-959-things-we-learned-in-2019"&gt;Planey Money episode #959&lt;/a&gt;&amp;#160;&lt;a href="#fnref:5" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/2020/</guid><pubDate>Tue, 04 Feb 2020 07:00:00 GMT</pubDate></item><item><title>
You should CD your blog (Firebase Hosting + Circle CI + Docker)</title><link>https://kau.sh/blog/you-should-cd-your-blog-with-jekyll-docker-circle-ci/</link><description>
&lt;p&gt;This blog now &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; uses &lt;a href="http://jekyllrb.com"&gt;Jekyll&lt;/a&gt; - a static blog generator that takes &lt;a href="https://daringfireball.net/projects/markdown/"&gt;markdown&lt;/a&gt; as an input and pumps html as output. I then copy it over to my hosting server - Firebase, which then happily serves it to the interwebs.&lt;/p&gt;
&lt;p&gt;This setup has worked out swimmingly well thus far and has been rock solid. Even when &lt;a href="../hacking-your-keyboard/"&gt;my last post&lt;/a&gt; got high up the ranks of &lt;a href="https://news.ycombinator.com"&gt;HN&lt;/a&gt;, my blog held steady. Not even the slightest of fears or signs of being fireballed or &lt;a href="https://en.wikipedia.org/wiki/Slashdot_effect"&gt;slashdotted&lt;/a&gt;. This is the beauty of HTML.&lt;/p&gt;
&lt;p&gt;To achieve the above, these are the actual steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;write my blog post in markdown&lt;/li&gt;
&lt;li&gt;git commit and push blog post to git(hub) (this entire blog is version controlled)&lt;/li&gt;
&lt;li&gt;run command &amp;ldquo;bundle exec jekyll build&amp;rdquo; (use jekyll to generate the html)&lt;/li&gt;
&lt;li&gt;run command &amp;ldquo;bundle exec &lt;a href="https://github.com/gjtorikian/html-proofer"&gt;htmlproofer&lt;/a&gt; ./site &amp;ndash;check-favicon &amp;ndash;check-html&amp;rdquo; (on generated html from step 3 to make sure I don&amp;rsquo;t have broken links etc.)&lt;/li&gt;
&lt;li&gt;run command &amp;ldquo;firebase deploy &amp;ndash;only hosting&amp;rdquo;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&amp;rsquo;s 3 steps more than my lazy self is willing to do every single time.&lt;/p&gt;
&lt;p&gt;In software land, we&amp;rsquo;ve solved this problem with CD (&lt;a href="https://en.wikipedia.org/wiki/Continuous_delivery"&gt;Continuous Delivery&lt;/a&gt;). You have a server somewhere on the internet, that keeps checking your github repo every time you&amp;rsquo;ve made a change, then runs a sequence of commands for you.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s exactly what I did for this blog. I now use &lt;a href="https://circleci.com"&gt;Circle CI&lt;/a&gt; &lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; to ping github and run through steps 3-5. If all&amp;rsquo;s well my blog is updated with my new post. If something didn&amp;rsquo;t go well, I get an email notifying me about it which I can then promptly fix.&lt;/p&gt;
&lt;p&gt;Even when I&amp;rsquo;m just tweaking the appearance of this blog, I tend to make &lt;em&gt;a lot&lt;/em&gt; of changes &lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt; to my css (it&amp;rsquo;s like my happy place). Setting up CD has saved me huge amounts of time! If you&amp;rsquo;re a developer, or savvy enough to dabble with some code, I highly recommend it!&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what my circle ci config looks like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-yml" data-lang="yml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;version&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;jobs&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;build&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;working_directory&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;~/repo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# docker hub images https://hub.docker.com/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;docker&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;image&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;kaushikgopal/ruby-node:2.6.5_12.14.0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;steps&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;checkout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Download and cache dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;restore_cache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;keys&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Find a cache corresponding to this specific file&amp;#39;s checksum&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# when any of these files are changed, this key will fail&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;v00-deps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# Find the most recently generated cache used from any branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;v00-deps-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Ruby dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bundle install --path vendor/bundle --verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Node dependencies&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;npm install firebase-tools@7.10.0 --unsafe-perm --verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;save_cache&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# path for directories is relative&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;# to the working_directory of your job&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;paths&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;vendor/bundle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;node_modules&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;key&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;v00-deps&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Build&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bundle exec jekyll build --verbose&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;HTMLProofer tests&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;bundle exec htmlproofer $HOME/repo/_site \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;allow-hash-href \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;check-favicon \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;check-html&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#f92672"&gt;run&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;name&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;Deploy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;command&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;$HOME/repo/node_modules/firebase-tools/lib/bin/firebase.js\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;deploy \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;token=$FIREBASE_TOKEN \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;non-interactive \&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; --&lt;span style="color:#ae81ff"&gt;only hosting&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To get the right version of ruby and node playing well together on the Circle CI server, I even had to create my &lt;a href="https://hub.docker.com/r/kaushikgopal/ruby-node/tags"&gt;custom docker container&lt;/a&gt;. I chatted in great detail about this &lt;a href="https://fragmentedpodcast.com/episodes/188"&gt;on Fragmented episode 188&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;audio style="width:100%" controls src="https://audio.simplecast.com/f5564fa8.mp3"&gt;&lt;/audio&gt;&lt;/p&gt;
&lt;p&gt;Curious to know more about this setup? hit me up on &lt;a href="https://twitter.com/kaushikgopal"&gt;Twitter&lt;/a&gt; and I&amp;rsquo;ll gladly try to help.&lt;/p&gt;
&lt;hr&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;I started out with &lt;a href="https://en.wikipedia.org/wiki/Blogger_%28service%29"&gt;Blogger&lt;/a&gt;, moved to &lt;a href="https://wordpress.com"&gt;Wordpress.com&lt;/a&gt;, then &lt;a href="https://wordpress.org"&gt;Wordpress.org&lt;/a&gt;, then &lt;a href="http://octopress.org"&gt;Octopress&lt;/a&gt; which i deeply loved, and finally now to &lt;a href="http://jekyllrb.com"&gt;Jekyll&lt;/a&gt;.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;They have a generous free tier.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Did you notice the recent switch to &lt;a href="https://www.fontshop.com/families/ff-tisa"&gt;FF Tisa&lt;/a&gt; and &lt;a href="https://www.jetbrains.com/lp/mono/"&gt;Jetbrains Mono&lt;/a&gt; 😍 ?&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/you-should-cd-your-blog-with-jekyll-docker-circle-ci/</guid><pubDate>Wed, 29 Jan 2020 07:00:00 GMT</pubDate></item><item><title>
Hacking your keyboard with karabiner</title><link>https://kau.sh/blog/hacking-your-keyboard/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Update
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;I&amp;rsquo;ve since come up with an easier way to maintain my rules. This post talks of some of the cool things you can do with Karabiner. But if you want a more maintainable way of customizing all this, read &lt;a href="https://kau.sh/blog/karabiner-kt"&gt;my other post&lt;/a&gt; instead.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;tl;dr:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install yqrashawn/goku/goku
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mkdir -p ~/.config &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; cd ~/.config
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;touch karabiner/karabiner.json
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;touch karabiner.edn
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# update your edn file with a sample from here https://github.com/yqrashawn/GokuRakuJoudo/blob/master/tutorial.md&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# or mine: https://gist.github.com/kaushikgopal/ff7a92bbc887e59699c804b59074a126&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;goku
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&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;
&lt;p&gt;I&amp;rsquo;ve been messing with mechanical keyboards recently.&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; There was one in particular which is a 60% layout that I fell in love with.&lt;/p&gt;
&lt;p&gt;The trouble with 60% keyboards though is that they can be pretty constraining for some basic operations (like navigation). Notice there aren&amp;rsquo;t dedicated keys for the up/left/down/arrows.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/keyboard-layouts.webp"
alt="ANSI Keyboard Layout"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://commons.wikimedia.org/wiki/File:ANSI_Keyboard_Layout_Diagram_with_Form_Factor.svg"&gt;
Courtesy: Wikipedia
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;Given these constraints, you have to get a little creative with how you use a keyboard. I added a bunch of mods for the keyboard using the mechanical keyboard&amp;rsquo;s included software configurator. I liked these mods so much that I wanted to find a way to use these mods with &lt;em&gt;any&lt;/em&gt; keyboard I use.&lt;/p&gt;
&lt;p&gt;The easiest way I&amp;rsquo;ve found on the Mac to do this is using &lt;a href="https://karabiner-elements.pqrs.org/"&gt;Karabiner&lt;/a&gt;. Karabiner intercepts every keystroke and allows you to send alternative signals.&lt;/p&gt;
&lt;p&gt;For example, a common mod that a lot of programmers like to do is remap their Caps Lock key -&amp;gt; Escape. So if I tap the Caps Lock key, it instead emulates hitting the Escape key. You can do this pretty easily with Karabiner but it barely scratches the surface of Karabiner&amp;rsquo;s true power.&lt;/p&gt;
&lt;h2 id="complex-modifications"&gt;
Complex modifications:
&lt;a class="heading-anchor" href="#complex-modifications" aria-label="Link to Complex modifications:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Where Karabiner will truly shine is when you want to do slightly more complex things: for example, take the case of navigation with 60% keyboards. While they don&amp;rsquo;t come with dedicated arrow keys you can emulate navigation in a pretty slick way (especiallly for us vim users).&lt;/p&gt;
&lt;p&gt;Instead of moving my fingers all the way to the bottom right of my keyboard and aiming for the arrow keys what if I could just keep my fingers on my home row and navigate like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tap caps lock once -&amp;gt; Escape key
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold caps lock -&amp;gt; left_control
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold caps lock and press h -&amp;gt; left arrow
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold caps lock and press j -&amp;gt; down arrow
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold caps lock and press k -&amp;gt; up arrow
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold caps lock and press l -&amp;gt; right arrow
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can of course choose to map any key combo (for example right shift key + W/A/S/D). I chose H/J/K/L cause I&amp;rsquo;m a vim user and I love just using h/j/k/l to navigate anywhere on my Mac!&lt;/p&gt;
&lt;h2 id="more-complex-modifications"&gt;
More Complex modifications:
&lt;a class="heading-anchor" href="#more-complex-modifications" aria-label="Link to More Complex modifications:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Even that, just scratches the surface of what you can do with Karabiner. Another quick hack I use and love (as a programmer):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tap [ once -&amp;gt; type &amp;#34;[&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold [ and tap ] key once -&amp;gt; type &amp;#34;{&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tap ] once -&amp;gt; type &amp;#34;]&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold ] and tap [ key once -&amp;gt; type &amp;#34;}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re a programmer this makes it easier to type curly braces without contorting your fingers and flaring up that RSI. For typing paranthesis there&amp;rsquo;s an even better one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;left_shift once -&amp;gt; type &amp;#34;(&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold left_shift -&amp;gt; use as a regular shift key modifier
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;right_shift once -&amp;gt; type &amp;#34;)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold right_shift -&amp;gt; used as a regular shift key modifier
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="but-theres-more"&gt;
But there&amp;rsquo;s more!
&lt;a class="heading-anchor" href="#but-theres-more" aria-label="Link to But there’s more!"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Apart from just modifying your key strokes, you can do advanced stuff like launching programs too. For example I typically switch between 3 applications pretty often: &lt;a href="https://iterm2.com/"&gt;iTerm&lt;/a&gt;, &lt;a href="https://www.mozilla.org/en-US/firefox/"&gt;Firefox&lt;/a&gt;, &lt;a href="https://culturedcode.com/things/"&gt;Things&lt;/a&gt;. With Karabiner, I use the same technique but switch programs pretty quickly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold o (for &amp;#34;open&amp;#34;) + tap i -&amp;gt; launch &amp;#34;iTerm&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold o + tap f -&amp;gt; launch &amp;#34;Firefox&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold o + tap t -&amp;gt; launch &amp;#34;Things&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tap o once -&amp;gt; type &amp;#34;o&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This makes switching between apps almost instant. I don&amp;rsquo;t have to carefully Cmd + Tab, visually recognize each icon (adding one extra cognitive step 😁) and then launch it. This might seem frivolous at first but once you start using this quick open mode like functionality, it&amp;rsquo;s hard to go back to Cmd Tabbing.&lt;/p&gt;
&lt;h3 id="turbocharge-mode-karabiner--keyboard-maestro"&gt;
Turbocharge mode (Karabiner + Keyboard Maestro)
&lt;a class="heading-anchor" href="#turbocharge-mode-karabiner--keyboard-maestro" aria-label="Link to Turbocharge mode (Karabiner &amp;#43; Keyboard Maestro)"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;I swear by &lt;a href="http://www.stairways.com/action/kmdiscount?REF6JZA"&gt;Keyboard Maestro&lt;/a&gt; and use a lot of my macros pretty often. With Karabiner, I can map certain keyboard strokes to instead launch macros. For example, if I want to quickly Google something, I hold &amp;ldquo;o&amp;rdquo; then tap &amp;ldquo;g&amp;rdquo; -&amp;gt; this shows an input dialog to take in my text, then launches a new tab in firefox with a url to google prepoulated with my query.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hold &amp;#34;s&amp;#34; + tap g -&amp;gt; google for something on the web
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This kind of usage in conjunction with Keyboard Maestro basically unleashes the power of your Mac.&lt;/p&gt;
&lt;h1 id="how-do-i-do-this"&gt;
How do I do this?
&lt;a class="heading-anchor" href="#how-do-i-do-this" aria-label="Link to How do I do this?"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Before you head to the races and start using Karabiner though there&amp;rsquo;s one caveat:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use &lt;a href="https://github.com/yqrashawn/GokuRakuJoudo#intro"&gt;Goku&lt;/a&gt; to organize your Karabiner&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;See the problem with Karabiner is that if you want to start doing the tricks I mention above, you pretty much have to start meddling directly with this file called &lt;code&gt;karabiner.json&lt;/code&gt;. Let&amp;rsquo;s just say json is not the most maintainable format for this kind of stuff.&lt;/p&gt;
&lt;p&gt;After meddling with Karabiner for a few days, it was clearly getting unwieldy to maintain the json file. So on an exaseperated whim, I went searching for a way to make it easier to deal with Karabiner and found the wonderful &lt;a href="https://github.com/yqrashawn/GokuRakuJoudo"&gt;Goku&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="enter-goku"&gt;
Enter &lt;a href="https://github.com/yqrashawn/GokuRakuJoudo#intro"&gt;Goku&lt;/a&gt;
&lt;a class="heading-anchor" href="#enter-goku" aria-label="Link to Enter Goku"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Goku basically is a DSL where you can write a nice and condensed form of the same code you would otherwise wrangle up in your karabiner.json. You write the code in this special format called &lt;a href="https://github.com/edn-format/edn"&gt;&amp;ldquo;extensible data notation&amp;rdquo; (edn)&lt;/a&gt;. Goku then reads that edn format and generates the karabiner.json file for you.&lt;/p&gt;
&lt;p&gt;For context:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;my &lt;a href="https://gist.github.com/kaushikgopal/c802ca81044066f7d93a50c5269cc812"&gt;&lt;code&gt;karabiner.json&lt;/code&gt;&lt;/a&gt; file currently has about 920 lines&lt;/li&gt;
&lt;li&gt;my &lt;a href="https://gist.github.com/kaushikgopal/ff7a92bbc887e59699c804b59074a126"&gt;&lt;code&gt;karabiner.edn&lt;/code&gt;&lt;/a&gt; file on the other hand has 140 lines (and most of it is liberal commenting and whitespace to combat &lt;code&gt;edn&lt;/code&gt;&amp;rsquo;s terseness)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-edn" data-lang="edn"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#e6db74"&gt;:des&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;o-mode: quick open&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;:rules&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:o-mode&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;:f&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:open&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/Applications/Firefox.app&amp;#34;&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;:g&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:km&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;0) Search Google&amp;#34;&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;:i&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:open&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/Applications/iTerm.app&amp;#34;&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;:q&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:km&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;open: Quip&amp;#34;&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#e6db74"&gt;:t&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:open&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;/Applications/Things3.app&amp;#34;&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#e6db74"&gt;:des&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;right cmd -&amp;gt; right ctrl&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;:rules&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;##&lt;/span&gt;right_command &lt;span style="color:#e6db74"&gt;:right_control&lt;/span&gt; [&lt;span style="color:#e6db74"&gt;:apl-usb-int&lt;/span&gt; &lt;span style="color:#e6db74"&gt;:apl-bt&lt;/span&gt; &lt;span style="color:#e6db74"&gt;:ap2&lt;/span&gt;] ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#e6db74"&gt;:des&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;left shift once -&amp;gt; (&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;:rules&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;##&lt;/span&gt;left_shift &lt;span style="color:#e6db74"&gt;:left_shift&lt;/span&gt; nil {&lt;span style="color:#e6db74"&gt;:alone&lt;/span&gt; &lt;span style="color:#e6db74"&gt;:!S9&lt;/span&gt;} ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&lt;span style="color:#e6db74"&gt;:des&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;right shift once -&amp;gt; )&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#e6db74"&gt;:rules&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&lt;span style="color:#f92672"&gt;##&lt;/span&gt;right_shift &lt;span style="color:#e6db74"&gt;:right_shift&lt;/span&gt; nil {&lt;span style="color:#e6db74"&gt;:alone&lt;/span&gt; &lt;span style="color:#e6db74"&gt;:!S0&lt;/span&gt;} ]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ]}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Speaking as a programmer the edn format is quite possibly the most terse, terrible format I&amp;rsquo;ve encountered. I gave a quick look at &lt;a href="https://github.com/yqrashawn/GokuRakuJoudo/blob/master/examples.org"&gt;some of the examples&lt;/a&gt; and almost abandoned my Goku effort half way.&lt;/p&gt;
&lt;p&gt;But after spending an 11 hour flight back home fiddling and learning how to use Goku, I&amp;rsquo;ve gotten a decent hang of the format and I admit it&amp;rsquo;s &lt;em&gt;super&lt;/em&gt; mantainable.&lt;/p&gt;
&lt;h2 id="how-to-read-and-use-goku"&gt;
How to read and use Goku
&lt;a class="heading-anchor" href="#how-to-read-and-use-goku" aria-label="Link to How to read and use Goku"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;m not going to explain how to use Goku, but I&amp;rsquo;ll tell you what helped me pick it up:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read this &lt;a href="https://medium.com/@nikitavoloboev/karabiner-god-mode-7407a5ddc8f6"&gt;fantastic blog post by Nikita Voloboev&lt;/a&gt; once&lt;/li&gt;
&lt;li&gt;Follow along then with the &lt;a href="https://github.com/yqrashawn/GokuRakuJoudo/blob/master/tutorial.md"&gt;official tutorial&lt;/a&gt; as they explain the basics&lt;/li&gt;
&lt;li&gt;Read the blog post from step 1 again, given you&amp;rsquo;re a little more familiar with the format&lt;/li&gt;
&lt;li&gt;Read my &lt;a href="https://gist.github.com/kaushikgopal/ff7a92bbc887e59699c804b59074a126"&gt;&lt;code&gt;karabiner.edn&lt;/code&gt;&lt;/a&gt; file&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I spent a lot of time commenting &lt;a href="https://gist.github.com/kaushikgopal/ff7a92bbc887e59699c804b59074a126"&gt;my edn file&lt;/a&gt; with the hope of making it more readable, so hope that helps. I plan on making a youtube video at some point demoing all this but until that happens, hope this blog post is helpful.&lt;/p&gt;
&lt;p&gt;Hit me up on &lt;a href="https://twitter.com/kaushikgopal"&gt;Twitter&lt;/a&gt; for queries or comments. Super curious to see other creative hacks people can come up with this technique.&lt;/p&gt;
&lt;h3 id="example-edns"&gt;
Example edns
&lt;a class="heading-anchor" href="#example-edns" aria-label="Link to Example edns"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/yqrashawn/GokuRakuJoudo/blob/master/in-the-wild.md"&gt;goku repo example&amp;rsquo;s in the
wild&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kchen0x/k-goku/blob/master/karabiner.edn"&gt;k-goku&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/nikitavoloboev/dotfiles/blob/178ed88fadaeb889e8a5431e9790f0791dd75b05/karabiner/karabiner.edn#L718"&gt;nikitavoloboev&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/yqrashawn/GokuRakuJoudo/blob/master/examples.org"&gt;examples.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Mechanical keyboards are a costly unhealthy obsession. I switch between an &lt;a href="http://en.obins.net/anne-pro2"&gt;Anne Pro 2&lt;/a&gt; and a &lt;a href="https://mechanicalkeyboards.com/shop/index.php?l=product_list&amp;amp;c=647"&gt;TADA68&lt;/a&gt; these days.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/hacking-your-keyboard/</guid><pubDate>Wed, 25 Dec 2019 07:00:00 GMT</pubDate></item><item><title>
Architecting Android and iOS app features for 2020</title><link>https://kau.sh/ppt/architecting-android-and-ios-app-features-for-2020/</link><description>
&lt;p&gt;Inspired by some well known architecture patterns like MVVM/MVI, I set out to come up with an agnostic set of principles that would help developers build features in their app in a robust, safe and (importantly) &amp;ldquo;testable&amp;rdquo; way. At Instacart, we&amp;rsquo;ve started to use these principles to build features on both iOS and Android.&lt;/p&gt;
&lt;p&gt;In this talk, we&amp;rsquo;ll examine these principles, discuss the merits (+ disadvantages!) and see how these can be implemented with precise code examples. Having implemented this pattern for sometime now at Instacart, I&amp;rsquo;ll also share some of our learnings along the way for both platforms.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/BdGVCsHj2vU?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="8e60b645b4d34a42a0d3d3ba0956102b" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/architecting-android-and-ios-app-features-for-2020/</guid><pubDate>Sat, 26 Oct 2019 00:00:00 GMT</pubDate></item><item><title>
Bob Iger remembers Steve Jobs</title><link>https://kau.sh/blog/bob-iger-remembers-steve/</link><description>
&lt;p&gt;This whole piece is such a biograpy treasure trove and one I&amp;rsquo;m filing under the &amp;ldquo;come back and read in a year&amp;rdquo; category.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m highlighting some of the snippets in particular that really resonated with me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“This is going to allow people to watch video on our iPods, not just listen to music,” he said. “If we bring this product to market, will you put your television shows on it?” I said yes right away.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love how Steve was testing Bob with this decision. Could this still work? Can this eager hungry new CEO walk the talk, with said bold decisions. The part that might not be immediately obvious here is that in this test, Steve too is opening up the kimono about the video iPods here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The ease and the speed with which we got the deal done, combined with the fact that it showed an admiration for Apple and its products, blew Steve’s mind. He told me he’d never met anyone in the entertainment business who was willing to try something that might disrupt his own company’s business model.
Steve responded to boldness. Among his many frustrations was a feeling that it was often too difficult to get anything done with Disney. Every agreement needed to be vetted and analyzed to within an inch of its life, and that’s not how he worked. I wanted him to understand that I didn’t work that way, either, that I was empowered to make a call, and that I was eager to figure out this future together, and to do so quickly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A lesson to always remember for any successfull company. The irony here is that Steve functioned this way. A company the size of Apple (i presume) can&amp;rsquo;t. Yet Steve pulled it off with Apple. An individual &lt;em&gt;can&lt;/em&gt; bring in change.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Okay,” he said. “Well, I’ve got some cons.” He wrote the first with gusto: “Disney’s culture will destroy Pixar!” I couldn’t blame him for that. His experience with Disney so far hadn’t provided any evidence to the contrary. He went on, writing his cons in full sentences across the board. “Fixing Disney Animation will take too long and will burn John and Ed out in the process.” “There’s too much ill will and the healing will take years.” “Wall Street will hate it.” “Your board will never let you do it.” There were many more, but one in all cap letters, “DISTRACTION WILL KILL PIXAR’S CREATIVITY.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Culture, individual contributor burnout, killing Creativity. I love how these were so important to Steve, more so than any other factor. Most companies like to &lt;em&gt;talk&lt;/em&gt; about these things and mostly cause it worked for Apple. But such massive positive change can be brought about in a company when leaders a) understand these qualities and b) hold them up high.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Several members of the board were still opposed, but the moment the Pixar team started talking, everyone in the room was transfixed. They had no notes, no decks, no visual aids. They just talked&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve been interested in public speaking for quite sometime, and I&amp;rsquo;ve always believed this to be a mark of a truly engaging speaker. Another example of this is &lt;a href="https://www.ted.com/talks/ken_robinson_says_schools_kill_creativity/transcript?language=en"&gt;Ken Robinson&amp;rsquo;s talk on schools killing creativity&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Sadly this isn&amp;rsquo;t easy for technical talks but i&amp;rsquo;d love to see more keynote speakers at technical conferences adopte this style of just telling a genuine, honest, engaging story without needing slides to keep listener attention.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;He spent about 10 seconds looking at it, then pushed it aside and said, “Is this one important to you? Do you really want it? Is it another Pixar?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love the trust here. This is an incredibly crazy reality shifting deal here that was made in the end by one genius trusting the instinct of another. This is especially noteworth given Steve &lt;em&gt;hated&lt;/em&gt; comics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;He claimed to have never read a comic book in his life (“I hate them more than I hate video games,” he told me)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sorry Steve, you missed out on a lot here.&lt;/p&gt;
&lt;p&gt;Parting thoughts:&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s so important to have mentors and to constantly be learning from others. Seniority has nothing to do with it. Craftsmen are available everywhere.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been learning iOS development lately and i&amp;rsquo;m a newb. I&amp;rsquo;ve got some experience in the Android development side now and can hold my own. But it&amp;rsquo;s just so refreshing to go into something that you know very little but can learn. I understand mobile development so i have an advantage, but i have to be so careful to not think i know how stuff works already.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m willing to look foolish if it means, i&amp;rsquo;ll learn something. The best piece of advise i&amp;rsquo;ve picked up on.&lt;/p&gt;
&lt;p&gt;Learning is the most important thing. Learning happens at different levels. Conviction is important&lt;/p&gt;
&lt;p&gt;Thanks to the brother who shared this on &lt;a href="https://twitter.com/kageman"&gt;his tweet stream&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/bob-iger-remembers-steve/</guid><pubDate>Mon, 23 Sep 2019 07:00:00 GMT</pubDate></item><item><title>
Speed up your Android Studio</title><link>https://kau.sh/blog/android-studio-slow-as-fxxx/</link><description>
&lt;p&gt;I use a 13&amp;quot; MacBook Pro at work these days. Android Studio frequently sent my machine into a tailspin.&lt;/p&gt;
&lt;p&gt;Over time, I&amp;rsquo;ve had to tweak and update my AS settings to make AS work well on the 13&amp;quot;. I figured I should post them here for posterity and the benefit of other AndroidDevs battling with deathly slow AS experiences.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m posting the abridged instructions and linking to the blog posts that led me to these settings, if you care for the details.&lt;/p&gt;
&lt;h1 id="switch-to-a-local-gradle-distribution"&gt;
Switch to a local gradle distribution
&lt;a class="heading-anchor" href="#switch-to-a-local-gradle-distribution" aria-label="Link to Switch to a local gradle distribution"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s &lt;span style="color:#e6db74"&gt;&amp;#34;https://get.sdkman.io&amp;#34;&lt;/span&gt; | bash &lt;span style="color:#75715e"&gt;# install sdkman&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install your preferred version of gradle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install gradle 5.4.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk default gradle 5.4.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gradle --status
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Once you have a local distribution of gradle installed, switch the &amp;ldquo;Gradle home:&amp;rdquo; setting in your Android Studio project to point to your local gradle distribution. You&amp;rsquo;ll have to do these for all your projects individually.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/as-local-gradle.png" alt="Android Studio &amp;gt; Preferences &amp;gt; Build, Execution, Deployment &amp;gt; Gradle"&gt;&lt;/p&gt;
&lt;p&gt;With this setup, when you run gradle on the command line or Android Studio, they&amp;rsquo;ll share the same gradle daemon instances.&lt;/p&gt;
&lt;h2 id="--also-make-sure-the-daemons-from-as-and-your-command-line-are-compatible"&gt;
☝️ also make sure the daemons from AS and your command line are compatible
&lt;a class="heading-anchor" href="#--also-make-sure-the-daemons-from-as-and-your-command-line-are-compatible" aria-label="Link to ☝️ also make sure the daemons from AS and your command line are compatible"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Well almost. You also have to make sure you&amp;rsquo;re using the same JDK otherwise the gradle daemons will be incompatible.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# you have sdkman installed already&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install java 8.0.202-zulu &lt;span style="color:#75715e"&gt;# use java 8 preferably&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk default java 8.0.202-zulu
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install kotlin &lt;span style="color:#75715e"&gt;# cause why not&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now point Android Studio to use this jdk like so:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/as-same-jdk.png" alt="Android Studio Project Settings &amp;gt; SDK Location"&gt;&lt;/p&gt;
&lt;p&gt;💥 now we&amp;rsquo;re in business.&lt;/p&gt;
&lt;h1 id="disable-any-useless-plugins-or-add-ons-that-you-dont-need"&gt;
Disable any useless plugins or add-ons that you don&amp;rsquo;t need
&lt;a class="heading-anchor" href="#disable-any-useless-plugins-or-add-ons-that-you-dont-need" aria-label="Link to Disable any useless plugins or add-ons that you don’t need"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/as-plugins-disabled.png" alt="Preference &amp;gt; Plugins &amp;gt; Installed"&gt;&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get carried away and disable &lt;em&gt;everything&lt;/em&gt; here. It will hose your Android Studio. I know this cause I sure as hell tried.&lt;/p&gt;
&lt;p&gt;The only plugins I have enabled are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;(Downloaded) IdeaVim&lt;/li&gt;
&lt;li&gt;Android APK Suport&lt;/li&gt;
&lt;li&gt;Android NDK Support&lt;/li&gt;
&lt;li&gt;Git Integration (cause i like Intellij&amp;rsquo;s Differ)&lt;/li&gt;
&lt;li&gt;Gradle&lt;/li&gt;
&lt;li&gt;Groovy&lt;/li&gt;
&lt;li&gt;Il8n for Java&lt;/li&gt;
&lt;li&gt;Intellij Configuration Script&lt;/li&gt;
&lt;li&gt;IntelliLang&lt;/li&gt;
&lt;li&gt;Java Bytecode Decompiler&lt;/li&gt;
&lt;li&gt;JUnit&lt;/li&gt;
&lt;li&gt;Kotlin&lt;/li&gt;
&lt;li&gt;Properties Support&lt;/li&gt;
&lt;li&gt;Smali Support&lt;/li&gt;
&lt;li&gt;YAML&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everything else is disabled. &lt;a href="https://www.reddit.com/r/androiddev/comments/7sxhig/android_studio_slower_when_using_kotlin/dt88pgn/"&gt;This crony from Bob&amp;rsquo;s Discount Action Bars&lt;/a&gt; apparently knows a thing or two about Android.&lt;/p&gt;
&lt;h1 id="tweak-your-studiovmoptions-file"&gt;
Tweak your &lt;code&gt;studio.vmoptions&lt;/code&gt; file
&lt;a class="heading-anchor" href="#tweak-your-studiovmoptions-file" aria-label="Link to Tweak your studio.vmoptions file"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;The official &lt;a href="https://developer.android.com/studio/intro/studio-config.html"&gt;developer docs&lt;/a&gt; talk about configuring this properly.&lt;/p&gt;
&lt;p&gt;tl;dr: in Android Studio head to &lt;em&gt;&amp;ldquo;Help &amp;gt; Edit Custom VM Options&amp;hellip;&amp;rdquo;&lt;/em&gt; and change it to the below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#-Xms256m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#-Xmx2048m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:ReservedCodeCacheSize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;240m
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:+UseCompressedOops
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Dfile.encoding&lt;span style="color:#f92672"&gt;=&lt;/span&gt;UTF-8
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:+UseConcMarkSweepGC
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:SoftRefLRUPolicyMSPerMB&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;50&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Dsun.io.useCanonCaches&lt;span style="color:#f92672"&gt;=&lt;/span&gt;false
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Djava.net.preferIPv4Stack&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Djdk.http.auth.tunneling.disabledSchemes&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Djna.nosys&lt;span style="color:#f92672"&gt;=&lt;/span&gt;true
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Djna.boot.library.path&lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-da
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Xverify:none
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:ErrorFile&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$USER_HOME/java_error_in_studio_%p.log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:HeapDumpPath&lt;span style="color:#f92672"&gt;=&lt;/span&gt;$USER_HOME/java_error_in_studio.hprof
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ###############################################&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# custom settings from https://github.com/artem-zinnatullin/AndroidStudio-VM-Options/blob/master/studio.vmoptions&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# ###############################################&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Runs JVM in Server mode with more optimizations and resources usage&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# It may slow down the startup, but if you usually keep IDE running for few hours/days&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# JVM may profile and optimize IDE better. Feel free to remove this flag.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-server
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Sets the initial size of the heap, default value is 256m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Xms1G
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Max size of the memory allocation pool, default value is 1280m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-Xmx2G
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# Sets the size of the allocated class metadata space that will trigger a GC the first time it is exceeded, default max value is 350m&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-XX:MetaspaceSize&lt;span style="color:#f92672"&gt;=&lt;/span&gt;512m
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Anything else you&amp;rsquo;ve come across that&amp;rsquo;s helped a lot? &lt;a href="https://twitter.com/kaushikgopal/status/1163684028651732995"&gt;Hit me up on Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="updates-since-my-post-was-originally-published"&gt;
Updates since my post was originally published:
&lt;a class="heading-anchor" href="#updates-since-my-post-was-originally-published" aria-label="Link to Updates since my post was originally published:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/TheSNAKY/status/1163703431237918720?s=20"&gt;@TheSNAKY&lt;/a&gt; pointed me to an easier way to get to the Edit VM options.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/matthewcmckenna/status/1163834088509386754?s=20"&gt;@matthewcmckenna&lt;/a&gt; reminded me that using sdk man, using the &lt;code&gt;/current&lt;/code&gt; path is better as you upgrade versions. I&amp;rsquo;ve changed the commands here to mark the default installation and then just point to that within the Android Studio screenshots&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="useful-resources-and-reference-links"&gt;
Useful resources and reference links:
&lt;a class="heading-anchor" href="#useful-resources-and-reference-links" aria-label="Link to Useful resources and reference links:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@mydogtom/tip-reduce-the-number-of-gradle-daemon-instances-by-using-local-distribution-56f645cf2c97"&gt;Tip: Use local Gradle distribution instead of wrapper&lt;/a&gt; and &lt;a href="https://medium.com/@mydogtom/tip-how-to-reuse-gradle-daemon-between-android-studio-and-terminal-df5232d63f38"&gt;Tip: How to reuse Gradle daemon between Android Studio and terminal&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@championswimmer/bringing-android-app-build-times-down-by-95-at-zomato-a505d938e9b8"&gt;Bringing Android app build times down by 95% at Zomato&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/android-studio-slow-as-fxxx/</guid><pubDate>Mon, 19 Aug 2019 07:00:00 GMT</pubDate></item><item><title>
assertLastValue for RxJava TestObservers</title><link>https://kau.sh/blog/rxjava-test-observer-assert-last-value/</link><description>
&lt;p&gt;The &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/observers/TestObserver.html"&gt;TestObserver&lt;/a&gt; is an RxJava staple for testing.&lt;/p&gt;
&lt;p&gt;It allows you to assert values in a stream, in the specific order they were emitted. Here&amp;rsquo;s a quick code snippet from the &lt;a href="https://github.com/kaushikgopal/movies-usf"&gt;movies-usf repository&lt;/a&gt; &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onSearchingForMovieBladeRunner&lt;/span&gt;_shouldSeeSearchResult() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewModel = MSMainVm(mockApp, mockMovieRepo)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; viewStateTester = viewModel.viewState.test()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewModel.processInput(SearchMovieEvent(&lt;span style="color:#e6db74"&gt;&amp;#34;blade runner 2049&amp;#34;&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewStateTester.assertValueAt(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(&lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;.searchedMovieTitle).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;Searching Movie...&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewStateTester.assertValueAt(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertThat(&lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;.searchedMovieTitle).isEqualTo(&lt;span style="color:#e6db74"&gt;&amp;#34;Blade Runner 2049&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you look at &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/observers/BaseTestConsumer.html"&gt;the source for the base TestObserver&lt;/a&gt;, there are a bunch of these useful methods:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValue(Predicate&lt;T&gt; valuePredicate):
Asserts that this TestObserver/TestSubscriber received exactly one onNext value for which the provided predicate returns true.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValue(T value):
Assert that this TestObserver/TestSubscriber received exactly one onNext value which is equal to the given value with respect to Objects.equals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValueAt(int index, Predicate&lt;T&gt; valuePredicate):
Asserts that this TestObserver/TestSubscriber received an onNext value at the given index for the provided predicate returns true.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValueAt(int index, T value)
Asserts that this TestObserver/TestSubscriber received an onNext value at the given index which is equal to the given value with respect to null-safe Object.equals.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValues(T&amp;hellip; values)
Assert that the TestObserver/TestSubscriber received only the specified values in the specified order.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;assertValuesOnly(T&amp;hellip; values)
Assert that the TestObserver/TestSubscriber received only the specified values in the specified order without terminating.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;While all these operators are useful, most of them require you to know the precise index of the value you&amp;rsquo;re testing. Typically though when i&amp;rsquo;m testing a feature built with a USF architecture, I really only care about the last value emitted in my test case.&lt;/p&gt;
&lt;p&gt;When working with a &lt;a href="https://github.com/kaushikgopal/movies-usf/blob/kg/feat/checklist-zone-demo/app/src/test/java/co/kaush/msusf/genres/DemoGenreVMTest.kt"&gt;more recent example&lt;/a&gt; in my demo app: I had a checklist that I would toggle multiple times in different permutations and combinations. Depending on the final state of the checklist, a save button would be enabled/disabled. So my test really cares only about the very last value viz. the state of the save button. Knowing the precise index value of the final result started to get tiring.&lt;/p&gt;
&lt;p&gt;An argument could also be made that for this test, testing with the precise index value actually makes it a little brittle. Say I introduced an additional loading state (for some reason), I would have to go back and change all my tests to update the precise index number. &lt;em&gt;I fully understand this is tricky territory cause some would argue you would also want to test the intermediate loading states&lt;/em&gt;. All I&amp;rsquo;m saying is that for these specific tests, I didn&amp;rsquo;t care about the intermediate loading state, just the final output.&lt;/p&gt;
&lt;p&gt;So if we ran with that idea, how do you make the test less brittle?&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vsTester.assertValueAt(vsTester.valueCount() - &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// predicate condition tested
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As simple as that, just test the last value using &lt;code&gt;valueCount&lt;/code&gt;. But if you&amp;rsquo;re in Kotlin land, you&amp;rsquo;re spoilt with all those tasty elegant apis, so you definitely want to make this easier by adding an extension function like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &amp;lt;&lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;&amp;gt; &lt;span style="color:#a6e22e"&gt;TestObserver&lt;/span&gt;&amp;lt;T&amp;gt;.assertLastValue(predicate: (T) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; Boolean) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; assertValueAt(valueCount() - &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) { predicate.invoke(&lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;💥&lt;/p&gt;
&lt;p&gt;Now these tests read much more fluidly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;vsTester.assertLastValue {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// predicate condition tested
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Take a look at the &lt;a href="https://github.com/kaushikgopal/movies-usf/blob/kg/feat/checklist-zone-demo/app/src/test/java/co/kaush/msusf/genres/DemoGenreVMTest.kt"&gt;DemoGenreVmTest&lt;/a&gt; class if you&amp;rsquo;re interested to see a bunch of these in full action.&lt;/p&gt;
&lt;p&gt;Remember the ABC of programming - Always Be CTesting&amp;hellip;&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;movies-usf is a simple open source Android demo app where I demonstrate how one can build features using a &lt;a href="https://speakerdeck.com/kaushikgopal/unidirectional-state-flow-patterns-a-refactoring-story"&gt;Unidirectional State Flow (MVI)&lt;/a&gt; pattern&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/rxjava-test-observer-assert-last-value/</guid><pubDate>Sun, 18 Aug 2019 07:00:00 GMT</pubDate></item><item><title>
Is iOS starting to get fragmented?</title><link>https://kau.sh/blog/ios-might-be-fragmented/</link><description>
&lt;p&gt;I&amp;rsquo;ve been dipping my toes into some iOS development recently. Nothing too crazy, just pairing with some colleagues and trying to see how we can jointly improve the technical design on both platforms .&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;It so happens that Apple just finished it&amp;rsquo;s annual conference &lt;a href="https://developer.apple.com/wwdc/"&gt;WWDC&lt;/a&gt;, so I&amp;rsquo;ve been following the announcements closer this timer around. In that process, I ran across a tweet (from &lt;a href="https://twitter.com/jnadeau"&gt;Jeff Nadeau&lt;/a&gt; who&amp;rsquo;s a developer working at Apple):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sort of blown away by the take that a min OS target is harmful to a new framework. It’s absurdly small-minded; one or two years of adoption lag is nothing when the arc of such a system is measured in decades.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To set a little more context: the system Jeff talks about here is &lt;a href="https://developer.apple.com/xcode/swiftui/"&gt;SwiftUi&lt;/a&gt;, a new framework library that will truly change the way iOS developers write their UI code. The niggle here is that SwiftUI is limited to just adopters of iOS 13.&lt;/p&gt;
&lt;p&gt;This basically means:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since the adoption rate for new OS updates is about 2 years for iOS, iOS 13 (newest version) will take 2 years to roll out to most iPhones&lt;/li&gt;
&lt;li&gt;iPhones 6 and below will never be able to run apps built with SwiftUi (as iOS 13 will run on iPhone 6S and later only)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I found this bizarrely familiar since this was the exact same problem Google had with Android&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; for the longest time. It was frustrating for Android developers because we would come back all excited from &lt;a href="https://events.google.com/io/"&gt;Google I/O&lt;/a&gt;, only to have the winds promptly taken out of our sails after we reviewed the horrendous state of &lt;a href="https://developer.android.com/about/dashboards"&gt;OS adoption&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It would be pretty irresponsible to start using SwiftUi right now for app developers building apps today.&lt;/p&gt;
&lt;p&gt;I do want to make it clear: this is not what Jeff is saying. He&amp;rsquo;s not suggesting everyone start using SwiftUi right now. The latter part of the tweet clarifies this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; when the arc of such a system is measured in decades&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2 years might just be the cost to pay for iOS developers to have an amazing framework like SwiftUi that will eventually make development cycles 4x better. Especially since a sea of change is measured in longer cycles (10 years?).&lt;/p&gt;
&lt;p&gt;That being said, this is painful to hear for app developers today. 2 years is excruciatingly painful. Trust me, us Android developers have been there &lt;a href="https://twitter.com/minsdkversion"&gt;multiple times&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But this fragmentation problem is something that the Android team has experienced and countered. From the &lt;a href="https://android.googlesource.com/platform/frameworks/support/&amp;#43;/androidx-master-dev/README.md"&gt;Android Jetpack&lt;/a&gt; docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jetpack comprises the androidx.* package libraries, &lt;strong&gt;unbundled from the platform APIs&lt;/strong&gt;. This means that it offers backward compatibility and is updated more frequently than the Android platform, making sure you always have access to the latest and greatest versions of the Jetpack components.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This means that when &lt;a href="https://developer.android.com/jetpack/compose"&gt;Jetpack Compose&lt;/a&gt; (Android&amp;rsquo;s SwiftUi) comes out, developers can start using it right away. They won&amp;rsquo;t have to wait for their users to adopt the newest OS update.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m curious if the existing iOS infrastructure didn&amp;rsquo;t allow such a mechanism or it was just a conscious decision to take SwiftUi forward faster, without being held back by the burdern of backwards compatibility.&lt;/p&gt;
&lt;p&gt;Closing remarks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Even though Jetpack Compose was announced earlier, SwiftUi looks to be in far better shape as of today. Just &lt;a href="https://developer.apple.com/tutorials/swiftui/tutorials"&gt;look at the examples and documentation&lt;/a&gt; (&lt;em&gt;also @&lt;a href="https://twitter.com/RunChristinaRun/status/1138987880674521088"&gt;RunChristinaRun&lt;/a&gt; says so&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;SwiftUi will works on iOS, MacOS, iPadOS, watchOS and tvOS. That&amp;rsquo;s incredible. Google hasn&amp;rsquo;t confirmed yet if JetPack Compose will work on more platforms, but the way it&amp;rsquo;s being built, it sounds like it &lt;em&gt;might be possible&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;If you know me, you&amp;rsquo;ll know why this title is kind of funny&lt;/li&gt;
&lt;li&gt;See &lt;a href="https://mjtsai.com/blog/2019/06/10/supporting-the-iphone-se-resolution/"&gt;supporting the iPhone SE resolution&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;em&gt;I also diligently throw shade every time I see them struggle with Xcode trying to autocomplete something. It&amp;rsquo;s almost laughable how far ahead Android studio is.&lt;/em&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;Granted the case of OS fragmentation on Android is exacerbated by the fact that OEMs control when the device gets an OS update (if at all).&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/ios-might-be-fragmented/</guid><pubDate>Thu, 13 Jun 2019 07:00:00 GMT</pubDate></item><item><title>
Processing a sorted array is faster than an unsorted one?</title><link>https://kau.sh/blog/processing-sorted-array-is-faster-than-processing-unsorted-array/</link><description>
&lt;p&gt;This super intesting &lt;a href="https://stackoverflow.com/questions/11227809/why-is-processing-a-sorted-array-faster-than-processing-an-unsorted-array"&gt;stack overflow answer&lt;/a&gt; explains why -in programming- if you have a sorted array, somehow magically it can seem like it&amp;rsquo;s easier to process each element vs processing the same array if it were unsorted.&lt;/p&gt;
&lt;p&gt;tl;dr - branch prediction&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With a sorted array, the condition data[c] &amp;gt;= 128 is first false for a streak of values, then becomes true for all later values. That&amp;rsquo;s easy to predict. With an unsorted array, you pay for the branching cost.&lt;/p&gt;
&lt;/blockquote&gt;</description><guid>https://kau.sh/blog/processing-sorted-array-is-faster-than-processing-unsorted-array/</guid><pubDate>Sat, 08 Jun 2019 07:00:00 GMT</pubDate></item><item><title>
Space Shuttle style programming</title><link>https://kau.sh/blog/space-shuttle-style-programming/</link><description>
&lt;p&gt;Sometime back I ran across a thread where folks talked about this programming style called &amp;ldquo;Space Shuttle style&amp;rdquo; that the Kubernetes codebase followed.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ==================================================================
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// KEEP THE SPACE SHUTTLE FLYING.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ==================================================================
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// This controller is intentionally written in a very verbose style. You will
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// notice:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 1. Every &amp;#39;if&amp;#39; statement has a matching &amp;#39;else&amp;#39; (exception: simple error
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// checks for a client API call)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 2. Things that may seem obvious are commented explicitly
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// We call this style &amp;#39;space shuttle style&amp;#39;. Space shuttle style is meant to
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ensure that every branch and condition is considered and accounted for -
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// the same way code is written at NASA for applications like the space
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// shuttle.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Originally, the work of this controller was split amongst three
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// controllers. This controller is the result a large effort to simplify the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// PV subsystem. During that effort, it became clear that we needed to ensure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// that every single condition was handled and accounted for in the code, even
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// if it resulted in no-op code branches.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// As a result, the controller code may seem overly verbose, commented, and
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// &amp;#39;branchy&amp;#39;. However, a large amount of business knowledge and context is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// recorded here in order to ensure that future maintainers can correctly
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// reason through the complexities of the binding behavior. For that reason,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// changes to this file should preserve and add to the space shuttle style.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ==================================================================
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// PLEASE DO NOT ATTEMPT TO SIMPLIFY THIS CODE.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// KEEP THE SPACE SHUTTLE FLYING.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ==================================================================
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To be clear: I don&amp;rsquo;t follow the Space Shuttle style programming with my own code. However, I&amp;rsquo;ve noticed now that when I do comment my code, I&amp;rsquo;m a little more liberal with my explanations. It&amp;rsquo;s made me realize that commenting your code doesn&amp;rsquo;t necessarily always mean bastardizing it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Originally, the work of this controller was split amongst three controllers. This controller is the result a large effort to simplify the PV subsystem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;However, a large amount of business knowledge and context is recorded here in order to ensure that future maintainers can correctly reason through the complexities of the binding behavior.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is how I feel sometimes when people abuse abstraction. Yes, composition is important but long files are sometimes ok if it makes the code easier to comprehend by preventing that extra cognitive jump (required to first understand the name of the newly abstracted component).&lt;/p&gt;
&lt;p&gt;From the &lt;a href="https://github.com/kubernetes/kubernetes/blob/ec2e767e59395376fa191d7c56a74f53936b7653/pkg/controller/volume/persistentvolume/pv_controller.go"&gt;Kubernetes source code&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/space-shuttle-style-programming/</guid><pubDate>Wed, 05 Jun 2019 07:00:00 GMT</pubDate></item><item><title>
IO vs CPU operations</title><link>https://kau.sh/blog/io-cpu-bound-threads/</link><description>
&lt;p&gt;This is a fantastic post by Erik where he explains the nuance between &lt;a href="https://en.wikipedia.org/wiki/I/O_bound"&gt;IO-bound&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/CPU_bound"&gt;CPU-bound&lt;/a&gt; operations in programming.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; libraries have dedicated APIs for I/O scheduling work, separate from other types of operations&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;. but why is this the case? Why don’t we use a single thread pool for all background operations? The operating system will handle the scheduling of these threads the same&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I love how this specific question is framed (a good interview question for advanced mobile developers):&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="faq" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-help-circle"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"&gt;&lt;/path&gt;&lt;path d="M12 17h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
Why can&amp;rsquo;t we just split work into:
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ol&gt;
&lt;li&gt;main (or UI) thread and&lt;/li&gt;
&lt;li&gt;&amp;ldquo;all other background work&amp;rdquo; thread pool?&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This way the UI shouldn&amp;rsquo;t stutter and if you have a pool of threads running in the background, all that background work will conveniently be executed, no problem. Right?&lt;/p&gt;
&lt;p&gt;Even though both I/O-bound and CPU-bound work need to be executed in the background for mobile development, they have different demands and stress the operating system (OS) differently. Hence the need for different thread pool types to handle this kind of work differently.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="tldr" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-clipboard-list"&gt;&lt;rect x="8" y="2" width="8" height="4" rx="1" ry="1"&gt;&lt;/rect&gt;&lt;path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"&gt;&lt;/path&gt;&lt;path d="M12 11h4"&gt;&lt;/path&gt;&lt;path d="M12 16h4"&gt;&lt;/path&gt;&lt;path d="M8 11h.01"&gt;&lt;/path&gt;&lt;path d="M8 16h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;ul&gt;
&lt;li&gt;CPU operations (like number crunching, computation etc.) run incredibly fast&lt;/li&gt;
&lt;li&gt;I/O operations on the other hand (reading from a database, network etc.) take longer as the OS has to wait for underlying hardware to retrieve and deliver the data&lt;/li&gt;
&lt;li&gt;Typically the underlying hardware sends out a signal &lt;code&gt;IOWait&lt;/code&gt; to the OS while the underlying hardware is out and about doing the IO work&lt;/li&gt;
&lt;li&gt;Instead of sitting and twiddling its thumbs, the OS should be finishing other tasks in the meantime (especially ones that are not waiting on underlying hardware) through other threads&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;When the operating system encounters an IOwait, it pauses the execution of that thread and is free to pick up another thread that is ready to run. Theoretically, an operating system can have an unlimited number of threads that are sleeping due to IOwait, without the user noticing any difference&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That last point is subtle: this is why &lt;em&gt;you don&amp;rsquo;t want to mix CPU and I/O threads in the same pool&lt;/em&gt;. You could potentially have a whole bunch of them &lt;code&gt;IOWait&lt;/code&gt;ing and precious CPU work that could otherwise be done in the meantime, will also hang in the process waiting for that I/O work. Armed with this knowledge, if you&amp;rsquo;re into Rx you might ask:&lt;/p&gt;
&lt;p&gt;How many maximum threads can the &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/schedulers/Schedulers.html#io--"&gt;IO Scheduler&lt;/a&gt; have?&lt;/p&gt;
&lt;p&gt;Unbounded! This should also makes sense now since we don&amp;rsquo;t want one I/O thread ever waiting on another since &lt;code&gt;IOWait&lt;/code&gt;ing can take quite some time. Note that this is still a &amp;ldquo;pool&amp;rdquo; so threads are recycled but if all them are currently doing some work, Rx will spawn a new one everytime.&lt;/p&gt;
&lt;p&gt;How many maximum threads can the &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/schedulers/Schedulers.html#computation--"&gt;Computation Scheduler&lt;/a&gt; spawn?&lt;/p&gt;
&lt;p&gt;Basically, the number of processors your CPU has. My Pixel for example has a quad core processor, so that&amp;rsquo;s 4. This should also make sense now because CPU work should run blazingly fast, you probably don&amp;rsquo;t want an unbounded pool but some kind of optimum upper limit. That upper limit is the number of cores you have handy.&lt;/p&gt;
&lt;p&gt;Erik&amp;rsquo;s a really smart guy. You should &lt;a href="https://twitter.com/ErikHellman"&gt;follow him&lt;/a&gt; on Twitter.&lt;/p&gt;</description><guid>https://kau.sh/blog/io-cpu-bound-threads/</guid><pubDate>Mon, 03 Jun 2019 07:00:00 GMT</pubDate></item><item><title>
.hide() your Subjects - RxJava tip</title><link>https://kau.sh/blog/rx-tip-hide-your-subjects/</link><description>
&lt;p&gt;A not so well known api in RxJava is the &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#hide--"&gt;&lt;code&gt;.hide()&lt;/code&gt;&lt;/a&gt; operator.&lt;/p&gt;
&lt;h2 id="when-does-one-use-the-hide-operator-in-rx"&gt;
When does one use the hide operator in Rx?
&lt;a class="heading-anchor" href="#when-does-one-use-the-hide-operator-in-rx" aria-label="Link to When does one use the hide operator in Rx?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;From the docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hides the identity of this Observable and its Disposable.&lt;/p&gt;
&lt;p&gt;Allows hiding extra features such as Subject&amp;rsquo;s Observer methods or preventing certain identity-based optimizations (fusion). there are a lot of complex operations that take place internally in RxJava (like internal queue creation, worker instantiation + release, numerous atomic variables being created and modified.)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If this doesn&amp;rsquo;t make too much sense, let&amp;rsquo;s look at examples to make this clear. Consider a typical MVVM usecase:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyAndroidVm&lt;/span&gt; : ViewModel() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; outputSubject = &lt;span style="color:#a6e22e"&gt;BehaviorSubject&lt;/span&gt;.createOnDefault(initialViewState())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;init&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Observable&lt;/span&gt;.just(service.pollModelData())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .map {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ... do something with the Event that pipes a result
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; &lt;span style="color:#75715e"&gt;// or view state eventually
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .subscribe { viewState &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; outputSubject.onNext(viewState)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;listenToViewState&lt;/span&gt;(): BehaviorSubject&amp;lt;ViewState&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; outputSubject
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this sample code, the ViewState is basically what the Activity would consume. So within the ViewModel, we pipe the data into a Subject, which is then exposed to the Activity like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyAndroidActivity&lt;/span&gt;: Activity {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onResume&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewModel.listenToViewState()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .observeOn(&lt;span style="color:#a6e22e"&gt;AndroidSchedulers&lt;/span&gt;.mainThread())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .subscribe { viewState &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// bind Android Views and ViewState object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is all great, however the trouble with exposing a Subject is that it allows the activity to modify the internal state of the Behavior Subject from the outside. This is non-ideal as we only want the ViewModel controlling this.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MyAndroidActivity&lt;/span&gt;: Activity {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;override&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onResume&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; viewModel.listenToViewState()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .onNext(badViewState()) &lt;span style="color:#75715e"&gt;// we want to avoid this
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; .observeOn(&lt;span style="color:#a6e22e"&gt;AndroidSchedulers&lt;/span&gt;.mainThread())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .subscribe { viewState &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// bind Android Views and ViewState object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So we don&amp;rsquo;t want to expose the Subject directly. It would make more sense instead to just expose an Observable.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is where the &lt;code&gt;.hide()&lt;/code&gt; operator comes in handy. It basically converts a Subject into an Observable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// inside the ViewModel
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;listenToViewState&lt;/span&gt;(): Observable&amp;lt;ViewState&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; outputSubject.hide()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// this returns an Observable (instead of a Subject)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But there&amp;rsquo;s actually more to the hide operator. It&amp;rsquo;s not as simple as merely converting a Subject -&amp;gt; Observable. If i merely wanted to just convert the Subject to an Observable, I could have just used the cast operator:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;outputSubject.cast(ViewState&lt;span style="color:#f92672"&gt;::&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt;.java) &lt;span style="color:#75715e"&gt;// this returns an Observable
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So you may ask when should one use &lt;code&gt;.hide&lt;/code&gt; vs just a &lt;code&gt;.cast&lt;/code&gt;&lt;/p&gt;
&lt;h2 id="operator-fusion-and-performance"&gt;
Operator fusion and performance
&lt;a class="heading-anchor" href="#operator-fusion-and-performance" aria-label="Link to Operator fusion and performance"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s revisit the documentation for &lt;code&gt;.hide&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Allows hiding extra features such as Subject&amp;rsquo;s Observer methods or preventing certain identity-based optimizations (fusion).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is where things get interesting. We&amp;rsquo;ve already seen the &amp;ldquo;hiding extra features such as Subject&amp;rsquo;s Observer methods&amp;rdquo; bit through our example.&lt;/p&gt;
&lt;p&gt;But apparently, there are also some identity-based optimizations that are avoided. I wasn&amp;rsquo;t aware of what these optimizations were but RxJava savante and maintainer David Karnok has written a couple of &lt;a href="http://akarnokd.blogspot.com/2016/03/operator-fusion-part-1.html"&gt;great&lt;/a&gt; &lt;a href="http://akarnokd.blogspot.com/2016/04/operator-fusion-part-2-final.html"&gt;posts&lt;/a&gt; on operator fusion.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to regurgitate his content here but the tl;dr version is - for all the thread hopping and asynchronous magic that RxJava enables, there are some internal costs to pay (obviously). When you combine a sequence of operators (which you&amp;rsquo;re bound to do for any Rx call), there are times where we incur an overhead.&lt;/p&gt;
&lt;p&gt;Operator fusion is the idea that we can fuse or combine some of the internal operations to help reduce some of that overhead. If you want a more detailed (and accurate) description of the details, head on over to &lt;a href="http://akarnokd.blogspot.com/2016/03/operator-fusion-part-1.html"&gt;David&amp;rsquo;s blog&lt;/a&gt; for some of those details.&lt;/p&gt;
&lt;p&gt;So if you don&amp;rsquo;t need some of these additional optimizations, use the &lt;a href="http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#hide--"&gt;&lt;code&gt;.hide()&lt;/code&gt;&lt;/a&gt; operator.&lt;/p&gt;
&lt;p&gt;I also love looking at the &lt;a href="https://github.com/ReactiveX/RxJava/blob/2.x/src/main/java/io/reactivex/Observable.java#L9459"&gt;source code&lt;/a&gt;, so i did just that for &lt;code&gt;.hide&lt;/code&gt; and noticed it simply wraps and sends the observable with &lt;a href="https://github.com/ReactiveX/RxJava/blob/579e90dc21937b900877a8baf3918cdca22d3a91/src/main/java/io/reactivex/internal/operators/observable/ObservableHide.java"&gt;&lt;code&gt;ObservableHide&lt;/code&gt;&lt;/a&gt;. This proces of re-creating a wrapped version of the &lt;code&gt;ObservableHide&lt;/code&gt; pares down a lot of the operator functionality. Reading the source also made me realize that this would work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;outputSubject.hide().hide().hide().hide().hide()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;But I wouldn&amp;rsquo;t be worrying about performance and functionality at that point.&lt;/p&gt;</description><guid>https://kau.sh/blog/rx-tip-hide-your-subjects/</guid><pubDate>Sat, 30 Mar 2019 07:00:00 GMT</pubDate></item><item><title>
Japanese Manhole art</title><link>https://kau.sh/blog/japanese-manhole-art/</link><description>
&lt;p&gt;The Japanese can chalk this up to their creative laurels along with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the &lt;a href="https://www.youtube.com/watch?v=35zY-UnrY6s"&gt;Fujifilm X100S&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;the &lt;a href="https://en.wikipedia.org/wiki/Walkman"&gt;Sony walkmans&lt;/a&gt; and&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=HBNqOHX57Qg"&gt;one of my favorite Anime&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="full-bleed"&gt;
&lt;img src="https://assets.atlasobscura.com/media/W1siZiIsInVwbG9hZHMvYXNzZXRzLzllMTAyMGM4NWI1YmEyNWM3OF9HZXR0eUltYWdlcy05MDQ2NDU1OTQuanBnIl0sWyJwIiwiY29udmVydCIsIiJdLFsicCIsImNvbnZlcnQiLCItcXVhbGl0eSA4MSAtYXV0by1vcmllbnQiXSxbInAiLCJ0aHVtYiIsIjEyODB4PiJdXQ/GettyImages-904645594.jpg"
class="full-bleed"
alt="Japanese Manhole Art"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;</description><guid>https://kau.sh/blog/japanese-manhole-art/</guid><pubDate>Mon, 18 Mar 2019 07:00:00 GMT</pubDate></item><item><title>
Kotlin 1.3.20 released</title><link>https://kau.sh/blog/kotlin-1.3.2-released/</link><description>
&lt;blockquote&gt;
&lt;p&gt;This feature is beneficial for projects defining custom source sets, since the compilation of independent source sets can be parallelized. In the case of multiplatform projects, targets for different platforms can also be built in parallel. For Android, the debug and release build types can be compiled in parallel.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This sounds pretty cool, however I paused to think how often would i need the &lt;code&gt;debug&lt;/code&gt; and &lt;code&gt;release&lt;/code&gt; build types to be compiled in parallel? Probably CI like environment? oooo but with &lt;code&gt;androidTest&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; ? that might help.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Brings improvements for inline classes&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;inline classes imho, is one of those things developers should jump on to right away. It can have a significant impact on the way you do development especially as it relates to Type driven design. I did a &lt;a href="https://fragmentedpodcast.com/episodes/149/"&gt;fragmented episode&lt;/a&gt; on the subject recently.&lt;/p&gt;
&lt;p&gt;All improvements to inline classes, highly welcome 🙏.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since Kotlin 1.3, you can use the main function without parameters.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For the &lt;a href="https://github.com/holgerbrandl/kscript"&gt;kscript&lt;/a&gt;ers, this is actually pretty nifty. I missed the introduction of this feature in 1.3.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A new intention allows to convert constructs using lambdas with SAM to anonymous objects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you understand this well, you&amp;rsquo;ll realize this almost seems backwards. Why would i ever want SAM -&amp;gt; anonymous you ask? Hello dear RxJava Kotlin users, read &lt;a href="https://readyset.build/kotlins-sam-problem-f315ffe6be3a"&gt;this blog post on Kotlin&amp;rsquo;s SAM problem&lt;/a&gt; to understand the specifics better.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In addition, we’ve added support for Compile Avoidance for kaptKotlin tasks in Gradle, improving build performance times.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I hate on kapt a lot for affecting my build times. Any improvement here is always welcome.&lt;/p&gt;
&lt;p&gt;In closing, how cool is it, my chosen programming language gets an update, and i can start using it almost right away? (let the Java-writing-old-timers from the early Android world envy).&lt;/p&gt;</description><guid>https://kau.sh/blog/kotlin-1.3.2-released/</guid><pubDate>Sun, 27 Jan 2019 07:00:00 GMT</pubDate></item><item><title>
New Year - 2019</title><link>https://kau.sh/blog/new-year-2019/</link><description>
&lt;p&gt;So i&amp;rsquo;ve been doing these kinds of &lt;a href="https://kau.sh/tags/new-year/"&gt;posts for sometime now&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Truth is i&amp;rsquo;ve been inconsistent with the format. Sometimes these are reflection posts (what happened last year) and other times they are resolutions posts (what i&amp;rsquo;m looking forward to in the next year). Usually it lands up being an amalgam of both, and I kind of like that personally. So i&amp;rsquo;m going to stick to keeping it a mishmash of thoughts as i begin the new year.&lt;/p&gt;
&lt;h1 id="2018-in-review"&gt;
2018 in review:
&lt;a class="heading-anchor" href="#2018-in-review" aria-label="Link to 2018 in review:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;So &lt;a href="https://karthickg.com/"&gt;the brother&lt;/a&gt; was married end of 2017 and while things got busy (Indian weddings and all), it was a blast.&lt;/li&gt;
&lt;li&gt;I helped organize Instacart&amp;rsquo;s &lt;a href="https://twitter.com/kaushikgopal/status/1039613034254266368"&gt;annual hackathon&lt;/a&gt; for the first time and it was way more fun than i anticipated :).&lt;/li&gt;
&lt;li&gt;I &lt;a href="https://twitter.com/kaushikgopal/status/1045606147045175296"&gt;spoke at a conference in Russia&lt;/a&gt;. Moscow was one of the most beautiful cities i&amp;rsquo;ve visited and i was surprised by how friendly the folks were. Easily one of the best trips last year. Definitely going to be visiting them again (don&amp;rsquo;t let the current political climate fool you, it&amp;rsquo;s refreshing how much the locals don&amp;rsquo;t care about that stuff).&lt;/li&gt;
&lt;li&gt;We created our &lt;a href="https://fragmentedpodcast.com/episodes/100/"&gt;100th episode on Fragmented&lt;/a&gt; (it was in 2018, but i didn&amp;rsquo;t write one of these posts for that year so i&amp;rsquo;m allowed to cheat). It remains one of the most fulfilling projects i&amp;rsquo;ve started in my life. The listeners remain the #1 reason Donn and I continue push episodes every week.&lt;/li&gt;
&lt;li&gt;I watched &lt;strong&gt;a tonne&lt;/strong&gt; of movies in the theaters. Sadly, i didn&amp;rsquo;t keep a track of them but intend to for 2020 (already counting 5 movies watched this year).&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="2019-todos"&gt;
2019 todos:
&lt;a class="heading-anchor" href="#2019-todos" aria-label="Link to 2019 todos:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;While i think it&amp;rsquo;s important to live life as the universe serves it to you, i typically like to have a few goals or things in my bucket list that i like to kick the year off with. These are the ones i&amp;rsquo;m thinking/starting with for 2019:&lt;/p&gt;
&lt;h2 id="practice-vim-for-30-days"&gt;
Practice Vim for 30 days
&lt;a class="heading-anchor" href="#practice-vim-for-30-days" aria-label="Link to Practice Vim for 30 days"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I love tinkering with my tools and i genuinely think that responsible &amp;ldquo;good&amp;rdquo; programmers spend at least 20-30% of their time tweaking their tooling. I&amp;rsquo;ve also noticed over the years, that the mentors i look up to, who use vim, friggin &lt;em&gt;fly&lt;/em&gt; with it. It&amp;rsquo;s mesmerizing to simply watch them fiddle with text! Check &lt;a href="https://www.destroyallsoftware.com/screencasts/catalog/some-vim-tips"&gt;Gary from DAS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So this year, i decided i&amp;rsquo;m going to only use vim, for 30 days straight. I&amp;rsquo;m not saying i&amp;rsquo;ll switch to vim 100% yet (thought it&amp;rsquo;s looking very likely) but i want to genuinely give it a fair shot and see if sticks (&lt;em&gt;this is day 22 of only using vim and this post was written entirely in vim. Look out for a follow up post with my feelings and findings&lt;/em&gt;).&lt;/p&gt;
&lt;h2 id="care-about-your-privacy"&gt;
Care about your privacy
&lt;a class="heading-anchor" href="#care-about-your-privacy" aria-label="Link to Care about your privacy"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The Facebook debacle shook me a lot this year. We live in a world where democratic presidential races were influenced by Facebook. &lt;strong&gt;Your data is valuable&lt;/strong&gt;. Yes, you who thinks you have nothing to hide, your data is equally valuable. Wake up already.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve already switched completely to Firefox (follow up post on this as well coming). The biggest change will be my phone. I also use an iPhone now along with the Pixel 2 i use and i&amp;rsquo;m starting to not like the fact that Google tracks everything I do and doesn&amp;rsquo;t allow me to use half their services without sharing uncomfortable amounts of my data. This one is going to be hard.&lt;/p&gt;
&lt;h2 id="blogging--screencasting"&gt;
Blogging + screencasting?
&lt;a class="heading-anchor" href="#blogging--screencasting" aria-label="Link to Blogging &amp;#43; screencasting?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I moved this blog to &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; (after it became clear that &lt;a href="http://octopress.org/"&gt;Octopress&lt;/a&gt; was probably not going to be actively maintained anymore). My hello 2020 post will bear evidence to how active i&amp;rsquo;ve been here but i&amp;rsquo;m going to try and post more here.&lt;/p&gt;
&lt;p&gt;The other thing i&amp;rsquo;m also really curious about doing more is screencasting. I don&amp;rsquo;t want to limit it to just Andorid, if anything i want to do more non-Androidy stuff like my tips, hacks, productivity scripts etc. Let&amp;rsquo;s see if i can bring myself to doing more of that.&lt;/p&gt;
&lt;p&gt;I remain grateful for my friendships and the new friends i&amp;rsquo;ve made this year, that have truly made me a better person.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s kick 2019&amp;rsquo;s butt!&lt;/p&gt;</description><guid>https://kau.sh/blog/new-year-2019/</guid><pubDate>Tue, 22 Jan 2019 07:00:00 GMT</pubDate></item><item><title>
Unidirectional State Flow patterns – a refactoring story</title><link>https://kau.sh/ppt/unidirectional-state-flow-patterns-a-refactoring-story/</link><description>
&lt;p&gt;Learn how to go to your existing old mobile app and refactor it into one with a
powerful architecture.&lt;/p&gt;
&lt;p&gt;Gave the presentation at &lt;a href="https://mbltdev.ru/en"&gt;MBLT Dev 2018&lt;/a&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="752cc3f1d2964cf8858feb6943641afa" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/unidirectional-state-flow-patterns-a-refactoring-story/</guid><pubDate>Fri, 28 Sep 2018 00:00:00 GMT</pubDate></item><item><title>
Free hackathon vote tabulation using Google Forms &amp; Kotlin</title><link>https://kau.sh/blog/free-hackathon-voting-google-form-with-kotlin/</link><description>
&lt;p&gt;We recently held our semi-annual hackathon at Instacart - the Carrot Wars 2018!&lt;/p&gt;
&lt;p&gt;In putting this hackathon together, I noticed a pretty blaring gap - there wasn&amp;rsquo;t a simple (and free) online service that would quickly tabulate the results for a hackathon event. We looked around and found some nifty options, but most of them were a tad bit too expensive for our liking. They also were not setup for a single event use or required a monthly subscription. There other usage restrictions, too - max vote count, concurrent user count, etc.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;d think there would be at least some option out there, given how popular hackathons are these days. We did some cursory searching but couldn&amp;rsquo;t find something that would work for us.&lt;/p&gt;
&lt;p&gt;My co-organizer , admittedly wiser about such things, made it super clear to me, &amp;ldquo;No KG, we ARE NOT building our own hackathon voting website 2 days before the event! You have more important things to do!&amp;rdquo;. So I set out to do exactly (½ of) just that.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re on a time crunch and just want to use this post for a hackathon you&amp;rsquo;re about to run, jump to the &amp;ldquo;How to use this form for your hackathon&amp;rdquo; section below.&lt;/p&gt;
&lt;p&gt;For the juicy details, please continue reading!&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h1 id="solution-choice"&gt;
Solution choice
&lt;a class="heading-anchor" href="#solution-choice" aria-label="Link to Solution choice"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="google-forms"&gt;
Google Forms
&lt;a class="heading-anchor" href="#google-forms" aria-label="Link to Google Forms"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I wasn&amp;rsquo;t too thrilled about building an online voting form website (hi 2018!) so instead I chose to just leverage Google Forms. Google Forms is pretty easy to use, can handle a bunch of users slamming your form at the same time, provides a decent enough UI and is ridiculously easy to create and get going.&lt;/p&gt;
&lt;h2 id="kotlin-script"&gt;
Kotlin script
&lt;a class="heading-anchor" href="#kotlin-script" aria-label="Link to Kotlin script"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The part I found more interesting about this process was the result tabulation. I could have probably gotten Google Forms to dump the results in a Google Sheet. But my excel/sheets expression fu was not strong enough to conconct the required mathematical expression to get the results I wanted.&lt;/p&gt;
&lt;p&gt;So instead of a seizure inducing mathematical sheet expression, I did what came more naturally to me and wrote a script in Kotlin to tabulate the votes and &lt;code&gt;println&lt;/code&gt; the results nice and cleanly 😎.&lt;/p&gt;
&lt;p&gt;I chose Kotlin cause I&amp;rsquo;m an Android developer and we love Kotlin here at Instacart. Interestingly the hackathon also coincided with the &lt;a href="https://blog.jetbrains.com/kotlin/2018/06/kotlin-1-2-50-is-out/"&gt;release of Kotlin 1.2.50&lt;/a&gt; where the Kotlin scripting support was supposed to have been improved.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s something very satisfying about running a script in a terminal prompt like you would with bash, but without all the unpleasantness of the bash syntax and all the pleasantness of the Kotlin language.&lt;/p&gt;
&lt;h1 id="voting-process"&gt;
Voting process:
&lt;a class="heading-anchor" href="#voting-process" aria-label="Link to Voting process:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;The hackathon had 5 categories (based on our core company values) that folks could participate in&lt;/li&gt;
&lt;li&gt;On presentation day, we sent out a google form to all hackathon attendees with the list of final projects. They would pick the 1st, 2nd and 3rd places for each category&lt;/li&gt;
&lt;li&gt;Google Forms allows a pretty convenient way to export these results as a CSV file&lt;/li&gt;
&lt;li&gt;This &lt;a href="https://github.com/kaushikgopal/kotlin-scripts/blob/master/carrot-wars-tabulate.kts"&gt;Kotlin script&lt;/a&gt; ingests the CSV, tabulate the results, does some weighting math and prints the results for each category&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="how-to-use-this-form-for-your-hackathon"&gt;
How to use this form for your hackathon
&lt;a class="heading-anchor" href="#how-to-use-this-form-for-your-hackathon" aria-label="Link to How to use this form for your hackathon"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="1-building-the-google-form"&gt;
1. Building the Google Form
&lt;a class="heading-anchor" href="#1-building-the-google-form" aria-label="Link to 1. Building the Google Form"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I can&amp;rsquo;t recommend enough this Google Forms add-on called &lt;a href="https://workspace.google.com/marketplace/app/form_ranger/387838027286"&gt;&lt;strong&gt;formRanger&lt;/strong&gt;&lt;/a&gt;. formRanger allows you to populate your form from a simple google sheet. &lt;/p&gt;
&lt;p&gt;This was particularly useful for us because we had folks signing up right at the very last moment, wanting to pitch their hackathon project. So up until the very last presentation, the project list was in flux and so the form needed to constantly be modified (we had 5 categories and about 30 pitches. That&amp;rsquo;s 150 entrees that we would have had to keep consistent - no bueno). With formRanger setup this was a single list in a simple google sheet.&lt;/p&gt;
&lt;p&gt;You want your questions to be of type &lt;strong&gt;multiple choice grid&lt;/strong&gt;. This way you can link a project (rows) to a position i.e. 1st, 2nd and 3rd place (columns).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s another important option you want set for each question - &amp;ldquo;&lt;strong&gt;Limit to one response per column&lt;/strong&gt;&amp;rdquo;. This will make sure you don&amp;rsquo;t have more than one 1st, 2nd or 3rd place.&lt;/p&gt;
&lt;h2 id="2-installing-kotlin-script"&gt;
2. Installing kotlin script
&lt;a class="heading-anchor" href="#2-installing-kotlin-script" aria-label="Link to 2. Installing kotlin script"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;You can find most of the &lt;a href="https://github.com/kaushikgopal/kotlin-scripts"&gt;instructions for setting up the Kotlin script in the github project&lt;/a&gt;. tl;dr:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# install sdkman (if you haven&amp;#39;t already)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl -s &lt;span style="color:#e6db74"&gt;&amp;#34;https://get.sdkman.io&amp;#34;&lt;/span&gt; | bash
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install maven
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install kotlin
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sdk install kscript
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;# alternatively with homebrew&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install maven
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;brew install holgerbrandl/tap/kscript
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="3-generate-the-csv-results-file"&gt;
3. Generate the CSV results file
&lt;a class="heading-anchor" href="#3-generate-the-csv-results-file" aria-label="Link to 3. Generate the CSV results file"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In your google form, you should see a tab called &amp;ldquo;Reponses&amp;rdquo;. From there you should be able to &amp;ldquo;download responses (.csv)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Google Forms has a pretty decent visualization, which might just work for you, but I wanted to add some more sophistication in the tabulation of the results like weighting and maybe filtering based on categories. Basically, moving this to an actual program opens up a whole bunch of possibilities.&lt;/p&gt;
&lt;h2 id="4-running-the-command"&gt;
4. Running the command
&lt;a class="heading-anchor" href="#4-running-the-command" aria-label="Link to 4. Running the command"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;From a terminal prompt run the command like so:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kscript kscript carrot-wars-tabulate.kts ~/Downloads/star-wars-demo-results.csv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;The very first time you run the script, it&amp;rsquo;ll take sometime as it pulls the necessary dependencies via maven. Give it some time; subsequent runs should be pretty snappy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s what it should look like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ kscript carrot-wars-tabulate.kts ~/Downloads/star-wars-demo-results.csv
******* PROGRAM START *****************
1 folk(s) voted!
-----------------------
Category: &amp;quot;Which is your favorite star wars movie overall?
----
STAR WARS: EPISODE IV A NEW HOPE ----&amp;gt; 6 &amp;lt;- [1]
STAR WARS: EPISODE VII THE FORCE AWAKENS ----&amp;gt; 3 &amp;lt;- [2]
ROGUE ONE: A STAR WARS STORY ----&amp;gt; 2 &amp;lt;- [3]
-----------------------
Category: &amp;quot;Which was your favorite SW movie from the original episodes?
----
STAR WARS: EPISODE IV A NEW HOPE ----&amp;gt; 6 &amp;lt;- [1]
STAR WARS: EPISODE VII THE FORCE AWAKENS ----&amp;gt; 3 &amp;lt;- [2]
STAR WARS: EPISODE V THE EMPIRE STRIKES BACK ----&amp;gt; 2 &amp;lt;- [3]
-----------------------
Category: &amp;quot;Which was your favorite standalone SW movie?
----
ROGUE ONE: A STAR WARS STORY ----&amp;gt; 6 &amp;lt;- [1]
SOLO: A STAR WARS STORY ----&amp;gt; 3 &amp;lt;- [2]
******* PROGRAM END *****************
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Haters hate all you want, but those are my movie choices!&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="4-resources"&gt;
4. Resources
&lt;a class="heading-anchor" href="#4-resources" aria-label="Link to 4. Resources"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://goo.gl/forms/hVl7afuKAKgPFhxy1"&gt;Demo Google voting form&lt;/a&gt; (go ahead and vote for your choice!)&lt;/li&gt;
&lt;li&gt;Sample &lt;a href="https://raw.githubusercontent.com/kaushikgopal/kotlin-scripts/master/star-wars-demo-results.csv"&gt;CSV responses file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.google.com/spreadsheets/d/1v94s9nIrJcO53ohKke9nTELWOv9wByWfsTyn-Snw_cI/edit?usp=sharing"&gt;Google sheet&lt;/a&gt; used to populate above form&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/kaushikgopal/kotlin-scripts#tabulate-hackathon-votes"&gt;Source repo&lt;/a&gt; for Kotlin script that tabulates and prints out the results&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally posted this article on the &lt;a href="https://tech.instacart.com/free-hackathon-vote-tabulation-using-google-forms-kotlin-3c7b7080ea"&gt;Instacart tech blog&lt;/a&gt;. Reproduced here for posterity.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/free-hackathon-voting-google-form-with-kotlin/</guid><pubDate>Sun, 09 Sep 2018 07:00:00 GMT</pubDate></item><item><title>
Supercharging your workflow with App Center and Azure</title><link>https://kau.sh/ppt/supercharging-your-workflow-with-app-center-and-azure/</link><description>
&lt;p&gt;Discover how to break down the barrier between your hopes for a mobile CI/CD system and what&amp;rsquo;s available today using the powerful Azure toolset and App Center API&amp;rsquo;s. Go from an idea to a multi-platform React Native app powered by CI/CD in just a few steps. We’ll continue from there to learn how Logic Apps and Azure Functions helped power Instacart&amp;rsquo;s mobile development workflow from commit to a release to the store.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/HG3FD_gdTCo?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;</description><guid>https://kau.sh/ppt/supercharging-your-workflow-with-app-center-and-azure/</guid><pubDate>Mon, 07 May 2018 00:00:00 GMT</pubDate></item><item><title>
Squircle and sweating the design details</title><link>https://kau.sh/blog/squircle-and-sweating-the-design-details/</link><description>
&lt;p&gt;This is such a fantastic post on how Apple sweats certain &lt;em&gt;almost&lt;/em&gt; unnoticeable design details.&lt;/p&gt;
&lt;p&gt;I picked up a whole bunch of nuggets reading this article:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip;iPhone X rounded screen corners don’t use the classic rounding method where you move in a straight line and then arc using a single quadrant of a circle. Instead, the math is a bit more complicated. Commonly called a squircle, the slope starts sooner, but is more gentle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here&amp;rsquo; the wikipedia link to the mathematical shape &lt;a href="https://en.wikipedia.org/wiki/Squircle"&gt;Squircle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The aforementioned article follows &lt;a href="https://hackernoon.com/apples-icons-have-that-shape-for-a-very-good-reason-720d4e7c8a14"&gt;another link&lt;/a&gt; which has more gems:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Apple doesn’t have the patent on this. Any company can bring their surfaces to this degree of quality. So why don’t they? Companies used to have more excuses. It used to be that engineering CAD tools weren’t as concerned about this sort of thing. Or engineers might not have been expert in that module of their CAD tools. Or surface design tools and engineering tools didn’t play well together. Or its importance to the bottom line wasn’t recognized.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I got to admit, once I learnt of this shape — and how beautiful it looks when applied to hardware — I can&amp;rsquo;t stop seeing the absence of this shape in competing products.&lt;/p&gt;
&lt;p&gt;Keep your friends close &lt;a href="https://www.youtube.com/watch?v=DfHJDLoGInM"&gt;;)&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/squircle-and-sweating-the-design-details/</guid><pubDate>Thu, 15 Mar 2018 07:00:00 GMT</pubDate></item><item><title>
Smarter ToDos with Kotlin</title><link>https://kau.sh/blog/smarter-todos-with-kotlin/</link><description>
&lt;p&gt;Checkout this quick blog post I wrote for my company, tweaking the existing Kotlin TODO to work towards our requirements.&lt;/p&gt;
&lt;p&gt;While I don&amp;rsquo;t think this solution is a panacea for all your missing code snippets, I have found some luck with this method, in adding accountability for those PR review feedback comments you say you&amp;rsquo;ll get to, but conveniently forget :)&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a bonus if you&amp;rsquo;re reading this article from here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; * @param month - regular month (so 3 = March)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@JvmOverloads&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;fun&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ISDate&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; day: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; month: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; year: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hour: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; minute: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; second: Int? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; date: Date? = &lt;span style="color:#66d9ef"&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;): Date = Date().apply {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;val&lt;/span&gt; cal = &lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.getInstance()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; date&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.time = &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; day&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.DAY_OF_MONTH, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; month&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.MONTH, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt; - &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; year&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.YEAR, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; hour&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.HOUR_OF_DAY, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; minute&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.MINUTE, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; second&lt;span style="color:#f92672"&gt;?.&lt;/span&gt;let { cal.&lt;span style="color:#66d9ef"&gt;set&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;Calendar&lt;/span&gt;.SECOND, &lt;span style="color:#66d9ef"&gt;it&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; cal.time
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It’s a pretty straightforward date builder with minor tweaks (for e.g. 3 = March cause what are we, Monsters ?).&lt;/p&gt;</description><guid>https://kau.sh/blog/smarter-todos-with-kotlin/</guid><pubDate>Wed, 14 Mar 2018 07:00:00 GMT</pubDate></item><item><title>
My new programming font - IBM Plex Mono</title><link>https://kau.sh/blog/ibm-plex-mono/</link><description>
&lt;p&gt;I&amp;rsquo;m obsessed with typefaces and fonts .&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;My programming productivity is irrationally dependent on the font I pick for my IDE. I have spent &lt;em&gt;unhealthy&lt;/em&gt; amounts of time experimenting and trying different fonts for programming.&lt;/p&gt;
&lt;p&gt;I usually prefer a monospaced font and I&amp;rsquo;ve bounced between &lt;a href="https://fonts.google.com/specimen/Inconsolata"&gt;Inconsolata&lt;/a&gt; and &lt;a href="https://www.fonts.com/font/microsoft-corporation/consolas/regular"&gt;Consolas&lt;/a&gt; in the past – both truly beautiful typefaces.&lt;/p&gt;
&lt;p&gt;Recently though, a design director at Instacart shared this link on the &lt;a href="https://lawsofux.com/"&gt;laws of UX&lt;/a&gt; (a fantastic read btw).&lt;/p&gt;
&lt;p&gt;But — hot damn — I was smitten after looking at that website. The font used there was &lt;strong&gt;GORGEOUS&lt;/strong&gt;! I knew it was monospaced just from looking at it. I didn&amp;rsquo;t get past the first law, without first finding out which font was being used.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/ibm_plex_mono.png" alt="IBM Plex Mono used on lawsofux.com" title="IBM Plex Mono sample"&gt;&lt;/p&gt;
&lt;figcaption&gt;[Image: lawsofux.com]&lt;/figcaption&gt;
&lt;p&gt;Thus I stumbled upon my new programming font – IBM Plex Mono. I don&amp;rsquo;t make a font change in my IDE that casually, without first putting it through the paces.&lt;/p&gt;
&lt;p&gt;I give it a glowing recommendation for your IDE. There&amp;rsquo;s an interesting back story behind the making of the font on &lt;a href="https://medium.com/@Uxtrending/ibm-plex-a-typeface-and-a-story-e01b2e502aab"&gt;Medium&lt;/a&gt; too.&lt;/p&gt;
&lt;p&gt;Btw, just changing the font is not enough – you have to pick the right size and line spacing (I&amp;rsquo;ve found a size of 15 and line-spacing of 1.15 works best for me).&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/IBM/plex"&gt;Download IBM Plex Mono&lt;/a&gt; absolutely for free.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Did you know – fonts and typefaces aren&amp;rsquo;t &lt;a href="https://graphicdesign.stackexchange.com/questions/12717/what-is-the-difference-between-a-font-and-a-typeface"&gt;necessarily interchangeable&lt;/a&gt;. You use &amp;ldquo;typeface&amp;rdquo; when you’d use &amp;ldquo;song&amp;rdquo; (e.g. &amp;ldquo;I love that song/typeface …&amp;rdquo;), and &amp;ldquo;font&amp;rdquo; when you’d use &amp;ldquo;track&amp;rdquo; (&amp;quot;… so I’m going to buy the track/font for it&amp;quot;).&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/ibm-plex-mono/</guid><pubDate>Fri, 02 Mar 2018 07:00:00 GMT</pubDate></item><item><title>
Kindness and Positivity</title><link>https://kau.sh/blog/more-kindness-and-positivity/</link><description>
&lt;p&gt;We need more kindness and positivity in our online lives today. There is no side, that cannot benefit from that.&lt;/p&gt;
&lt;p&gt;Being rational or correct is futile without empathy.&lt;/p&gt;
&lt;p&gt;You can have opposing (or even incorrect) views but you have to find a kind and positive way to express that.&lt;/p&gt;
&lt;p&gt;You either learn you&amp;rsquo;re wrong or convince the other of your point of view. But everyone is more willing to have the conversation, if approached with kindness and positivity.&lt;/p&gt;
&lt;p&gt;A good first step for me is surrounding myself with kinder and positive people.&lt;/p&gt;
&lt;!--
http://boz.com/articles/be-kind.html
http://gizmodo.com/exclusive-heres-the-full-10-page-anti-diversity-screed-1797564320
https://medium.com/@yonatanzunger/so-about-this-googlers-manifesto-1e3773ed1788
--&gt;</description><guid>https://kau.sh/blog/more-kindness-and-positivity/</guid><pubDate>Tue, 08 Aug 2017 07:00:00 GMT</pubDate></item><item><title>
Rx by example – Volume 3 (the multicast edition)</title><link>https://kau.sh/ppt/rx-by-example-volume-3-the-multicast-edition/</link><description>
&lt;p&gt;The Android development community today has embraced Rx(Java) in all it’s glory. But as developer’s understanding of RxJava has matured, they’ve started to peel back the layers and unleash its true power and potential.&lt;/p&gt;
&lt;p&gt;One such power is &amp;ldquo;Multicasting&amp;rdquo; where you get to share work across your app and reuse a whole bunch of stuff. This is really hard to do in general but Rx makes it really really easy.&lt;/p&gt;
&lt;p&gt;Rx itself though has a steep learning curve. But the easiest way to grasp the concepts and features of Rx, is through examples. I’ve given talks in the past explaining Rx usage purely through examples and folks have found this to be the easiest way to warm up to Rx.&lt;/p&gt;
&lt;p&gt;So that is what we’ll do today, learn by examples! In this talk, we look at fresh set of examples, all demonstrating the powerful concept of multicasting, but in the comfortable playground of simple and practical examples.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/wxx9hEi_E8c?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="8206ef736b694a5d903091dc260e502b" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/rx-by-example-volume-3-the-multicast-edition/</guid><pubDate>Fri, 14 Jul 2017 00:00:00 GMT</pubDate></item><item><title>
RxJava 1 -&gt; RxJava 2 (Disposing Subscriptions)</title><link>https://kau.sh/blog/rxjava-1-rxjava-2-disposing-subscriptions/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
2 part series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is a continuation post in a 2 part series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="../rxjava1-rxjava2-migration-understanding-changes/"&gt;Understanding the changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../rxjava-1-rxjava-2-disposing-subscriptions/"&gt;Disposing subscriptions&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h1 id="disposing-subscriptions"&gt;
Disposing Subscriptions
&lt;a class="heading-anchor" href="#disposing-subscriptions" aria-label="Link to Disposing Subscriptions"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;This was the part that I initially found most tricky to grasp but also most important to know as an AndroidDev (memory leak and all).&lt;/p&gt;
&lt;p&gt;Jedi master Karnok explains this best in the wiki:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In RxJava 1.x, the interface rx.Subscription was responsible for stream and resource lifecycle management, namely unsubscribing a sequence and releasing general resources such as scheduled tasks. The Reactive-Streams specification took this name for specifying an interaction point between a source and a consumer: org.reactivestreams.Subscription allows requesting a positive amount from the upstream and allows cancelling the sequence.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;From that definition alone, it would appear like nothing&amp;rsquo;s changed but that is definitely not the case. In my first post, I pointed out:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Publisher.&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Subscriber) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; Subscription
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The use of =&amp;gt; vs = was intentional. If you look at the &lt;a href="https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/api/src/main/java/org/reactivestreams/Publisher.java#L28"&gt;source code for &lt;code&gt;Publisher&lt;/code&gt;&amp;rsquo;s subscribe method&lt;/a&gt; again, you&amp;rsquo;ll notice a return type of &lt;code&gt;void&lt;/code&gt; viz. it doesn&amp;rsquo;t return a Subscription for you to tack on to a CompositeSubscription (which you can then conveniently dispose of onStop/onDestroy).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Publisher&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// return type void (not Subscription like before)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Subscriber&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; s);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Karnok again:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because Reactive-Streams base interface, org.reactivestreams.Publisher defines the subscribe() method as void, Flowable.subscribe(Subscriber) no longer returns any Subscription (or Disposable). The other base reactive types also follow this signature with their respective subscriber types.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So if you look at the declarations again&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// RxJava specific constructs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Observable implements &amp;#34;ObservableSource&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ObservableSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Observer&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Single implements SingleSource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SingleSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(SingleObserver&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CompletableSource&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(CompletableObserver observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MaybeSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(MaybeObserver&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the return type &lt;code&gt;void&lt;/code&gt; in all of them.&lt;/p&gt;
&lt;h2 id="getting-hold-of-subscriptions"&gt;
Getting hold of Subscriptions
&lt;a class="heading-anchor" href="#getting-hold-of-subscriptions" aria-label="Link to Getting hold of Subscriptions"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So you may ask how do I get a hold off that Subscription then (so that you might rightly cancel or dispose it off like a responsible citizen)?&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s take a look at the &lt;a href="https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.0/api/src/main/java/org/reactivestreams/Subscriber.java#L31"&gt;the &lt;code&gt;Subscriber&lt;/code&gt;&amp;rsquo;s onSubscribe&lt;/a&gt; method:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Subscriber&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onSubscribe&lt;/span&gt;(Subscription s);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Subscriptions are additionally cool cause they have:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// s.request(n) -&amp;gt; request data&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// s.cancel() -&amp;gt; cancel this connection&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onNext&lt;/span&gt;(T t);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onError&lt;/span&gt;(Throwable t);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onComplete&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You are now given the Subscription class as a parameter in your &lt;code&gt;onSubscribe&lt;/code&gt; callback. So within the OnSubscribe method, you have a hold of the subscription and can then conveniently dispose of the Subscription inside the &lt;code&gt;onSubscribe&lt;/code&gt; callback.&lt;/p&gt;
&lt;p&gt;This was actually a pretty well thought off change because this really makes the interface for a Subscriber lightweight. In RxJava 1 land, Subscribers were more &amp;ldquo;heavy&amp;rdquo; cause they had to deal with a lot of the internal state handling.&lt;/p&gt;
&lt;p&gt;&amp;hellip; but who are we kidding: that is not convenient at all (atleast for those of us who need the subscriber to depend on our lifecycle). I&amp;rsquo;d rather just shove everything into a CompositeSubscription like before and be on my merry way. But such are the rulings of the Reactive Streams spec.&lt;/p&gt;
&lt;p&gt;Thankfully the maintainers of RxJava in all their benevolence realized this trade-off and have remedied this with convenient helpers.&lt;/p&gt;
&lt;p&gt;But first, some more definitions:&lt;/p&gt;
&lt;h2 id="disposable-is-the-new-subscripton"&gt;
&lt;code&gt;Disposable&lt;/code&gt; is the new &lt;code&gt;Subscripton&lt;/code&gt;
&lt;a class="heading-anchor" href="#disposable-is-the-new-subscripton" aria-label="Link to Disposable is the new Subscripton"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;What we called &lt;code&gt;Subscription&lt;/code&gt; in RxJava 1 is now called &lt;code&gt;Disposable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why couldn&amp;rsquo;t we just keep the name &lt;code&gt;Subscription&lt;/code&gt;? (per my understanding):&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You have to remember the Reactive Streams spec already has this name reserved and the maintainers of RxJava 2 are serious about the spec adherence. We don&amp;rsquo;t want confusion about there being more functionality with an Rx Subscription vs other Reactive Stream spec adhering libraries&lt;/li&gt;
&lt;li&gt;We still want some of the behaviors and conveniences of RxJava 1 like CompositeSubscriptions.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So if Disposables is what we&amp;rsquo;re using now, by that token we have a CompositeDisposable which is the object you want to be using and tacking all your Disposables onto. It functions pretty similarly to how we used CompositeSubscription before.&lt;/p&gt;
&lt;p&gt;Ok, back to the original question: how do I get a hold of the Disposable?&lt;/p&gt;
&lt;h2 id="getting-hold-of-disposables"&gt;
Getting hold of Disposables
&lt;a class="heading-anchor" href="#getting-hold-of-disposables" aria-label="Link to Getting hold of Disposables"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Now before we go any further, if you&amp;rsquo;re adding your callbacks directly in the form of lambdas, this is not a problem as most observable sources return a Disposable with their subscribe method call when not provided with a subscriber object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Flowable.&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Subscriber)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// void return type&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Flowable.&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(nextEvent &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {}, error &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {}, () &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {})
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// return Disposable so we&amp;#39;re good&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So if you look at some sample code, the below works fine no problem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;disposable &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; myAwesomeFlowable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;observeOn&lt;/span&gt;(AndroidSchedulers.&lt;span style="color:#a6e22e"&gt;mainThread&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(event &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// onNext&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, throwable &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// onError&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }, () &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// onComplete&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compositeDisposable.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(disposable);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;However if I rewrote that code just a little differently:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;disposable &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; myAwesomeFlowable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;observeOn&lt;/span&gt;(AndroidSchedulers.&lt;span style="color:#a6e22e"&gt;mainThread&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; FlowableSubscriber&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TextViewTextChangeEvent&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onSubscribe&lt;/span&gt;(Subscription subscription) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onNext&lt;/span&gt;(TextViewTextChangeEvent textViewTextChangeEvent) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onError&lt;/span&gt;(Throwable t) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onComplete&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ^ THIS IS WRONG. Won&amp;#39;t work&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// compositeDisposable.add(disposable);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The above code won&amp;rsquo;t compile. If you want to pass a Subscriber object (like the above &lt;code&gt;FlowableSubscriber&lt;/code&gt;, &lt;code&gt;ObservableSource&lt;/code&gt; or an &lt;code&gt;Observer&lt;/code&gt;) then this strategy won&amp;rsquo;t work.&lt;/p&gt;
&lt;p&gt;A lot of existing RxJava 1 code uses this strategy a lot, so the RxJava maintainers very kindly added a handy method on most Publishers called &lt;code&gt;subscribeWith&lt;/code&gt;. From the wiki:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Due to the Reactive-Streams specification, Publisher.subscribe returns void and the pattern by itself no longer works in 2.0. To remedy this, the method E subscribeWith(E subscriber) has been added to each base reactive class which returns its input subscriber/observer as is.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;E &lt;span style="color:#a6e22e"&gt;subscribeWith&lt;/span&gt;(E subscriber)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re still following closely, you&amp;rsquo;d ask&amp;hellip; wait! that doesn&amp;rsquo;t return a Disposable! why the hell is this even remotely more convenient?&lt;/p&gt;
&lt;p&gt;Well&amp;hellip; it says that the &lt;code&gt;Subscriber&lt;/code&gt; you pass is sent back to you with &lt;code&gt;subscribeWith&lt;/code&gt;. But what if your Subscriber itself &amp;ldquo;implemented&amp;rdquo; the Disposable interface? If you had a DisposableSubscriber, you could for all practical purposes treat it as a disposable and tack it on to a CompositeDisposable, while still using it as a Subscriber. That&amp;rsquo;s typically the pattern you want to adopt. Here&amp;rsquo;s some code that should make these techniques clear:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;disposable &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; myAwesomeFlowable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;observeOn&lt;/span&gt;(AndroidSchedulers.&lt;span style="color:#a6e22e"&gt;mainThread&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribeWith&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; getDisposableSubscriber&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;TextViewTextChangeEvent&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onNext&lt;/span&gt;(TextViewTextChangeEvent event) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onError&lt;/span&gt;(Throwable e) {}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onComplete&lt;/span&gt;() { }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compositeDisposable.&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(disposable);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Apart from &lt;code&gt;DisposableSubscriber&lt;/code&gt;, there&amp;rsquo;s also a &lt;code&gt;ResourceSubscriber&lt;/code&gt; which implements Disposable. There&amp;rsquo;s also a &lt;code&gt;DefaultSubscriber&lt;/code&gt; which doesn&amp;rsquo;t implement the Disposable interface, so you can&amp;rsquo;t use it with &lt;code&gt;subscribeWith&lt;/code&gt; (you could use it but you wouldn&amp;rsquo;t get anything &amp;ldquo;disposable&amp;rdquo; out of it).&lt;/p&gt;
&lt;p&gt;It seems like both DisposableSubscriber and ResourceSubscriber do the same thing. Why do both of these exist you ask?&lt;/p&gt;
&lt;p&gt;The original 1.x Subscriber had the ability to take Subscriptions which allowed &amp;ldquo;disposing&amp;rdquo; the additional resources that particular Subscriber needed when the lifecycle ended or the Subscriber got unsubscribed. Since 2.x Subscriber is an interface declared externally, the old functionality had to be implemented via a separate abstract class: &amp;ldquo;ResourceSubscriber&amp;rdquo;. A key difference is you can create and associate Disposable resources with it and dispose them together from within the onError() and onComplete() methods you implement. Have a look at the example from &lt;a href="http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/subscribers/ResourceSubscriber.html"&gt;within the docs&lt;/a&gt;&lt;/p&gt;
&lt;h1 id="to-clear-or-to-dispose"&gt;
to .clear or to .dispose
&lt;a class="heading-anchor" href="#to-clear-or-to-dispose" aria-label="Link to to .clear or to .dispose"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;There&amp;rsquo;s no longer an &lt;code&gt;unsubscribe&lt;/code&gt; call on CompositeDisposable. It&amp;rsquo;s been renamed to &lt;code&gt;dispose&lt;/code&gt; ☝️️ but you don&amp;rsquo;t want to be using either of those anyway. The &lt;code&gt;clear&lt;/code&gt; method remains, and is most likely the method you want to use.&lt;/p&gt;
&lt;h2 id="whats-the-difference"&gt;
What&amp;rsquo;s the difference?
&lt;a class="heading-anchor" href="#whats-the-difference" aria-label="Link to What’s the difference?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;unsubscribe/dispose &lt;a href="https://github.com/kaushikgopal/RxJava-Android-Samples/commit/1e7d4b2f867a97b32a0cde81cb488c3d17d4952f"&gt;terminates even future subscriptions while clear doesn&amp;rsquo;t&lt;/a&gt; allow you to reuse the CompositeDisposable.&lt;/p&gt;
&lt;p&gt;In the next and final part, we&amp;rsquo;ll look at some of the miscellaneous changes.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My thanks to Donn Felker &amp;amp; David Karnok for reviewing this post. Special thanks to David for correcting some of my misconceptions.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/rxjava-1-rxjava-2-disposing-subscriptions/</guid><pubDate>Wed, 21 Jun 2017 07:00:00 GMT</pubDate></item><item><title>
RxJava 1 -&gt; RxJava 2 (Understanding the changes)</title><link>https://kau.sh/blog/rxjava1-rxjava2-migration-understanding-changes/</link><description>
&lt;p&gt;In case you haven&amp;rsquo;t heard: &lt;a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0"&gt;RxJava2&lt;/a&gt; was released sometime back. RxJava 2 was a massive rewrite with breaking apis (but for good reasons). Most dependent libraries have upgraded by now though, so you&amp;rsquo;re safe to pull that migration trigger with your codebases.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Folks starting out directly with Rx2 might enjoy this guide but it&amp;rsquo;s the ones that started with Rx 1 that will probably appreciate it the most&lt;/em&gt;.&lt;/p&gt;
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="example" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-list"&gt;&lt;line x1="8" y1="6" x2="21" y2="6"&gt;&lt;/line&gt;&lt;line x1="8" y1="12" x2="21" y2="12"&gt;&lt;/line&gt;&lt;line x1="8" y1="18" x2="21" y2="18"&gt;&lt;/line&gt;&lt;line x1="3" y1="6" x2="3.01" y2="6"&gt;&lt;/line&gt;&lt;line x1="3" y1="12" x2="3.01" y2="12"&gt;&lt;/line&gt;&lt;line x1="3" y1="18" x2="3.01" y2="18"&gt;&lt;/line&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-title" dir="auto"&gt;
&lt;div class="callout-title-inner"&gt;
2 part series
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is a continuation post in a 2 part series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="../rxjava1-rxjava2-migration-understanding-changes/"&gt;Understanding the changes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="../rxjava-1-rxjava-2-disposing-subscriptions/"&gt;Disposing subscriptions&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s get started.&lt;/p&gt;
&lt;p&gt;In this first part, I want to dive into making sense of the Rx2 changes from the point of view of an Rx1 user.&lt;/p&gt;
&lt;h1 id="why-things-changed-with-rxjava2"&gt;
Why things changed with RxJava2
&lt;a class="heading-anchor" href="#why-things-changed-with-rxjava2" aria-label="Link to Why things changed with RxJava2"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="tldr--reactive-streams-spec"&gt;
tl;dr- &lt;a href="http://www.reactive-streams.org/"&gt;Reactive Streams&lt;/a&gt; spec
&lt;a class="heading-anchor" href="#tldr--reactive-streams-spec" aria-label="Link to tl;dr- Reactive Streams spec"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="http://www.reactive-streams.org/"&gt;Reactive Streams&lt;/a&gt; is a standard for doing &amp;ldquo;reactive&amp;rdquo; programming and RxJava now implements the Reactive Streams specs with version 2.x. RxJava was sort of a trailblazer in reactive programming land but it wasn&amp;rsquo;t the only library around. There were others that also dealt with reactive paradigms. But with all the libraries adhering to the Reactive Streams spec now, interop between the libraries is a tad bit easier.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/reactive-streams/reactive-streams-jvm/tree/v1.0.0/api/src/main/java/org/reactivestreams"&gt;spec&lt;/a&gt; per say is pretty straightforward with just 4 interfaces:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Publisher&lt;/strong&gt; (anything that publishes events, so &lt;code&gt;Observable&lt;/code&gt;,&lt;code&gt;Flowable&lt;/code&gt; etc. - more on this later)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscriber&lt;/strong&gt; (anything that listens to a Publisher)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Subscription&lt;/strong&gt; (&lt;code&gt;Publisher.subscribe(Subscriber) =&amp;gt; Subscription&lt;/code&gt; when you join a Publisher and a Subscriber, you are given a connection also called a &lt;code&gt;Subscription&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processor&lt;/strong&gt; (a Publisher + a Subscriber, sound familiar? yep &lt;code&gt;Subject&lt;/code&gt;s for us RxJava 1 luddites)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;If you&amp;rsquo;re slightly more curious about the design goals, I also suggest the following resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/ReactiveX/RxJava/wiki/What%27s-different-in-2.0"&gt;What&amp;rsquo;s different in 2.0&lt;/a&gt; wiki page - this is really the place I kept coming back to and referencing when I needed to understand the details&lt;/li&gt;
&lt;li&gt;&lt;a href="http://fragmentedpodcast.com/episodes/053-jake-wharton-on-rxjava-2/"&gt;Fragmented Ep #53 with JakeWharton&lt;/a&gt; (forgive the shameless promotion) - ultimate lazy person&amp;rsquo;s guide to understand why/what things changed with RxJava2, as explained by an actual demigod. This was the one that really made it first click for me.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ReactiveX/RxJava/issues/2787"&gt;Thought process behind the 2.0 design&lt;/a&gt; for the truly loyal&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/artem-zinnatullin/TheContext-Podcast/blob/master/show_notes/Episode_11.md"&gt;Ep 11 of The Context &lt;/a&gt; —  by my friends Hannes and Artem :)&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- * [Exploring RxJava2 for Android](https://youtu.be/htIXKI5gOQU?t=14m49s) - if you'd rather "see" said demigod, check this video out. I would start at the 14:50 mark. --&gt;
&lt;hr&gt;
&lt;p&gt;We should probably get one important change out of the way:&lt;/p&gt;
&lt;h1 id="dependency-change"&gt;
Dependency change
&lt;a class="heading-anchor" href="#dependency-change" aria-label="Link to Dependency change"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Search:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;io.reactivex:rxjava:${rxJavaVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;io.reactivex:rxandroid:${rxAndroidVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;com.jakewharton.rxbinding:rxbinding:${rxBindingsVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;com.squareup.retrofit2:adapter-rxjava:${retrofit2Version}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Replace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-groovy" data-lang="groovy"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;io.reactivex.rxjava2:rxjava:${rxJavaVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;io.reactivex.rxjava2:rxandroid:${rxAndroidVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;com.jakewharton.rxbinding2:rxbinding:${rxBindingsVersion}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;compile &lt;span style="color:#e6db74"&gt;&amp;#34;com.squareup.retrofit2:adapter-rxjava2:${retrofit2Version}&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A minor change in your gradle dependency pull (&amp;ldquo;2&amp;rdquo; suffix).&lt;/p&gt;
&lt;p&gt;The actual classes though have been moved to a new package internally &lt;em&gt;io.reactivex&lt;/em&gt; (vs &lt;em&gt;rx&lt;/em&gt;). So you&amp;rsquo;ll have to change those import statements.&lt;/p&gt;
&lt;p&gt;Also you &amp;ldquo;could&amp;rdquo; theoretically have both Rx1 and Rx2 running simultaneously but this is a &lt;em&gt;bad&lt;/em&gt; idea because certain primary constructs like Observables have a very different notion of handling streams (backpressure). It can be a nightmare during that interim period where you have to remember the behavior differences. Also if you happen to use both Rx1 and Rx2 Observables you have to be careful about qualifying them explicitly with the right package name (&lt;code&gt;rx.Observable&lt;/code&gt; or &lt;code&gt;io.reactivex.Observable&lt;/code&gt;). This is very easy to mix up and get wrong.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bite the bullet and migrate it all in one shot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Another super important change:&lt;/p&gt;
&lt;h1 id="observable---flowable"&gt;
Observable -&amp;gt; Flowable
&lt;a class="heading-anchor" href="#observable---flowable" aria-label="Link to Observable -&amp;gt; Flowable"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;Search : `import rx.Observable;`
Replace: `import io.reactivex.Flowable;`
&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;
&lt;li&gt;Flowable is the new Observable. Succinctly - it’s a backpressure-enabled base reactive class.&lt;/li&gt;
&lt;li&gt;You want to be using Flowable everywhere now, &lt;strong&gt;not&lt;/strong&gt; Observable. Use this as your default.&lt;/li&gt;
&lt;li&gt;Observables are still available for use, but unless you really understand backpressure, you probably don&amp;rsquo;t want to be using them anymore.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Flowable = Observable + backpressure handling&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id="note-on-publishers"&gt;
Note on &amp;ldquo;Publisher&amp;quot;s:
&lt;a class="heading-anchor" href="#note-on-publishers" aria-label="Link to Note on “Publisher&amp;#34;s:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Remember &lt;code&gt;Publisher&lt;/code&gt;? it&amp;rsquo;s basically the Reactive Streams interface for anything that produces events (&lt;em&gt;Reread the Reactive Streams spec section above if this is not making sense&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Flowable&lt;/code&gt; implements &lt;code&gt;Publisher&lt;/code&gt;. This is our new base default reactive class and implements the Reactive Streams spec 1 &amp;lt;-&amp;gt; 1. Think of of Flowable as primero uno &amp;ldquo;Publisher&amp;rdquo; (this is also partly the reason I recommend Flowable as the new default, in the previous section).&lt;/p&gt;
&lt;p&gt;The other base reactive classes that are Publishers include Observable, Single, Completable and Maybe. But they don&amp;rsquo;t implement the &lt;code&gt;Publisher&lt;/code&gt; interface directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why you ask?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well the other base classes are now considered &amp;ldquo;Rx&amp;rdquo; specific constructs with specialized behavior pertaining to Rx. These are not necessarily notions you would find in the Reactive Streams specs.&lt;/p&gt;
&lt;p&gt;We can look at the actual interface declarations and it&amp;rsquo;ll be clear.&lt;/p&gt;
&lt;h1 id="note-on-subscribers"&gt;
Note on &amp;ldquo;Subscriber&amp;quot;s:
&lt;a class="heading-anchor" href="#note-on-subscribers" aria-label="Link to Note on “Subscriber&amp;#34;s:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;So if &lt;code&gt;Publisher&lt;/code&gt; is the Reactive Streams event producer, &lt;code&gt;Subscriber&lt;/code&gt; is the Reactive Streams event &amp;ldquo;listener&amp;rdquo; (it&amp;rsquo;s extremely helpful to keep these terms firmly grounded in our heads, hence the incessant repetition).&lt;/p&gt;
&lt;p&gt;Looking at the actual interface code declaration should offer more clarity to the above two sections.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Reactive Streams spec&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Flowable implements Publisher&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Publisher&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Subscriber&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; s);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As noted before, &lt;code&gt;Publisher&lt;/code&gt; and &lt;code&gt;Subscriber&lt;/code&gt; are part of the Reactive Streams spec. Flowable -which is now numero uno base reactive class of choice- implements &lt;code&gt;Publisher&lt;/code&gt;. All good so far.&lt;/p&gt;
&lt;p&gt;But what about the other base reactive classes like &lt;code&gt;Observable&lt;/code&gt; and &lt;code&gt;Single&lt;/code&gt; that we&amp;rsquo;ve come to love and use?&lt;/p&gt;
&lt;p&gt;On the publishing side, instead of implementing the standard &lt;code&gt;Publisher&lt;/code&gt; interface the other event producers implement a &amp;ldquo;similar&amp;rdquo; interface.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// RxJava specific constructs&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Observable implements &amp;#34;ObservableSource&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ObservableSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(Observer&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// notice &amp;#34;Observer&amp;#34; here vs the standard &amp;#34;Subscriber&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Single implements SingleSource&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;SingleSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(SingleObserver&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;CompletableSource&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(CompletableObserver observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MaybeSource&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(MaybeObserver&lt;span style="color:#f92672"&gt;&amp;lt;?&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt; T&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; observer);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice: instead of having the standard &lt;code&gt;Subscriber&lt;/code&gt; (Reactive Streams standard) the other base reactive classes (&lt;code&gt;Observable&lt;/code&gt;, &lt;code&gt;Single&lt;/code&gt; etc.) now have corresponding &amp;ldquo;special&amp;rdquo; Rx specific &lt;code&gt;Subscriber&lt;/code&gt; or event listeners called &amp;ldquo;Observer&amp;quot;s.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it for Part 1. In the next part, we&amp;rsquo;ll look at disposing subscriptions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;My thanks to David Karnok &amp;amp; Donn Felker for reviewing this post.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/rxjava1-rxjava2-migration-understanding-changes/</guid><pubDate>Wed, 21 Jun 2017 07:00:00 GMT</pubDate></item><item><title>
That Time I Met Andy Rubin</title><link>https://kau.sh/blog/that-time-i-met-andy-rubin/</link><description>
&lt;p&gt;One of the advantages of working in the Bay area is you tend to run in to tech celebrities every so often.&lt;/p&gt;
&lt;p&gt;I was working in Palo Alto (late 2015) on a previous startup (Wedding Party). My colleagues and I decided to get coffee at the nearby cafe. I saw &lt;a href="https://en.wikipedia.org/wiki/Andy_Rubin"&gt;Andy Rubin&lt;/a&gt; sitting outside casually talking to two other folks. I was certain it was him and told my buddies, &amp;ldquo;Hey, I think that&amp;rsquo;s Andy Rubin&amp;rdquo;. My colleagues (the lovable jerks that they were) said: &amp;ldquo;that&amp;rsquo;s the Android fanboy in you seeing things&amp;rdquo;. I obviously protested (with some casual bashing of their own phone choices).&lt;/p&gt;
&lt;p&gt;As we grabbed our lattes and headed out, one of my buddies told me, &amp;ldquo;if you&amp;rsquo;re so sure, why don&amp;rsquo;t you just go up to him and ask?&amp;rdquo;. To which I replied: &amp;ldquo;you know what? I bloody well will! just to prove you jerks wrong&amp;rdquo;. They thought I was being my kooky self and went back to the office.&lt;/p&gt;
&lt;p&gt;After making sure I wasn&amp;rsquo;t intruding his conversation, I walked up to him and said, &amp;ldquo;I hate to bother you but would you by any chance be Andy Rubin?&amp;rdquo;. Seeing the giant Android developer tee I was wearing, he let out a small smile and said, &amp;ldquo;that would be me! I&amp;rsquo;m Andy. How are you doing?&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I was so elated and star struck that speech began to fail me. I don&amp;rsquo;t recall exactly what I said but it was along the lines of &amp;ldquo;thank you so much for Android! Your work has made such a difference, more so than you can imagine, you&amp;rsquo;re awesome, i&amp;rsquo;ve seen some of the code you wrote, it&amp;rsquo;s also awesome&amp;hellip;etc etc (facepalm)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;He was incredibly humble and asked me to sit down and join him while we talked. He asked where I worked and what our startup was doing. You could tell the dude was incredibly smart; 5 minutes into the conversation, he had some really great questions, thoughts and insights. I was truly amazed how someone so incredibly intelligent and smart, could also be so humble and hold a conversation with a joe like me. It definitely left a mark.&lt;/p&gt;
&lt;p&gt;I walked back to the office and triumphantly told my colleagues they were dum-dums and I was totally right and I had the most awesomest conversation with Andy Rubin. They were pretty excited too and one of my pals, H asked:&lt;/p&gt;
&lt;p&gt;H: &amp;ldquo;Oh cool! so did you tell him you just started an Android developer podcast?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;me: &amp;ldquo;arr&amp;hellip; no, that&amp;hellip; oddly&amp;hellip; didn&amp;rsquo;t come up&amp;rdquo;&lt;/p&gt;
&lt;p&gt;H: &amp;ldquo;did you tell him that our last office was the one where he started Android?&amp;rdquo; (this was true and probably a fantastic conversation starter)&lt;/p&gt;
&lt;p&gt;me: &amp;ldquo;no&amp;hellip; but i told him where our current office is and what we did&amp;rdquo; (sheepish smile)&lt;/p&gt;
&lt;p&gt;H: &amp;ldquo;jeez KG! did you atleast tell him you were an Android developer?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;me: &amp;ldquo;i think he might have figured that out from our conversation&amp;hellip; or my tshirt&amp;rdquo;&lt;/p&gt;
&lt;p&gt;My colleagues were facepalming and laughing so hard that my announcement didn&amp;rsquo;t seem so triumphant after all. Sigh&amp;hellip; to be star struck.&lt;/p&gt;
&lt;p&gt;I still fondly remember the memory and am now fully armed with conversation points, for that next time I run into Andy.&lt;/p&gt;
&lt;p&gt;Andy Rubin &lt;a href="https://en.wikipedia.org/wiki/Essential_Phone"&gt;just announced Essential&lt;/a&gt; and I&amp;rsquo;m super excited to see where this goes.&lt;/p&gt;</description><guid>https://kau.sh/blog/that-time-i-met-andy-rubin/</guid><pubDate>Wed, 31 May 2017 07:00:00 GMT</pubDate></item><item><title>
New Year - 2017</title><link>https://kau.sh/blog/new-year-2017/</link><description>
&lt;p&gt;I know it&amp;rsquo;s almost the end of January but I like to take my time with these posts. You can take a look at &lt;a href="https://kau.sh/tags/new-year/"&gt;my previous year-end posts here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In addition to just jotting things I was most proud/happy about this year, I also want to note down some of my learnings. There were many overwhelming moments that led to much introspection. I want to try and document some of those moments here (atleast the less embarrassing ones). So here goes:&lt;/p&gt;
&lt;h1 id="stuff-i-did"&gt;
Stuff I did:
&lt;a class="heading-anchor" href="#stuff-i-did" aria-label="Link to Stuff I did:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="fun-stuff"&gt;
Fun stuff:
&lt;a class="heading-anchor" href="#fun-stuff" aria-label="Link to Fun stuff:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;met John Paul White : if you haven’t heard of the band &lt;a href="https://www.youtube.com/playlist?list=PLEUHxnpJL16K8MG1BEeDd3VXqKySs2veF"&gt;Civil Wars, I plead you take a listen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;learnt how to make my own latte (like an actual barista would). Given how big a coffee addict I am, this was bound to happen. Also landed up buying a Keurig machine for my new place &amp;gt;&amp;lt; (don&amp;rsquo;t judge me, it&amp;rsquo;s been the best investment ever)&lt;/li&gt;
&lt;li&gt;cleaned up the blog design yet again (did it in a Singapore lounge on an 8 hour layover)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="travels"&gt;
Travels:
&lt;a class="heading-anchor" href="#travels" aria-label="Link to Travels:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;made the pilgrimage to Disneyland again this year (twice). The night parade blew my socks away (twice!)&lt;/li&gt;
&lt;li&gt;hiked Grand Canyon with some buddies. Grand doesn&amp;rsquo;t even close to describing this place&lt;/li&gt;
&lt;li&gt;Went camping at Yellow Stone National Park. It was crazy fun and cold! (we didn&amp;rsquo;t anticipate the temperature drop and woke up one morning in our tent to subzero temperatures.. that part was not so fun)&lt;/li&gt;
&lt;li&gt;travelled to Sweden for the first time (my first time to Europe). I &lt;a href="https://kau.sh/ppt/"&gt;went to speak at a conference&lt;/a&gt; and the overall experience was simply amazing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="android-stuff"&gt;
Android stuff:
&lt;a class="heading-anchor" href="#android-stuff" aria-label="Link to Android stuff:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://fragmentedpodcast.com"&gt;Fragmented&lt;/a&gt; hit ep.70: this may not seem like a big deal by regular podcasting standards but Donn and I try very hard to keep Fragmented rich with content. Coming up with new interesting content every week is not as easy as you would think. But it&amp;rsquo;s been a crazy and extremely rewarding journey so far&lt;/li&gt;
&lt;li&gt;Fragmented joins the Spec network. It&amp;rsquo;s an amazing team and the collaboration really helps us move faster&lt;/li&gt;
&lt;li&gt;became a &lt;a href="https://developers.google.com/experts/people/kaushik-gopal"&gt;GDE&lt;/a&gt;. I&amp;rsquo;m honored and extremely grateful to have been recognized by Google for this&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/instacart/truetime-android"&gt;open sourced TrueTime&lt;/a&gt;: as part of a really interesting and challenging project at Instacart, we landed up open sourcing this library for Android. &lt;a href="https://tech.instacart.com/offline-first-introducing-truetime-for-swift-and-android-15e5d968df96#.i8gncu83i"&gt;Read more about it here&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="life-learnings"&gt;
Life learnings
&lt;a class="heading-anchor" href="#life-learnings" aria-label="Link to Life learnings"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="buffets-2-list-strategy"&gt;
Buffet&amp;rsquo;s 2 list strategy
&lt;a class="heading-anchor" href="#buffets-2-list-strategy" aria-label="Link to Buffet’s 2 list strategy"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I found myself constantly interested in a gazillion projects but struggled for time. I achieved a lot but just found myself more depressed cause I couldn&amp;rsquo;t finish everyting on my admittedly unrealistic list. I then came across this fantastic &lt;a href="http://jamesclear.com/buffett-focus"&gt;post by James Clear on Warren Buffett&amp;rsquo;s &amp;ldquo;2 list&amp;rdquo; strategy&lt;/a&gt; that explained how Warrent Buffett tackled this problem. It was eye opening and I now follow a similar routine to really whittle down my wishlist of projects.&lt;/p&gt;
&lt;h2 id="highly-polarized-decisions"&gt;
Highly polarized decisions
&lt;a class="heading-anchor" href="#highly-polarized-decisions" aria-label="Link to Highly polarized decisions"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;I knew we were on to something because it was so polarizing&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Picked this up in &lt;a href="http://pca.st/MgSK#t=530"&gt;the Presentable podcast&lt;/a&gt;. To paraphrase what&amp;rsquo;s said: You know you&amp;rsquo;re treading the right path if the decisions you take are highly polarized. If everyone is meh, that’s the most dangerous thing for your product. But if you have two groups both feeling very strongly about something, then you know you’re getting somewhere.&lt;/p&gt;
&lt;h2 id="on-changing-your-mind"&gt;
On changing your mind
&lt;a class="heading-anchor" href="#on-changing-your-mind" aria-label="Link to On changing your mind"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Anybody who doesn’t change their mind a lot is dramatically underestimating the complexity of the world we live in&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jeff Bezos&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;by way of &lt;a href="https://twitter.com/david_perell/status/792747921473171456"&gt;this tweet&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="mindfulness-and-observation"&gt;
Mindfulness and observation
&lt;a class="heading-anchor" href="#mindfulness-and-observation" aria-label="Link to Mindfulness and observation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Mindfulness and keen observation just makes you a smarter person. One of my buddies is a top notch designer and all around super smart person. I noticed this quality about her and i&amp;rsquo;m convinced there&amp;rsquo;s a strong correlation between smarts and a good power of observation. If anything, being more aware just gives you the ammo to have smarter conversations.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an interesting exercise for you folks: if I asked you to draw the super ubiquitous USB symbol, how accurately would you be able to draw it? You&amp;rsquo;ve seen it countless times but have you stopped to observe that there&amp;rsquo;s actually a triangle, square and circle on each of the spokes of the trident? do you know what it signifies?&lt;/p&gt;
&lt;h2 id="being-happy-and-enjoying-your-life"&gt;
Being happy and enjoying your life
&lt;a class="heading-anchor" href="#being-happy-and-enjoying-your-life" aria-label="Link to Being happy and enjoying your life"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I need to keep reminding myself of this more:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the end, only three things matter:&lt;/p&gt;
&lt;p&gt;how much you loved,&lt;/p&gt;
&lt;p&gt;how gently you lived and&lt;/p&gt;
&lt;p&gt;how gracefully you let go of things not meant for you&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Buddha&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I draw my learning not just from scriptures but also the sanctimonious art form of TV: while watching a rerun of Boston Legal (James Spader at his best btw) I noticed this phrase, which just made me happy:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;wake up every morning, with all the promise that morning conveys&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By way of an anecdote that Matt Damon mentioned, in an interview:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It’s easy to be younger, just make time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stephen Hawking&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;h1 id="software-engineering-learnings"&gt;
Software Engineering learnings
&lt;a class="heading-anchor" href="#software-engineering-learnings" aria-label="Link to Software Engineering learnings"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;&lt;em&gt;These are more pertinent to software engineers and developers&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="yishan-tenets-of-growing-a-great-engineering-organization"&gt;
&lt;a href="http://algeri-wong.com/yishan/engineering-management.html"&gt;Yishan tenets of growing a great engineering organization&lt;/a&gt;
&lt;a class="heading-anchor" href="#yishan-tenets-of-growing-a-great-engineering-organization" aria-label="Link to Yishan tenets of growing a great engineering organization"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I came across this article by Yishan (an ex Engineering Manager at Facebook). If you&amp;rsquo;re looking for advice and tips on being a better software leader, definitely &lt;a href="http://algeri-wong.com/yishan/engineering-management.html"&gt;read this article&lt;/a&gt;. Key takeaways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Hiring is number one&lt;/li&gt;
&lt;li&gt;Let process be implemented by those who practice it&lt;/li&gt;
&lt;li&gt;Promote from within&lt;/li&gt;
&lt;li&gt;&lt;a href="http://algeri-wong.com/yishan/engineering-management-tools-are-top-priority.html"&gt;Tools are top priority (devs should really read this one)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="10-modern-software-engineering-mistakes"&gt;
&lt;a href="https://medium.com/@rdsubhas/10-modern-software-engineering-mistakes-bc67fbef4fc8#.afede6t1i"&gt;10 modern software engineering mistakes&lt;/a&gt;
&lt;a class="heading-anchor" href="#10-modern-software-engineering-mistakes" aria-label="Link to 10 modern software engineering mistakes"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This was a great read and honestly more a reaffirmation of some core technical beliefs. Keep learning and adapting.&lt;/p&gt;
&lt;h2 id="importance-of-vacation"&gt;
Importance of vacation
&lt;a class="heading-anchor" href="#importance-of-vacation" aria-label="Link to Importance of vacation"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Most of my career has been in startup land and I enjoy every moment of it. But make no mistake, it takes a toll. You&amp;rsquo;re driven by the energy around you and if you happen to have great colleagues, the excitement is compounded. I hadn&amp;rsquo;t taken a proper vacation in almost 2 and half years. I totally could have, but just never felt it was the right time. But I decided to take a good 3 weeks off for the holidays this time, and it was absolutely fabulous. I met up with old friends and made so many new ones.&lt;/p&gt;
&lt;p&gt;Most importantly, I came back fresh and my productivity was 2x what it was. Definitely going to be slotting in proper vacation time moving forward.&lt;/p&gt;
&lt;p&gt;I was reminded of this story about Pixar:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The most powerful example dates back to the making of Toy Story 2 in 1999. With only seven months to go before its scheduled release date, Pixar’s creative leadership felt that Toy Story 2 was not working creatively. Disney, Pixar’s distribution partner, knew that it took 3-4 years to make an animated film. They argued that it was too late to start over and that Pixar should release the film as is. But Pixar refused, deciding instead to tear up the story and re-write it from scratch. The studio pushed itself to the brink of collapse to complete the new version of Toy Story 2 on time, and the film was a huge commercial and critical success. And, when it was completed, the executive team took the extraordinary step of closing the studio for two entire months to let everyone recuperate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;2016 was by far my most action packed year. 2017 has started with much trepidation and fear. But if there&amp;rsquo;s one thing these years have taught me, it&amp;rsquo;s never to lose hope and always to keep charging forward.&lt;/p&gt;
&lt;p&gt;Onward and upward peeps!&lt;/p&gt;</description><guid>https://kau.sh/blog/new-year-2017/</guid><pubDate>Tue, 31 Jan 2017 07:00:00 GMT</pubDate></item><item><title>
What I learnt using the Presenter pattern</title><link>https://kau.sh/ppt/what-i-learnt-using-the-presenter-pattern/</link><description>
&lt;p&gt;We recently did an overhaul for Instacart&amp;rsquo;s shopper app and committed to using a presenter pattern for this. It turned out awesome!
Presenters are not a new concept. They&amp;rsquo;ve been around since the dawn of software engineering time. But the devil is in the implementation details. If you have an Activity, RecyclerView, Adapter etc. at how many levels would you have the presenter? How does the use of presenters enabled super fast testing? How does the use of presenters enable constantly changing user requirements?
We&amp;rsquo;ll discuss all the juicy war stories in this session&lt;/p&gt;
&lt;div
style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe
src="https://player.vimeo.com/video/191068122?dnt=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="9bc3d348bc7b4439ac31d6acc0639b05" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/what-i-learnt-using-the-presenter-pattern/</guid><pubDate>Thu, 10 Nov 2016 00:00:00 GMT</pubDate></item><item><title>
Learning Rx by Example (Part 2)</title><link>https://kau.sh/ppt/learning-rx-by-example-part-2/</link><description>
&lt;div data-callout-metadata="" data-callout-fold="" data-callout="fyi" class="callout"&gt;
&lt;div class="callout-icon"&gt;
&lt;svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="svg-icon lucide-info"&gt;&lt;circle cx="12" cy="12" r="10"&gt;&lt;/circle&gt;&lt;path d="M12 16v-4"&gt;&lt;/path&gt;&lt;path d="M12 8h.01"&gt;&lt;/path&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;div class="callout-content"&gt;
&lt;p&gt;This is a revised version of a &lt;a href="https://kau.sh/ppt/learning-rx-by-example-part-1/"&gt;talk I first gave in 2015&lt;/a&gt;.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div
style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe
src="https://player.vimeo.com/video/190922794?dnt=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" allow="fullscreen"&gt;
&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="26b6a98d0e6e4642ac8dfbd3b8a14f7f" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/learning-rx-by-example-part-2/</guid><pubDate>Wed, 09 Nov 2016 00:00:00 GMT</pubDate></item><item><title>
New Year - 2016</title><link>https://kau.sh/blog/new-year-2016/</link><description>
&lt;p&gt;I finished 2014 not having the slightest clue what would be in store. 2015 was a rollercoaster:&lt;/p&gt;
&lt;p&gt;I started a new podcast &lt;a href="http://fragmentedpodcast.com/about/"&gt;Fragmented&lt;/a&gt; (with my cohost and now friend Donn Felker), bought my childhood dream car (a Mini Cooper), got a new job (Wedding Party was acquired by Instacart), visited NY for the first time (for a &lt;a href="https://speakerdeck.com/kaushikgopal/painless-ui-testing"&gt;talk&lt;/a&gt; I gave at DroidCon NYC) and finally - this one really stumped me - moved to San Francisco.&lt;/p&gt;
&lt;p&gt;The new year brings in a sea of sweeping change. With it I gain a little insight about myself and the world (yes, even the goofball that you see has been chiseled with each passing year). There were lows but we should focus on the highs.&lt;/p&gt;
&lt;p&gt;Life is good, people can be amazing, let&amp;rsquo;s surprise ourselves and here&amp;rsquo;s to a peaceful and amazing 2016!&lt;/p&gt;</description><guid>https://kau.sh/blog/new-year-2016/</guid><pubDate>Fri, 01 Jan 2016 07:00:00 GMT</pubDate></item><item><title>
Painless UI Testing</title><link>https://kau.sh/ppt/painless-ui-testing/</link><description>
&lt;p&gt;What if you could get your UI tests to run as fast as your unit tests?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;you would write more tests&lt;/li&gt;
&lt;li&gt;you would feel happy writing the tests&lt;/li&gt;
&lt;li&gt;your managers would be pleased with you not goofing around with that new testing framework around the corner&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using patterns of yore (like Martin Fowler&amp;rsquo;s supervising controller, effective use of presenters and view model state) we&amp;rsquo;re going to tackle everyday-real-annoying impediments to UI testing. We&amp;rsquo;ll address what parts of the UI need testing and effective ways of testing them.&lt;/p&gt;
&lt;p&gt;The objective of the talk is to prove to the world that the title of this talk is not an oxymoron.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/ZzXN6iV26wg?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="889a91b23088486b83cd7d868fe7e75c" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/painless-ui-testing/</guid><pubDate>Fri, 28 Aug 2015 00:00:00 GMT</pubDate></item><item><title>
A note about the warmth of the share and replay operators</title><link>https://kau.sh/blog/a-note-about-the-warmth-share-operator/</link><description>
&lt;p&gt;A common question most android developers have when using RxJava is: how do I cache or persist the work done by my observables over a configuration change? If you start a network call and the user decides to rotate the screen, the work spent in executing that network call would have been in vain (considering the OS would re-create your activity).&lt;/p&gt;
&lt;p&gt;There are two widely accepted solutions to this problem:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;store the observable somewhere as a singleton, possibly apply the &lt;a href="https://github.com/ReactiveX/RxJava/wiki/Observable-Utility-Operators"&gt;cache&lt;/a&gt; operator and re-subscribe on activity recreation&lt;/li&gt;
&lt;li&gt;house your observable in a retained &amp;ldquo;worker&amp;rdquo; fragments &lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I &lt;a href="https://github.com/kaushikgopal/RxJava-Android-Samples#12-persist-data-on-activity-rotations-using-subjects-and-retained-fragments"&gt;whipped up a quick example&lt;/a&gt; on &lt;a href="https://github.com/kaushikgopal/RxJava-Android-Samples"&gt;github&lt;/a&gt; to demonstrate the second technique (which is generally my choice of poison). Now I&amp;rsquo;ve used the technique of worker fragments (successfully) a bunch of times before so I was a little surprised to see the example not work.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s go over the use case again:&lt;/p&gt;
&lt;p&gt;You have an observable that executes a long running network call. Before the call completes, you perform an activity rotation. After the activity is recreated, you continue the network call from where it left off or just use the result if it completes before your activity recreation process.&lt;/p&gt;
&lt;p&gt;Instead of simulating this network call use case I decided to just fake it with a &amp;ldquo;hot&amp;rdquo; observable instead (which makes the use case a tad bit different but would help demonstrate the solution equally well). If you&amp;rsquo;re looking for a quick simple example that demonstrates the difference between a hot and cold observable, I strongly recommend watching this &lt;a href="https://egghead.io/lessons/rxjs-demystifying-cold-and-hot-observables-in-rxjs"&gt;egghead.io video on the subject&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In fact, I used the exact same concoction of operators for my example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;interval&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Func1&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Long, Integer&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Integer &lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(Long aLong) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; aLong.&lt;span style="color:#a6e22e"&gt;intValue&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;take&lt;/span&gt;(20)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A little more investigation revealed that the share operator I used to fake the source stream was not really hot but &amp;ldquo;warm&amp;rdquo;. This is easier explained with Marble diagrams:&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how we expect share to behave (and it does):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diag_share_1.jpg" alt="share marble diag 1" title="Marble Diagram share 1"&gt;&lt;/p&gt;
&lt;p&gt;But owing to the activity recreation, what really happens is that the first subscriber (S1) unsubscribes from the source observable (O1 - housed in the worker fragment), after which a similar subscriber (S2) from the recreated activity subscribes again to O1 from the same worker fragment. So the marble diagram really lands up looking like this:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diag_share_2.jpg" alt="share marble diag 2" title="Marble Diagram share 2"&gt;&lt;/p&gt;
&lt;p&gt;Notice that re-subscription? That changes things a little.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diag_share_3.jpg" alt="share marble diag 3" title="Marble Diagram share 3"&gt;&lt;/p&gt;
&lt;p&gt;In this way, the share operator is &amp;ldquo;warm&amp;rdquo;. It behaves cold to first time subscribers but hot to subsequent ones.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;the share operator behaves cold to first time subscribers but hot to subsequent ones&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="epilogue"&gt;
Epilogue:
&lt;a class="heading-anchor" href="#epilogue" aria-label="Link to Epilogue:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;So how did I circumvent the problem? I added a fake subscriber that never unsubscribes.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storeObservable &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Observable.&lt;span style="color:#a6e22e"&gt;interval&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;map&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Func1&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Long, Integer&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Integer &lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(Long aLong) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; aLong.&lt;span style="color:#a6e22e"&gt;intValue&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;take&lt;/span&gt;(20)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Do not do this in production!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// this is a hack!&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;storeObservable &lt;span style="color:#f92672"&gt;=&lt;/span&gt; storeObservable.&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Clever but very hacky.&lt;sup id="fnref:2"&gt;&lt;a href="#fn:2" class="footnote-ref" role="doc-noteref"&gt;2&lt;/a&gt;&lt;/sup&gt; Think carefully before you write code like this in production.&lt;/p&gt;
&lt;h2 id="epilogue-part-2"&gt;
Epilogue (Part 2):
&lt;a class="heading-anchor" href="#epilogue-part-2" aria-label="Link to Epilogue (Part 2):"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://twitter.com/fabioCollini/status/620664072770592768"&gt;@fabioCollini&lt;/a&gt; on twitter said he preferred a replay().connect() approach over the publish().refcount() one which is &lt;a href="../rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/"&gt;what the share operator really is&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This reminded me of a tip that &lt;a href="https://twitter.com/danlew42"&gt;Dan&lt;/a&gt; mentioned once, replay is similar to share in that it’s hot to the first subscriber and cold to every other subscriber after the first item emits.&lt;sup id="fnref:3"&gt;&lt;a href="#fn:3" class="footnote-ref" role="doc-noteref"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;replay is hot to the first subscriber and cold to every other subscriber after the first item emits&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because marble diagrams are amazing:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diagram_replay.001.jpg" alt="replay marble diag 1" title="Marble Diagram replay 1"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diagram_replay.002.jpg" alt="replay marble diag 1" title="Marble Diagram replay 2"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/marble_diagram_replay.003.jpg" alt="replay marble diag 1" title="Marble Diagram replay 3"&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve since &lt;a href="https://github.com/kaushikgopal/RxJava-Android-Samples/commit/1812c18064a1a508b3d704be21137b1e3ab868f0?diff=split"&gt;modified the example to use a ConnectableObservable &lt;/a&gt; instead and it works pretty much the same way.&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;Alex Lockwood wrote about &lt;a href="http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html"&gt;this approach&lt;/a&gt; here.&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;I can&amp;rsquo;t take credit for this idea. The comments in the code reveal the identity of the troublemaker.&amp;#160;&lt;a href="#fnref:2" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;Depending on what it’s replaying, it could also be cold to the first subscriber (he is quick to point out)&amp;#160;&lt;a href="#fnref:3" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/a-note-about-the-warmth-share-operator/</guid><pubDate>Sat, 11 Jul 2015 07:00:00 GMT</pubDate></item><item><title>
Ken Burns: On Story</title><link>https://kau.sh/blog/ken-burns-on-story/</link><description/><guid>https://kau.sh/blog/ken-burns-on-story/</guid><pubDate>Wed, 17 Jun 2015 07:00:00 GMT</pubDate></item><item><title>
Learning Rx by Example (Part 1)</title><link>https://kau.sh/ppt/learning-rx-by-example-part-1/</link><description>
&lt;p&gt;So RxJava is this new kid on the block. But what can you the Android developer really use it for today? We&amp;rsquo;ll look at everyday use cases and solve them with RxJava.&lt;/p&gt;
&lt;p&gt;Remember that first day when you tasted Nutella? RxJava is Nutella for Android… everything is awesome with RxJava.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe src="https://www.youtube-nocookie.com/embed/k3D0cWyNno4?rel=0}&amp;start=0&amp;end=0"
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;"
allowfullscreen=""&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id="slides"&gt;
Slides
&lt;a class="heading-anchor" href="#slides" aria-label="Link to Slides"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;script async class="speakerdeck-embed" data-id="80b39d4e61eb47af817549ec3d366377" data-ratio="1.33333333333333" src="//speakerdeck.com/assets/embed.js"&gt;&lt;/script&gt;</description><guid>https://kau.sh/ppt/learning-rx-by-example-part-1/</guid><pubDate>Thu, 04 Jun 2015 00:00:00 GMT</pubDate></item><item><title>
The Audio Latency Problem on Android</title><link>https://kau.sh/blog/the-audio-latency-problem-on-android/</link><description>
&lt;p&gt;when &lt;a href="https://www.youtube.com/watch?v=cU-eAzNp5Hw"&gt;Billy Joel was on the Jimmy Falon show&lt;/a&gt; and they used the &lt;a href="http://loopyapp.com/"&gt;iPad app &amp;ldquo;Looper&amp;rdquo;&lt;/a&gt;, i immediately looked for the android equivalent. there wasn&amp;rsquo;t one. i&amp;rsquo;m an android developer and a sound engineer (enthusiast) so obviously the right thing to do, was build one&amp;hellip;&lt;/p&gt;
&lt;p&gt;i spent about two weeks and then gave up on the project cause it demanded far more time than i initially anticipated. the linked article is a &lt;a href="https://superpowered.com/androidaudiopathlatency"&gt;fantastic read and summary&lt;/a&gt; on the problems with audio latency in android (and why i eventually just gave up on that project).&lt;/p&gt;</description><guid>https://kau.sh/blog/the-audio-latency-problem-on-android/</guid><pubDate>Thu, 16 Apr 2015 07:00:00 GMT</pubDate></item><item><title>
Fragmented Podcast - Episode #2 : Android Studio</title><link>https://kau.sh/blog/fragmented-episode-2-is-out/</link><description>
&lt;p&gt;hear ye, hear ye! the 2nd episode of the &lt;a href="http://fragmentedpodcast.com/"&gt;Fragmented Podcast&lt;/a&gt; is out. &lt;a href="http://www.donnfelker.com/"&gt;Donn&lt;/a&gt; and i start by discussing Lamas, blue dresses and IDEs for Android development!&lt;/p&gt;</description><guid>https://kau.sh/blog/fragmented-episode-2-is-out/</guid><pubDate>Tue, 03 Mar 2015 07:00:00 GMT</pubDate></item><item><title>
Rx Is Coming</title><link>https://kau.sh/blog/rx-is-coming/</link><description>
&lt;p&gt;i spent a huge part of my 2014 on &amp;ldquo;&lt;a href="https://github.com/mono/rx"&gt;Rx&lt;/a&gt;&amp;rdquo; (or reactive extensions) which is essentially a library that helps with a development pattern called &amp;ldquo;reactive programming&amp;rdquo;. i think Rx is going to be huge in the app development world. it&amp;rsquo;s already picked up a lot of steam, but i think it&amp;rsquo;s going to become a &lt;strong&gt;staple&lt;/strong&gt; for professional app developers.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;consider this extremely common scenario&lt;/em&gt;:&lt;/p&gt;
&lt;p&gt;you&amp;rsquo;re in &lt;a href="http://en.wikipedia.org/wiki/Burundi"&gt;Burundi&lt;/a&gt; surfing facebook. you want to get the latest updates from facebook, so your smart phone (from a network in Burundi) shoots a request to the facebook servers in Menlo Park. that server reconciles your request and sends back 5 epic selfies from your dearest friends. in our universe, this takes time. maybe not a whole lot according to you (a second perhaps) but for that mini super-computer you hold in your hand, that&amp;rsquo;s 1000000 microseconds. that&amp;rsquo;s a long time that you&amp;rsquo;re asking it to twiddle its thumbs (given that it usually processes stuff in a couple of microseconds). what&amp;rsquo;s it supposed to do until it hears back from the facebook servers? after it&amp;rsquo;s done showing you the updates for your first screen, when would it know to trigger another update? what if the data that came back from the servers indicated that you need some more information? what if the third request in that chain failed for some reason? welcome to the world of asynchronous programming. Rx (or FRP as it is mistakingly assumed to be synonymous with) was built to deal with a lot of this pain.&lt;/p&gt;
&lt;p&gt;a natural question is: don&amp;rsquo;t app developers do this anyway today? yes we do. but it&amp;rsquo;s painful and error prone. it also results in bad UX (spinners, progress indicators, alert dialogs for failed network issues and other technical problems that users really shouldn&amp;rsquo;t have to deal with).&lt;/p&gt;
&lt;p&gt;you can get by very far without having to deal with many of the aforementioned problems (like keeping all your information saved on your device instead of say a big server machine located elsewhere that would require a network connection) but as more services move to storing data online, streaming data from servers, sharing data across multiple platforms and devices, dealing with the complexity of asynchronous programming has become common place.&lt;/p&gt;
&lt;p&gt;how is it then, that every developer you know, isn&amp;rsquo;t already talking about this Rx stuff? the reception to Rx (&lt;a href="http://en.wikipedia.org/wiki/Rx"&gt;see what i did there?&lt;/a&gt;) in the developer community has been &amp;hellip;warm. the few serious developers who have started to use it, swear by it and say it&amp;rsquo;s the most amazing thing ever. the ones that are yet to go through this feat, currently see it as another fancy hipster developer pattern. it also has a very steep learning curve. this doesn&amp;rsquo;t help its cause (did i mention it happens to be a pattern &lt;a href="https://msdn.microsoft.com/en-us/data/gg577609.aspx"&gt;pioneered by Microsoft&lt;/a&gt;?).&lt;/p&gt;
&lt;p&gt;but make no mistake, Rx is coming. eventually everyone is going to suck it up and use it in its current form, or the dev community will fashion a variant that&amp;rsquo;s easier for newer developers to pick up.&lt;/p&gt;</description><guid>https://kau.sh/blog/rx-is-coming/</guid><pubDate>Mon, 02 Mar 2015 07:00:00 GMT</pubDate></item><item><title>
RxJava Tip for the Day - Share, Publish, Refcount and All That Jazz</title><link>https://kau.sh/blog/rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/</link><description>
&lt;p&gt;Ok, so in &lt;a href="../debouncedbuffer-with-rxjava/"&gt;my previous post&lt;/a&gt; I innocuously introduced the &lt;code&gt;.share()&lt;/code&gt; operator.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="what-is-this-share-operator"&gt;
What is this share operator?
&lt;a class="heading-anchor" href="#what-is-this-share-operator" aria-label="Link to What is this share operator?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;!-- more --&gt;
&lt;p&gt;The &lt;code&gt;.share()&lt;/code&gt; operator is basically just &lt;a href="https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators"&gt;a wrapper to the chained call &lt;code&gt;.publish().refcount()&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ll find the chained combo &lt;code&gt;.publish().refcount()&lt;/code&gt; used in quite a few Rx examples on the web. It allows you to &amp;ldquo;share&amp;rdquo; the emission of the stream. Considering how powerpacked and frequently used this combo is, RxJava basically introduced the friendlier more useful operator &lt;code&gt;share()&lt;/code&gt;. This mechanism is sometimes referred to as &amp;ldquo;multicasting&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s dig into some of the basics first:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;&lt;a href="https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators"&gt;ConnectedObservable&lt;/a&gt;&amp;rdquo; - This is a kind of observable which doesn&amp;rsquo;t emit items even if subscribed to. It only starts emitting items after its &lt;code&gt;.connect()&lt;/code&gt; method is called.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;It is for this reason that a connected obesrvable is also considered &amp;ldquo;cold&amp;rdquo; or &amp;ldquo;inactive&amp;rdquo; before the connect method is invoked.&lt;/em&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;.publish()&lt;/code&gt;- This method allows us to change an ordinary observable into a &amp;ldquo;ConnectedObservable&amp;rdquo;. Simply call this method on an ordinary observable and it becomes a connected one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We now know what 1/2 of the operator &lt;code&gt;share&lt;/code&gt; does. Now why would you ever use a Connected Observable? &lt;a href="https://github.com/ReactiveX/RxJava/wiki/Connectable-Observable-Operators"&gt;The docs say&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In this way you can wait for all intended Subscribers to subscribe to the Observable before the Observable begins emitting items.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This essentially means that a regular usecase for &lt;code&gt;publish&lt;/code&gt; would involve more than one subscriber. When you have more than one subscriber, it can get tricky to handle each of the subscriptions and dispose them off correctly. To make this process easier, Rx introduced this magical operator called &lt;code&gt;refcount()&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;refcount()&lt;/code&gt; - This operator keeps track of how many subscribers are subscribed to the resulting Observable and refrains from disconnecting from the source ConnectedObservable until all such Observables are unsubscribed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It essentially maintains a reference counter in the background and accordingly takes the correct action when a subscription needs to be unsubscribed or disposed off. This is the second 1/2 of the operator &lt;code&gt;share&lt;/code&gt;. You are now armed with knowledge of what each of those terms mean.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s look at the example from debouncedBuffer again and see how &lt;code&gt;share&lt;/code&gt; was used there:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// which is really the same as:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;publish&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;refcount&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We now have a &amp;ldquo;shareable&amp;rdquo; observable called &amp;ldquo;tapEventEmitter&amp;rdquo; and because it&amp;rsquo;s sharable and still not yet &amp;rsquo;live&amp;rsquo; (&lt;code&gt;publish&lt;/code&gt; from the &lt;code&gt;share&lt;/code&gt; call changes it to a ConnectedObservable), we can use it to compose our niftier Observables and rest assured that we always have a reference to the original observable (the original observable being &lt;code&gt;_rxBus.toObserverable()&lt;/code&gt; in this case).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; debouncedEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tapEventEmitter.&lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tapEventEmitter.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(debouncedEventEmitter)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All this sounds good. There is however a possible race condition with this implementation (which &lt;a href="https://gist.github.com/benjchristensen/e4524a308456f3c21c0b#comment-1367814"&gt;Ben pointed out through a comment on this gist&lt;/a&gt;). The race condition occurs because there are two subscribers here (debounce and buffer) and they may come and go at different points. Remember that the RxBus is backed by a hot/live Subject which is constantly emitting items. By using the &lt;code&gt;share&lt;/code&gt; operator we guarantee a reference to the same source, but NOT that they&amp;rsquo;ll receive the exact same items if the subscribers enter at different points of time. Ben explains this well:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The race condition is when the two consumers subscribe. Often on a hot stream it doesn&amp;rsquo;t matter when subscribers come and go, and refCount is perfect for that. The race condition refCount protects against is having only 1 active subscription upstream. However, if 2 Subscribers subscribe to a refcounted stream that emits 1, 2, 3, 4, 5, the first may get 1, 2, 3, 4, 5 and the second may get 2, 3, 4, 5.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;To ensure all subscribers start at exactly the same time and get the exact same values, refCount can not be used. Either ConnectableObservable with a manual, imperative invocation of connect needs to be done, or the variant of publish(function) which connects everything within the function before connecting the upstream.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In our usage it&amp;rsquo;s almost immediate so it probably wouldn&amp;rsquo;t matter a whole lot but considering the use case, it&amp;rsquo;s ideal to have the exact same events emitted for the debouncedBuffer usecase. &lt;a href="https://github.com/kaushikgopal/Android-RxJava/blob/master/app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom3Fragment.java"&gt;I added a third improved implementation to handle this race condition&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// don&amp;#39;t start emitting items just yet by turning the observable to a connected one&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ConnectableObservable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;publish&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tapEventEmitter.&lt;span style="color:#a6e22e"&gt;publish&lt;/span&gt;((Func1) (stream) &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// inside `publish`, &amp;#34;stream&amp;#34; is truly multicasted&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// applying the same technique for getting a debounced buffer sequence&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; stream.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(stream.&lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}).&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;((Action1) (taps) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _showTapCount(taps.&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// start listening to events now&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;tapEventEmitter.&lt;span style="color:#a6e22e"&gt;connect&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description><guid>https://kau.sh/blog/rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/</guid><pubDate>Wed, 21 Jan 2015 07:00:00 GMT</pubDate></item><item><title>
Interested in an Android Developer Podcast?</title><link>https://kau.sh/blog/interested-in-an-android-developer-focused-podcast/</link><description>
&lt;p&gt;i listen to a tonne of podcasts. i find activities such as doing my laundry, driving back home from work, doing the dishes &amp;hellip; all delightful because it gives me a chance to strap on some headphones and listen to a bunch of podcasts. in the time that i run those dreadfully boring chores i&amp;rsquo;ve found myself learning so much about technology and good software development.&lt;/p&gt;
&lt;p&gt;as i was driving back home today i was listening to an &lt;a href="https://www.youtube.com/watch?v=T5e9h4poAvY"&gt;episode of Ruby Rogues&lt;/a&gt; and thought to myself:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;why aren&amp;rsquo;t there more podcasts like this for Android?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;the iOS folks have &lt;a href="http://www.coreint.org"&gt;some&lt;/a&gt; amazing &lt;a href="http://developingperspective.com"&gt;podcasts&lt;/a&gt; focused for the developer audience. heck, i listen to two &lt;a href="http://www.imore.com/debug"&gt;of&lt;/a&gt; &lt;a href="http://atp.fm"&gt;them&lt;/a&gt; and i&amp;rsquo;m not an iOS developer (granted they&amp;rsquo;re slightly more generic).&lt;/p&gt;
&lt;p&gt;as an android developer there is exactly one show today that is focused on android development called &amp;ldquo;&lt;a href="http://androidbackstage.blogspot.com"&gt;Android Developers Backstage&lt;/a&gt;&amp;rdquo;. it&amp;rsquo;s hosted by google engineers Chet Hasse and Tor Norbye (both of them work directly with Android). they&amp;rsquo;re funny and the podcast is amazing! if you&amp;rsquo;re an android developer you should absolutely be listening to it.&lt;/p&gt;
&lt;p&gt;my only gripe with the show is that they live in their &lt;em&gt;beautiful google bubble&lt;/em&gt;. on a recent show they &lt;a href="http://androidbackstage.blogspot.com/2014/08/android-developers-backstage-episode-11.html"&gt;interviewed Jake Wharton&lt;/a&gt; (a.k.a superman android dev) and i found it really interesting that they had genuinely not heard of some of the libraries that Jake works on (like &lt;a href="http://square.github.io/picasso/"&gt;Picasso&lt;/a&gt;, &lt;a href="http://square.github.io/retrofit/"&gt;Retrofit&lt;/a&gt; and &lt;a href="http://jakewharton.github.io/butterknife/"&gt;ButterKnife&lt;/a&gt;). i guess it makes sense though: they&amp;rsquo;re google engineers working on android directly. if they really needed something, they would have had it baked in.&lt;/p&gt;
&lt;p&gt;but i don&amp;rsquo;t think i&amp;rsquo;m alone when i say that for some of us android developers, those libraries are &lt;strong&gt;indispensable&lt;/strong&gt; for pushing out quality android apps quickly.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;i want to hear more about such libraries.&lt;/li&gt;
&lt;li&gt;what are some of your learnings as an android developer that you would love to share?&lt;/li&gt;
&lt;li&gt;what are some tricks and techniques you have in your utility belt?&lt;/li&gt;
&lt;li&gt;can you tell me about some android apps that are top notch and push the boundary in terms of quality and design?
&lt;ul&gt;
&lt;li&gt;what are some interesting things they do?&lt;/li&gt;
&lt;li&gt;how do they do it?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;i&amp;rsquo;ve been toying with the idea of starting an android podcast with a developer audience in mind. i&amp;rsquo;ve done a decent amount of sound-engineering in my day so the audio editing part isn&amp;rsquo;t daunting. it&amp;rsquo;s the whole planning around the actual podcast that sort of worries me. i&amp;rsquo;ll have to find developers to interview, do my homework before interviewing them, setup the environment and whole stack for the podcast, find a place to host the podcast and a whole bunch of other things that i can&amp;rsquo;t think of right now; all in addition to my regular job (which i also absolutely love).&lt;/p&gt;
&lt;p&gt;it&amp;rsquo;s clearly a lot of work and will require a solid chunk of my free time. i don&amp;rsquo;t intend to make any money out of it so i&amp;rsquo;ll probably have to bear the costs myself intially (i really don&amp;rsquo;t mind that part at all though cause i&amp;rsquo;ll definitely be learning along the way). the effort only makes sense though if a larger group of people can benefit from this.&lt;/p&gt;
&lt;p&gt;i&amp;rsquo;m trying to gauge interest and will explore the specifics only if people seem interested. so i ask you: &lt;a href="https://polldaddy.com/poll/8574924/"&gt;would you genuinely be interested in a developer-focused android podcast? (let me know by answering this poll)&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[update: many people were interseted, so i &lt;a href="http://www.donnfelker.com/announcing-fragmented-podcast-android-developers/"&gt;teamed up with the amazing Donn Felker&lt;/a&gt; to start &amp;ldquo;&lt;a href="http://fragmentedpodcast.com/"&gt;Fragmented - An Android developer podcast&lt;/a&gt;&amp;rdquo;. &lt;a href="http://twitter.com/kaushikgopal"&gt;Let us know what you think&lt;/a&gt;.]&lt;/p&gt;</description><guid>https://kau.sh/blog/interested-in-an-android-developer-focused-podcast/</guid><pubDate>Sun, 11 Jan 2015 07:00:00 GMT</pubDate></item><item><title>
DebouncedBuffer With RxJava</title><link>https://kau.sh/blog/debouncedbuffer-with-rxjava/</link><description>
&lt;p&gt;This is a bonus RxJava post that I landed up writing along with my &lt;a href="../implementing-an-event-bus-with-rxjava-rxbus"&gt;previous post on creating an event bus with RxJava&lt;/a&gt;. If you went through the &lt;a href="https://github.com/kaushikgopal/Android-RxJava/tree/master/app/src/main/java/com/morihacky/android/rxjava/rxbus"&gt;code in the actual repo&lt;/a&gt; you would have noticed more than one version of the bottom fragment in the RxBus demo.&lt;/p&gt;
&lt;p&gt;Originally I envisioned the RxBus example being a tad bit fanicer however as I coded up the example, I realized that too many concepts were getting conflated. The ridiculous simplicity of the RxBus implementation was lost. So I dumbed down the original example but left in the original code for the Rx padawans.&lt;/p&gt;
&lt;p&gt;Original example:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/rxbus_simple.gif" alt="Simple RxBus example"&gt;&lt;/p&gt;
&lt;p&gt;Fancier one:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/rxbus_fancy.gif" alt="Fancy RxBus example"&gt;&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;The fanciness is basically in the numbers being accumulated and shown in &amp;ldquo;chunks&amp;rdquo;. In my head I thought I could simply use the &lt;code&gt;debounce&lt;/code&gt; operator (like the &lt;a href="https://github.com/kaushikgopal/RxJava-Android-Samples/blob/master/app/src/main/java/com/morihacky/android/rxjava/fragments/DebounceSearchEmitterFragment.java"&gt;debounce search example&lt;/a&gt;) and be on my jolly way, but this was not to be&amp;hellip; From the always helpful &lt;a href="https://github.com/ReactiveX/RxJava/wiki/Alphabetical-List-of-Observable-Operators"&gt;RxJava wiki&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;debounce&lt;/code&gt; - only emit an item from the source Observable after a particular timespan has passed without the Observable emitting any other items&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I wanted the &amp;ldquo;&lt;em&gt;after a particular timespan has passed without the Observable emitting any other items&lt;/em&gt;&amp;rdquo; part of debounce, but I needed the whole &lt;em&gt;list of emitted items&lt;/em&gt; (not just a single item). The&lt;code&gt;buffer&lt;/code&gt;operator also seemed promising:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;buffer&lt;/code&gt; - periodically gather items from an Observable into bundles and emit these bundles rather than emitting the items one at a time&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;ldquo;emit as bundles&amp;rdquo;, perfect! uh&amp;hellip; not exactly, it says &amp;ldquo;periodically&amp;rdquo; and that literally means periodically. So EVERY X seconds/minutes it will emit a list of objects regardless of whether there were any taps/events. This would mean a bunch of empty lists would periodically be emitted when no taps were registered. I could get the example working if I tweaked the time component for &lt;code&gt;buffer&lt;/code&gt; just enough and &lt;code&gt;filter&lt;/code&gt; out the empty results, but it was a hack and not the way of the Rx.&lt;/p&gt;
&lt;p&gt;What I really needed was a selective combination of debounce and buffer - a &amp;ldquo;debouncedBuffer&amp;rdquo; operator. Such an operator doesn&amp;rsquo;t exist but you could achieve something similar by calling &lt;code&gt;.buffer()&lt;/code&gt; with a special &amp;ldquo;boundry observable&amp;rdquo; parameter, as Jedi master Ben Christensen points out in this &lt;a href="http://stackoverflow.com/questions/24828897/how-to-group-events-by-idle-periods-using-reactive-extensions"&gt;Stack Overflow post&lt;/a&gt; (the Rx is strong with this one).&lt;/p&gt;
&lt;p&gt;Essentially, if you pass an observable as a parameter to &lt;code&gt;buffer&lt;/code&gt;, every time this observable emits an item, &lt;code&gt;buffer&lt;/code&gt; will take the source observable and emit a &lt;em&gt;list&lt;/em&gt; of items from the &lt;em&gt;source&lt;/em&gt; observable (instead of the items emitted by the boundary observable).&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Source Observable For Actual Events&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Boundary Observable that only tells &lt;span style="color:#e6db74"&gt;&amp;#34;when&amp;#34;&lt;/span&gt; to take items from the Source&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;So what we&amp;rsquo;re going to do is use a &amp;ldquo;debouncedEventEmitter&amp;rdquo; as our boundary observable. The &amp;ldquo;debouncedEventEmitter&amp;rdquo; will basically emit a single item-which we really don&amp;rsquo;t care about-only after a certain time has elapsed from the emission of the first item.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; debouncedEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tapEventEmitter.&lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We now buffer our source observable again to give us a list of items (vs a single item) everytime the debouncedEventEmitter emits a single item.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; debouncedBufferEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tapEventEmitter.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(debouncedEventEmitter);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Altogether now:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; tapEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;share&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; debouncedEventEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tapEventEmitter.&lt;span style="color:#a6e22e"&gt;debounce&lt;/span&gt;(1, TimeUnit.&lt;span style="color:#a6e22e"&gt;SECONDS&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; debouncedBufferEmitter &lt;span style="color:#f92672"&gt;=&lt;/span&gt; tapEventEmitter.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(debouncedEventEmitter);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;debouncedBufferEmitter.&lt;span style="color:#a6e22e"&gt;buffer&lt;/span&gt;(debouncedEventEmitter)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;observeOn&lt;/span&gt;(AndroidSchedulers.&lt;span style="color:#a6e22e"&gt;mainThread&lt;/span&gt;())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Action1&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(List&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; taps) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _showTapCount(taps.&lt;span style="color:#a6e22e"&gt;size&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;.share()&lt;/code&gt; operator? In &lt;a href="../rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/"&gt;my next post&lt;/a&gt;, I&amp;rsquo;ll go into the details of that operator along with &lt;code&gt;.publish()&lt;/code&gt; and &lt;code&gt;refcount()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;[UPDATE: &lt;a href="https://twitter.com/benjchristensen/status/552709457856049152"&gt;Ben pointed&lt;/a&gt; me to a &lt;a href="https://gist.github.com/benjchristensen/e4524a308456f3c21c0b"&gt;niftier implementation of debounced buffer&lt;/a&gt;. I&amp;rsquo;ve added a &lt;a href="https://github.com/kaushikgopal/Android-RxJava/blob/master/app/src/main/java/com/morihacky/android/rxjava/rxbus/RxBusDemo_Bottom3Fragment.java"&gt;third variant of the Bottom fragment&lt;/a&gt; that uses this approach. A &lt;a href="../rxjava-tip-for-the-day-share-publish-refcount-and-all-that-jazz/"&gt;subsequent post&lt;/a&gt; will go into the details.]&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Follow discussion on &lt;a href="http://www.reddit.com/r/androiddev/comments/2rffd2/debouncedbuffer_with_rxjava/"&gt;Reddit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/debouncedbuffer-with-rxjava/</guid><pubDate>Mon, 05 Jan 2015 07:00:00 GMT</pubDate></item><item><title>
New Year - 2015</title><link>https://kau.sh/blog/new-year-2015/</link><description>
&lt;p&gt;i love the holidays. i &lt;em&gt;especially&lt;/em&gt; love the new year&amp;rsquo;s though cause it&amp;rsquo;s an acceptable time to hit that big-nice-red reset button.&lt;/p&gt;
&lt;h2 id="stuff-i-wanted-to-get-done-in-20132014"&gt;
stuff I wanted to &lt;a href="../2013/"&gt;get done in 2013&lt;/a&gt;/2014
&lt;a class="heading-anchor" href="#stuff-i-wanted-to-get-done-in-20132014" aria-label="Link to stuff I wanted to get done in 2013/2014"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;get back in shape (significant progress on this front)&lt;/li&gt;
&lt;li&gt;get more sleep (been getting atleast 6 hours, which is good, now to bump it to 7)&lt;/li&gt;
&lt;li&gt;get really good at Rails (switched gears and became an android developer. i like to think i&amp;rsquo;m a pretty decent one too)&lt;/li&gt;
&lt;li&gt;get a job after my masters&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="stuff-done-in-20132014"&gt;
stuff done in 2013/2014:
&lt;a class="heading-anchor" href="#stuff-done-in-20132014" aria-label="Link to stuff done in 2013/2014:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="joined-wedding-party"&gt;
joined &lt;a href="https://techcrunch.com/2013/02/21/wedding-party-the-mobile-app-that-lets-guests-contribute-photos-to-gorgeous-shared-albums-scores-a-million-dollar-seed-round/"&gt;wedding party&lt;/a&gt;
&lt;a class="heading-anchor" href="#joined-wedding-party" aria-label="Link to joined wedding party"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;i joined as a full-time rails dev but landed up becoming a full-time android dev for them. we have some of the craziest, smartest and fun loving peeps in the industry. i&amp;rsquo;m absolutely loving my time here.&lt;/p&gt;
&lt;h3 id="released-a-lot-of-new-android-stuff"&gt;
released a lot of new android stuff
&lt;a class="heading-anchor" href="#released-a-lot-of-new-android-stuff" aria-label="Link to released a lot of new android stuff"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;besides releasing a tonne of cool features for wedding party, i managed to ship two big android projects: &lt;a href="https://github.com/weddingparty/AndroidFloatLabel"&gt;Android-floatlabel&lt;/a&gt; and &lt;a href="https://github.com/kaushikgopal/Android-RxJava"&gt;learning Android-RxJava&lt;/a&gt;, both of which have been received pretty well by the open source community&lt;/p&gt;
&lt;h3 id="new-blog-design"&gt;
new blog design
&lt;a class="heading-anchor" href="#new-blog-design" aria-label="Link to new blog design"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;i changed this blog&amp;rsquo;s design (again) to match my &lt;a href="http://kau.sh"&gt;homepage&lt;/a&gt;. i&amp;rsquo;m really happy with this iteration of the design. i think it has just the right balance of minimalism, simplicity and style&lt;/p&gt;
&lt;h3 id="getting-fitter"&gt;
getting fit(ter)
&lt;a class="heading-anchor" href="#getting-fitter" aria-label="Link to getting fit(ter)"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;i&amp;rsquo;m a regular again at the gym. i was probably at my fittest around 2007 and i&amp;rsquo;ve still got a long way to go, before i get to that shape again. but i&amp;rsquo;m getting there.&lt;/p&gt;
&lt;p&gt;overall, 2013/2014 went by in a jiffy and was a total win!&lt;/p&gt;
&lt;h2 id="stuff-i-want-to-do-in-2015"&gt;
stuff I want to do in 2015:
&lt;a class="heading-anchor" href="#stuff-i-want-to-do-in-2015" aria-label="Link to stuff I want to do in 2015:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="start-playing-the-piano"&gt;
start playing the piano
&lt;a class="heading-anchor" href="#start-playing-the-piano" aria-label="Link to start playing the piano"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;this is an area i&amp;rsquo;ve totally fallen off the bandwagon. i don&amp;rsquo;t do enough sound-engineering or music anymore. i&amp;rsquo;m thinking of getting a piano/synthesizer and just start jamming again.&lt;/p&gt;
&lt;h3 id="travel-to-atleast-two-new-places-that-ive-never-been"&gt;
travel to atleast two new places that i&amp;rsquo;ve never been
&lt;a class="heading-anchor" href="#travel-to-atleast-two-new-places-that-ive-never-been" aria-label="Link to travel to atleast two new places that i’ve never been"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;a bunch of my close friends and i love traveling and we hit a high in 2013 (just after graduation). it was amazing until 2014, where i&amp;rsquo;ve hit a complete travel slump.&lt;/p&gt;
&lt;h3 id="release-an-independent-android-app"&gt;
release an independent (android) app
&lt;a class="heading-anchor" href="#release-an-independent-android-app" aria-label="Link to release an independent (android) app"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;i&amp;rsquo;ve always wanted to build the &lt;a href="http://dayoneapp.com/"&gt;Day One&lt;/a&gt; app for android (given that they are probably never going to&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt;). there are very few high-quality journaling apps on android. i think this is a personal itch that i&amp;rsquo;m going to try to scratch.&lt;/p&gt;
&lt;p&gt;here&amp;rsquo;s to a happy adventurous and exciting 2015!&lt;/p&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;3 years later, Day One released an Android app &lt;a href="https://play.google.com/store/apps/details?id=com.dayoneapp.dayone"&gt;https://play.google.com/store/apps/details?id=com.dayoneapp.dayone&lt;/a&gt;&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/blog/new-year-2015/</guid><pubDate>Wed, 31 Dec 2014 07:00:00 GMT</pubDate></item><item><title>
Implementing an Event Bus With RxJava - RxBus</title><link>https://kau.sh/blog/implementing-an-event-bus-with-rxjava-rxbus/</link><description>
&lt;p&gt;This post has three parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;quick primer on what an event bus is&lt;/li&gt;
&lt;li&gt;implementing the event bus with RxJava&lt;/li&gt;
&lt;li&gt;parting thoughts on this approach&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;ldquo;RxBus&amp;rdquo; is not going to be a library. Implementing an event bus with RxJava is so ridiculously easy that it doesn&amp;rsquo;t warrant the bloat of an independent library.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h1 id="part-1-what-is-an-event-bus"&gt;
Part 1: What is an event bus?
&lt;a class="heading-anchor" href="#part-1-what-is-an-event-bus" aria-label="Link to Part 1: What is an event bus?"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;Let&amp;rsquo;s talk about two concepts that seem similar: the Observer pattern and the Pub-sub pattern.&lt;/p&gt;
&lt;h2 id="observer-pattern"&gt;
Observer pattern
&lt;a class="heading-anchor" href="#observer-pattern" aria-label="Link to Observer pattern"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;This is a pattern of development in which your class or primary object (known as the Observable) notifies other interested classes or objects (known as Observers) with relevant information (events).&lt;/p&gt;
&lt;h2 id="pub-sub-pattern"&gt;
Pub-sub pattern
&lt;a class="heading-anchor" href="#pub-sub-pattern" aria-label="Link to Pub-sub pattern"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The objective of the pub-sub pattern is exactly the same as the Observer pattern viz. you want some other class to know of certain events taking place.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s an important semantic difference between the Observer and Pub-sub patterns though: in the pub-sub pattern the focus is on &amp;ldquo;broadcasting&amp;rdquo; messages outside. The Observable here doesn&amp;rsquo;t want to know who the events are going out to, just that they&amp;rsquo;ve gone out. In other words the Observable (a.k.a Publisher) doesn&amp;rsquo;t want to know who the Observers (a.k.a Subscribers) are.&lt;/p&gt;
&lt;h2 id="why-the-anonymity"&gt;
Why the anonymity?
&lt;a class="heading-anchor" href="#why-the-anonymity" aria-label="Link to Why the anonymity?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;It allows for this thing called &amp;ldquo;decoupling&amp;rdquo;, which is a good word in computer programming. You want to keep coupling as low as possible in your design.&lt;/p&gt;
&lt;p&gt;Typically, you would expect the publisher to have direct knowledge of each of the many subscribers that it needs to notify, so it can go about notifying each of them, once the &amp;ldquo;event&amp;rdquo; or message is ready. But with an event bus, the publisher is relieved of such duties and this independence helps, because the publisher and subscriber need not have logic coded in them that establish the dependencies between the two.&lt;/p&gt;
&lt;p&gt;In other words &amp;ldquo;consciously decouple&amp;rdquo; your code whenever you can*.&lt;/p&gt;
&lt;h2 id="how-the-anonymity"&gt;
How the anonymity?
&lt;a class="heading-anchor" href="#how-the-anonymity" aria-label="Link to How the anonymity?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Ok, so a natural question with the pub-sub pattern is: how do you actually achieve that anonymity between publisher and subscriber? An easy way is to just get hold of a middleman and let that middleman take care of all the communication. An event bus is one such middleman.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. An event bus is as simple as that.&lt;/p&gt;
&lt;p&gt;Two event bus libraries that are commonly used in Android are &lt;a href="http://square.github.io/otto/"&gt;Otto&lt;/a&gt; and Green Robot&amp;rsquo;s &lt;a href="https://github.com/greenrobot/EventBus"&gt;EventBus&lt;/a&gt;. There are plenty of posts online that explain how to implement them in your app.&lt;/p&gt;
&lt;h1 id="part-2-implementing-the-event-bus-with-rxjava"&gt;
Part 2: Implementing the event bus with RxJava
&lt;a class="heading-anchor" href="#part-2-implementing-the-event-bus-with-rxjava" aria-label="Link to Part 2: Implementing the event bus with RxJava"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;I&amp;rsquo;ve been posting real world examples of using &lt;a href="https://github.com/kaushikgopal/Android-RxJava"&gt;RxJava for Android in this github repo&lt;/a&gt;, so i&amp;rsquo;ll stick to posting the complete implementation in that repo. Here are the interesting parts of the implementation:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// this is the middleman object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;RxBus&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Subject&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object, Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; _bus &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; SerializedSubject&lt;span style="color:#f92672"&gt;&amp;lt;&amp;gt;&lt;/span&gt;(PublishSubject.&lt;span style="color:#a6e22e"&gt;create&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(Object o) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _bus.&lt;span style="color:#a6e22e"&gt;onNext&lt;/span&gt;(o);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; Observable&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; _bus;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. You now have an event bus ready to use.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s how you post an event to the bus:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@OnClick&lt;/span&gt;(R.&lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;btn_demo_rxbus_tap&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onTapButtonClicked&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _rxBus.&lt;span style="color:#a6e22e"&gt;send&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; TapEvent());
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;and here&amp;rsquo;s how you listen to those events from other fragments/services etc.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// note that it is important to subscribe to the exact same _rxBus instance that was used to post the events&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;_rxBus.&lt;span style="color:#a6e22e"&gt;toObserverable&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;subscribe&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Action1&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;Object&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;call&lt;/span&gt;(Object event) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(event &lt;span style="color:#66d9ef"&gt;instanceof&lt;/span&gt; TapEvent) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _showTapText();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt;(event &lt;span style="color:#66d9ef"&gt;instanceof&lt;/span&gt; SomeOtherEvent) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _doSomethingElse();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In the example we post events from the top fragment (green part) and listen in (through the bus) from the bottom fragment (blue part).&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/rxbus_simple.gif" alt="Simple RxBus example"&gt;&lt;/p&gt;
&lt;h1 id="part-3----parting-thoughts"&gt;
Part 3 - parting thoughts
&lt;a class="heading-anchor" href="#part-3----parting-thoughts" aria-label="Link to Part 3 - parting thoughts"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;h2 id="dead-events"&gt;
Dead events
&lt;a class="heading-anchor" href="#dead-events" aria-label="Link to Dead events"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;There are some cases where it&amp;rsquo;s useful to know if there are any observers currently listening to the bus. For e.g. &lt;a href="http://markhudnall.com/2013/11/13/gcm-foreground-and-background/"&gt;if you use an event bus to handle your GCM push notifications&lt;/a&gt;) and don&amp;rsquo;t want to send a push notification if the app is in the foreground, then it&amp;rsquo;s important to listen to &amp;ldquo;&lt;a href="https://github.com/square/otto/blob/master/otto/src/main/java/com/squareup/otto/DeadEvent.java"&gt;dead events&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;For e.g in a recent release for Wedding Party, we added &amp;ldquo;Messaging&amp;rdquo; to our app. If the user has the app open (thus having atleast 1 or more listeners to the bus) we don&amp;rsquo;t send push notifications but if they have the app in the background, then we send a push notification to let them know of chat messages. After an event is posted to the event bus, if no subscriber is listening, a dead event is returned. If we get back a dead event a push notification is shot out.&lt;/p&gt;
&lt;p&gt;How would you do this with the RxBus implementation?&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s pretty easy actually. &lt;code&gt;Subject&lt;/code&gt;s have this helpful method on them &lt;code&gt;hasObservers()&lt;/code&gt; that would tell us exactly just that. This was added in the &lt;a href="https://github.com/ReactiveX/RxJava/pull/1802"&gt;1.x release for RxJava&lt;/a&gt;, so you have to be on the latest version of RxAndroid (0.23.0 as of this writing) in order to see this method.&lt;/p&gt;
&lt;h2 id="so-should-i-use-rxbus-instead-of-ottoeventbus"&gt;
So should I use RxBus instead of Otto/EventBus?
&lt;a class="heading-anchor" href="#so-should-i-use-rxbus-instead-of-ottoeventbus" aria-label="Link to So should I use RxBus instead of Otto/EventBus?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you simply want to use an event bus with your Android app, you&amp;rsquo;re probably better off with a library like &lt;a href="http://square.github.io/otto/"&gt;Otto&lt;/a&gt; (which i highly recommend) or &lt;a href="https://github.com/greenrobot/EventBus"&gt;EventBus&lt;/a&gt;. Otto has a clean api driven by annotations and is probably far more simpler to use.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re familiar with Rx, already use RxJava in your app and want to get rid of an additional library, definitely try out the RxBus approach!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Did i miss anything? Do you have more suggestions on improving the RxBus implementation? Follow the discussion on &lt;a href="http://www.reddit.com/r/androiddev/comments/2qebdo/rxbus_implementing_an_event_bus_with_rxjava/"&gt;Reddit&lt;/a&gt;, &lt;a href="https://plus.google.com/105979641354189463768/posts/Au1xZ8RGLdG"&gt;Google Plus&lt;/a&gt;, feel free to tweet your comments @&lt;a href="http://twitter.com/kaushikgopal"&gt;kaushikgopal&lt;/a&gt; or just raise an issue in &lt;a href="https://github.com/kaushikgopal/Android-RxJava/issues"&gt;the repo&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Pssst. there are two bonus posts that delve a little more into the RxJava trickery I used to make the RxBus example a tad bit fancier. Stay tuned.&lt;/em&gt;&lt;/p&gt;
&lt;br /&gt;
&lt;p&gt;&lt;img src="https://kau.sh/images/content/uncopule-decouple.gif" alt="conscious decoupling"&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/implementing-an-event-bus-with-rxjava-rxbus/</guid><pubDate>Wed, 24 Dec 2014 07:00:00 GMT</pubDate></item><item><title>
Learning RxJava With Android by Example</title><link>https://kau.sh/blog/learning-rxjava-with-android-by-example/</link><description>
&lt;p&gt;I&amp;rsquo;ve read and watched a lot on Rx. Most examples either use the J8 lambda notations/Scala/Groovy or some other awesome language that us Android developers are constantly envious of.&lt;/p&gt;
&lt;p&gt;Unfortunately I could never find real-world simple examples in Android that could show me how to use RxJava. To scratch that itch, I &lt;a href="https://github.com/kaushikgopal/Android-RxJava"&gt;created a github repo&lt;/a&gt; with a couple of nifty examples using RxJava. You can build the apk and run the app to see different working examples.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t know anything about RxJava, I would strongly recommend you first get acquainted with RxJava and understand the terminology really well. It has a pretty steep learning curve, so just throw yourself at it, until you start to understand the motivation behind its usage. Here are some resources to get you started:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="http://channel9.msdn.com/Blogs/Charles/Erik-Meijer-Rx-in-15-Minutes"&gt;Rx in 15 minutes - by Erik Meijer&lt;/a&gt; creator of the Reactive extensions. RxJava is essentially a Java port of Reactive extensions.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://channel9.msdn.com/Series/Rx-Workshop"&gt;Rx Workshop - Channel9&lt;/a&gt; Microsoft&amp;rsquo;s video channel on RX. Just peruse through the first few videos. You&amp;rsquo;ll pick a lot of useful info.&lt;/li&gt;
&lt;li&gt;Watch this excellent &lt;a href="http://www.infoq.com/presentations/Netflix-API-rxjava-hystrix/"&gt;talk by Ben Christensen - one of the creators of RxJava&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An interesting writeup I found &lt;a href="https://gist.github.com/staltz/868e7e9bc2a7b8c1f754"&gt;somewhere in the interwebs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Dan Lew started a nice primer series today, on RxJava for Android. Definitely follow his &lt;a href="http://blog.danlew.net/2014/09/15/grokking-rxjava-part-1/"&gt;3 part series&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ReactiveX/RxJava/wiki"&gt;Snoop around the RxJava wiki&lt;/a&gt;. Look at those marble diagrams specifically. You will have to come back to them repeatedly to get a good understanding of the operators.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once you have a decent understanding of when to use RxJava and the different terms used, come back to the &lt;a href="https://github.com/kaushikgopal/Android-RxJava"&gt;learning RxJava by example repo&lt;/a&gt; and you&amp;rsquo;ll find the examples a godsend. If not, &lt;a href="https://github.com/kaushikgopal/Android-RxJava#contributing"&gt;contribute&lt;/a&gt; with the examples you think that will be.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Follow the discussion on &lt;a href="http://www.reddit.com/r/androiddev/comments/2gi7ik/learning_rxjava_with_android_by_example/"&gt;Reddit&lt;/a&gt; or &lt;a href="https://plus.google.com/106712246601366256750/posts/7bMbnsit91A"&gt;Google Plus&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/learning-rxjava-with-android-by-example/</guid><pubDate>Mon, 15 Sep 2014 07:00:00 GMT</pubDate></item><item><title>
Primer on Threading and Handlers in Android</title><link>https://kau.sh/blog/primer-on-threading-and-handlers-in-android/</link><description>
&lt;p&gt;Mobile devices are getting pretty fast, but they aren&amp;rsquo;t infinitely fast yet. If you want your app to be able to do any serious work without affecting the user experience by locking up the interface, you&amp;rsquo;ll have to resort to running things in parallel. On Android, this is done with &amp;ldquo;threads&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Grab yourself a cup of coffee and read this post line by line. I&amp;rsquo;ll introduce you to the concept of threads, talk about how Java uses threads and explain how &amp;ldquo;Handlers&amp;rdquo; in Android help with threading.&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Whenever you want to do &lt;em&gt;asynchronous/parallel processing&lt;/em&gt;, you do it with &lt;strong&gt;threads&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id="threads-you-say-"&gt;
Threads you say ?
&lt;a class="heading-anchor" href="#threads-you-say-" aria-label="Link to Threads you say ?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;A thread or &amp;ldquo;thread of execution&amp;rdquo; is basically a sequence of instructions (of program code), that you send to your operating system.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="http://upload.wikimedia.org/wikipedia/commons/a/a5/Multithreaded_process.svg"
alt="Multi-threaded process"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Image Courtesy: Wikipedia.
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;&amp;ldquo;Typically&amp;rdquo; your CPU can process one thread, per core, at any time. So a multi-core processor (most Android devices today) by definition can handle multiple-threads of execution (which is to say, they can do multiple things at once).&lt;/p&gt;
&lt;h2 id="truth-to-multi-core-processing-and-single-core-multitasking"&gt;
Truth to multi-core processing and single-core multitasking
&lt;a class="heading-anchor" href="#truth-to-multi-core-processing-and-single-core-multitasking" aria-label="Link to Truth to multi-core processing and single-core multitasking"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I say &amp;ldquo;typically&amp;rdquo; because the corollary to the above statement is not necessarily true. Single-core devices can &amp;ldquo;simulate&amp;rdquo; multithreading using multitasking.&lt;/p&gt;
&lt;p&gt;Every &amp;ldquo;task&amp;rdquo; that&amp;rsquo;s run on a thread can be broken down into multiple instructions. These instructions don&amp;rsquo;t have to happen all at once. So a single-core device can switch to a thread &amp;ldquo;1&amp;rdquo; finish an instruction 1A, then switch to thread &amp;ldquo;2&amp;rdquo; finish an instruction 2A, switch back to 1 finish 1B, 1C, 1D, switch to 2, finish 2B, 2C and so on&amp;hellip;&lt;/p&gt;
&lt;p&gt;This switching between threads happens &lt;em&gt;so fast&lt;/em&gt; that it &lt;em&gt;appears&lt;/em&gt;, even on a single-core device, that all the threads are making progress at exactly the same time. It&amp;rsquo;s an illusion caused by speed, much like Agent Brown appearing to have multiple heads and arms.&lt;/p&gt;
&lt;figure &gt;
&lt;img src="https://kau.sh/images/content/agent_brown_dodging_bullets.gif"
alt="dogde bullets like your agent brown"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;p&gt;Now on to some code.&lt;/p&gt;
&lt;h2 id="threads-in-core-java"&gt;
Threads in core Java
&lt;a class="heading-anchor" href="#threads-in-core-java" aria-label="Link to Threads in core Java"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In Java, when you want to do parallel processing, you execute your code in a &lt;code&gt;Runnable&lt;/code&gt; either by extending the &lt;code&gt;Thread&lt;/code&gt; class or implementing the Runnable interface&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Version 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IAmAThread&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Thread {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IAmAThread&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;super&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;IAmAThread&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// your code (sequence of instructions)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// to execute this sequence of instructions in a separate thread.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; IAmAThread().&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Version 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;IAmARunnable&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;implements&lt;/span&gt; Runnable {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// your code (sequence of instructions)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// to execute this sequence of instructions in a separate thread.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;IAmARunnable myRunnable &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; IAmARunnable();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Thread(myRunnable).&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Both of these approaches are fundamentally very similar. Version 1 involves creating an actual thread while Version 2 involves creating a runnable, which in-turn has to be called by a Thread.&lt;/p&gt;
&lt;p&gt;Version 2, is generally the preferred approach (and is a much &lt;a href="http://en.wikipedia.org/wiki/Composition_over_inheritance"&gt;larger subject&lt;/a&gt; &lt;a href="http://stackoverflow.com/questions/541487/implements-runnable-vs-extends-thread"&gt;of discussion&lt;/a&gt;, beyond the scope of this post).&lt;/p&gt;
&lt;h2 id="threads-in-android"&gt;
Threads in Android
&lt;a class="heading-anchor" href="#threads-in-android" aria-label="Link to Threads in Android"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Whenever your app starts up in Android, all components are run on a single primary thread (by default) called the &amp;ldquo;main&amp;rdquo; thread. The primary role of this thread though, is to handle the user interface and dispatch events to the appropriate widgets/views. For this reason, the main thread is also referred to as the &amp;ldquo;UI&amp;rdquo; thread.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://android-developers.blogspot.com/2009/05/painless-threading.html"&gt;If you have a long running operation on your UI thread&lt;/a&gt;, the user interface is going to get locked up until this operation is complete. This is bad for your users! That&amp;rsquo;s why it&amp;rsquo;s important to understand how threads work in Android specifically, so you can offload some of the work to parallel threads. Android is pretty merciless about keeping things off the UI thread. If you have a long running operation on the UI thread you&amp;rsquo;re probably going to run into the infamous &lt;a href="http://developer.android.com/training/articles/perf-anr.html"&gt;ANR&lt;/a&gt; that will conveniently allow your users to kill your app!&lt;/p&gt;
&lt;p&gt;Now Android is all Java, so it supports the usage of the core Java &lt;code&gt;Thread&lt;/code&gt; class to perform asynchronous processing. So you could use code very similar to the one shown in the &amp;ldquo;Threads in Java&amp;rdquo; section above, and start using threads in Android right away. But that can be a tad bit difficult.&lt;/p&gt;
&lt;h3 id="why-is-using-core-java-threads-in-android-difficult"&gt;
Why is using core Java threads in Android difficult?
&lt;a class="heading-anchor" href="#why-is-using-core-java-threads-in-android-difficult" aria-label="Link to Why is using core Java threads in Android difficult?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Well, parallel processing is not as easy as it sounds because you have to maintain &amp;ldquo;concurrency&amp;rdquo; across the multiple threads. In the words of the &lt;a href="https://www.tbray.org/ongoing/When/201x/2014/01/01/Software-in-2014#p-2"&gt;very wise Tim Bray&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ordinary humans can&amp;rsquo;t do concurrency at scale (or really at all) &amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Specifically for Android, the following is additionally cumbersome:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Synchronization with the UI thread is a major PITA (you typically want to do this, when you want to send progress updates to the UI, for your background operation)&lt;/li&gt;
&lt;li&gt;Things change even more weirdly with orientation/configuration changes because an orientation change causes an activity to be recreated (so background threads may be trying to change the state of a destroyed activity, and if the background thread isn&amp;rsquo;t on the UI thread, well that complicates things even more because of point 1).&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s also no default handling for thread pooling&lt;/li&gt;
&lt;li&gt;Canceling thread actions requires custom code&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="arrok-so-how-do-we-do-parallel-processing-in-android"&gt;
Arr&amp;hellip;ok, so how DO we do parallel processing in Android?
&lt;a class="heading-anchor" href="#arrok-so-how-do-we-do-parallel-processing-in-android" aria-label="Link to Arr…ok, so how DO we do parallel processing in Android?"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Some (in)famous Android constructs you&amp;rsquo;ve probably come across:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://developer.android.com/reference/android/os/Handler.html"&gt;&lt;code&gt;Handler&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the subject of our detailed discussion today&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://developer.android.com/reference/android/os/AsyncTask.html"&gt;&lt;code&gt;AsyncTask&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Using AsyncTasks are truly the simplest way to handle threads in Android. That being said, they are also the &lt;a href="http://blog.danlew.net/2014/06/21/the-hidden-pitfalls-of-asynctask/"&gt;most&lt;/a&gt; error &lt;a href="http://bon-app-etit.blogspot.com/2013/04/the-dark-side-of-asynctask.html"&gt;prone&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://developer.android.com/reference/android/app/IntentService.html"&gt;&lt;code&gt;IntentService&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It requires more boiler plate code, but this is generally my preferred mechanism for off-loading long-running operations to the background. Armed with an EventBus like &lt;a href="http://square.github.io/otto/"&gt;Otto&lt;/a&gt;, IntentServices become amazingly easy to implement.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://developer.android.com/guide/components/loaders.html#summary"&gt;&lt;code&gt;Loader&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;These are geared more towards performing asynchronous tasks, that have to deal with data from databases or content providers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href="http://developer.android.com/guide/components/services.html"&gt;&lt;code&gt;Service&lt;/code&gt;&lt;/a&gt; (honorable mention)&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve worked with Services closely, you should know that this is actually a little misleading. A common misconception is that Services run on the background thread. Nope! they &amp;ldquo;appear&amp;rdquo; to run in the background because they don&amp;rsquo;t have a UI component associated with them. They actually run on the UI thread (by default)&amp;hellip;. So they run on the UI thread by default, even though they don&amp;rsquo;t have a UI component?&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="http://www.topito.com/wp-content/uploads/2013/01/code-24.gif"
alt="programmer life gif"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Naming has never been Google&amp;#39;s strong suit. ActivityInstrumentationTestCase ... wait for it .... 2! Spinner anyone?
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;p&gt;If you want your service to run on a background thread, you&amp;rsquo;ll have to manually spawn another thread and execute your operations in that thread (similar to an approach discussed above). Really you should just use IntentServices but that is a subject for another post.&lt;/p&gt;
&lt;h1 id="android-handlers"&gt;
Android Handlers:
&lt;a class="heading-anchor" href="#android-handlers" aria-label="Link to Android Handlers:"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;p&gt;From the not-so-dummy-friendly &lt;a href="http://developer.android.com/reference/android/os/Handler.html"&gt;Android developer documentation for Handlers&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Handler allows you to send and process Message and Runnable objects associated with a thread&amp;rsquo;s MessageQueue. Each Handler instance is associated with a single thread and that thread&amp;rsquo;s message queue. When you create a new Handler, it is bound to the thread/message queue of the thread that is creating it &amp;ndash; from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;a href="http://martinvalasek.com/blog/pictures-from-a-developers-life"&gt;
&lt;img src="http://www.topito.com/wp-content/uploads/2013/01/code-02.gif"
alt="picture from a dev"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;p&gt;To understand that better, you probably need to know what &lt;strong&gt;Message Queues&lt;/strong&gt; are.&lt;/p&gt;
&lt;h2 id="message-queues"&gt;
Message Queues:
&lt;a class="heading-anchor" href="#message-queues" aria-label="Link to Message Queues:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Threads basically have something called a &amp;ldquo;Message Queue&amp;rdquo;. These message queues allow communication between threads and is a sort of pattern, where control (or content) is passed between the threads.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s actually a wonderful name, because it&amp;rsquo;s exactly just that: a queue of messages or sequence of instructions, for the thread, to perform one by one. This additionally allows us to do two more cool things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;ldquo;schedule&amp;rdquo; Messages and Runnables to be executed at some point in the future&lt;/li&gt;
&lt;li&gt;enqueue an action to be performed on a different thread other than your own&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Note: when I mention &amp;lsquo;message&amp;rsquo; from here onwards, it&amp;rsquo;s the same as a runnable object or a sequence of instructions.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So going back to Handlers in Android&amp;hellip;. if you read and pause at every single line, the docs make much more sense now:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Handler allows you to send and process Message and Runnable objects associated with a thread&amp;rsquo;s MessageQueue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, a handler allows you to send messages to a thread&amp;rsquo;s message queue. check ✔ !&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Each Handler instance is associated with a single thread and that thread&amp;rsquo;s message queue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A handler can only be associated with a single thread. ✔&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So, which thread is a handler associated with? The thread that creates it. ✔&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ndash; from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yeah yeah we already know that. Moving on&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Tip: Here&amp;rsquo;s something you probably didn&amp;rsquo;t know : in Android, every thread is associated with an instance of a Handler class, and it allows the thread to run along with other threads and communicate with them through messages.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Another Tip (if you&amp;rsquo;ve dealt with the more common AsyncTasks): AsyncTasks use Handlers but don&amp;rsquo;t run in the UI thread. They provide a channel to talk back to the UI, using the postExecute method.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="this-is-all-cool-yo-so-how-do-i-create-them-handlers"&gt;
This is all cool yo, so how do I create them Handlers?
&lt;a class="heading-anchor" href="#this-is-all-cool-yo-so-how-do-i-create-them-handlers" aria-label="Link to This is all cool yo, so how do I create them Handlers?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Two ways:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;using the default constructor: new Handler()&lt;/li&gt;
&lt;li&gt;using a parameterized constructor that takes a runnable object or callback object&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="what-useful-methods-does-the-handler-api-give-me"&gt;
What useful methods does the Handler API give me?
&lt;a class="heading-anchor" href="#what-useful-methods-does-the-handler-api-give-me" aria-label="Link to What useful methods does the Handler API give me?"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;Remember:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Handlers simply send messages or &amp;ldquo;posts&amp;rdquo; to the message queues.&lt;/li&gt;
&lt;li&gt;They are convenience methods that help syncing back with the UI thread.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you look at the &lt;a href="http://developer.android.com/reference/android/os/Handler.html"&gt;API for Handlers&lt;/a&gt; now, the main methods provided make sense:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;post&lt;/li&gt;
&lt;li&gt;postDelayed&lt;/li&gt;
&lt;li&gt;postAtTime&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="code-samples"&gt;
Code samples:
&lt;a class="heading-anchor" href="#code-samples" aria-label="Link to Code samples:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;The examples below are rudimentary, what you actually want to be closely following are the comments.&lt;/p&gt;
&lt;h3 id="example-1-using-post-method-of-the-handler"&gt;
Example 1: using &amp;ldquo;post&amp;rdquo; method of the Handler
&lt;a class="heading-anchor" href="#example-1-using-post-method-of-the-handler" aria-label="Link to Example 1: using “post” method of the Handler"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;TestActivity&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; Activity {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// all standard stuff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onCreate&lt;/span&gt;(Bundle savedInstanceState) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// all standard stuff&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// we&amp;#39;re creating a new handler here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// and we&amp;#39;re in the UI Thread (default)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// so this Handler is associated with the UI thread&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Handler mHandler &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Handler();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// I want to start doing something really long&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// which means I should run the fella in another thread.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// I do that by sending a message - in the form of another runnable object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// But first, I&amp;#39;m going to create a Runnable object or a message for this&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Runnable mRunnableOnSeparateThread &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Runnable() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt; () {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do some long operation&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; longOperation();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// After mRunnableOnSeparateThread is done with it&amp;#39;s job,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// I need to tell the user that i&amp;#39;m done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// which means I need to send a message back to the UI thread&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// who do we know that&amp;#39;s associated with the UI thread?&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mHandler.&lt;span style="color:#a6e22e"&gt;post&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Runnable(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// do some UI related thing&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// like update a progress bar or TextView&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ....&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; });
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Cool but I&amp;#39;ve not executed the mRunnableOnSeparateThread yet&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// I&amp;#39;ve only defined the message to be sent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// When I execute it though, I want it to be in a different thread&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// that was the whole point.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Thread(mRunnableOnSeparateThread).&lt;span style="color:#a6e22e"&gt;start&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If I didn&amp;rsquo;t have a handler object, posting back to the UI thread would have been pretty tricky.&lt;/p&gt;
&lt;h3 id="example-2-using-postdelayed"&gt;
Example 2: using postDelayed
&lt;a class="heading-anchor" href="#example-2-using-postdelayed" aria-label="Link to Example 2: using postDelayed"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;In a recent feature for Wedding Party, I had to emulate the auto-complete functionality with an EditText. Every change in text triggered an API call to retrieve some data from our servers.&lt;/p&gt;
&lt;p&gt;I wanted to reduce the number of API calls shot out by the app, so I used the Handler&amp;rsquo;s postDelayed method to achieve this.&lt;/p&gt;
&lt;p&gt;This example doesn&amp;rsquo;t focus on parallel processing, but rather the ability for the Handler to function as a Message Queue and schedule messages to be executed at some later point in the future&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// the below code is inside a TextWatcher&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// which implements the onTextChanged method&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// I&amp;#39;ve simplified it to only highlight the parts we&amp;#39;re&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// interested in&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; lastChange &lt;span style="color:#f92672"&gt;=&lt;/span&gt; 0;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;onTextChanged&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; CharSequence chars,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; start, &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; before, &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; count) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// The handler is spawned from the UI thread&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Handler().&lt;span style="color:#a6e22e"&gt;postDelayed&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// argument 1 for postDelated = message to be sent&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; Runnable() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;run&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (noChangeInText_InTheLastFewSeconds()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; searchAndPopulateListView(chars.&lt;span style="color:#a6e22e"&gt;toString&lt;/span&gt;()); &lt;span style="color:#75715e"&gt;// logic&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; },
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// argument 2 for postDelated = delay before execution&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 300);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastChange &lt;span style="color:#f92672"&gt;=&lt;/span&gt; System.&lt;span style="color:#a6e22e"&gt;currentTimeMillis&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;boolean&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;noChangeInText_InTheLastFewSeconds&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; System.&lt;span style="color:#a6e22e"&gt;currentTimeMillis&lt;/span&gt;() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastChange &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; 300
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I leave the &amp;ldquo;postAtTime&amp;rdquo; as an exercise for the reader. Got a grip on Handlers? Happy threading!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Follow the discussion on &lt;a href="https://news.ycombinator.com/item?id=7921979"&gt;Hacker News&lt;/a&gt; or &lt;a href="http://www.reddit.com/r/androiddev/comments/28nsty/primer_on_threading_and_handlers_in_android/"&gt;Reddit&lt;/a&gt; or &lt;a href="https://plus.google.com/106712246601366256750/posts/KMU37xoYGuY"&gt;Google Plus&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/primer-on-threading-and-handlers-in-android/</guid><pubDate>Fri, 20 Jun 2014 07:00:00 GMT</pubDate></item><item><title>
How I remembered Roshambo 2.0 (Rock Paper Scissors Lizard Spock)</title><link>https://kau.sh/blog/presentations-are-easy-just-tell-stories/</link><description>
&lt;p&gt;The title was just to grab your attention. The tl;dr version of this post is: get good at story telling. It&amp;rsquo;ll help you with two things:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Becoming a better presenter.&lt;/li&gt;
&lt;li&gt;Improving your memory.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;
&lt;p&gt;I urge you though, to read this post in it&amp;rsquo;s entirety. You might some of the stories interesting:&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;Presentations are generally looked at with much anxiety and trepidation (within the tech community at least). There are two ways to combat this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Practice like crazy (joining a group like Toastmasters helps)&lt;/li&gt;
&lt;li&gt;Have something interesting to say.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Obviously, you have to do both to become a good presenter, but I want to focus more on the latter point. You know what most human beings find interesting? Stories! Even crappy ones. We&amp;rsquo;re wired from birth to like stories. If i heard two presentations, both of mediocre quality, i would definitely prefer the one that had a story to tell. I think stories make your presentations more personal and relatable. Heck, it doesn&amp;rsquo;t even have to be your own. People relate more easily to stories because they feel they can associate themselves with your pain/experience. A &lt;a href="http://ninjasandrobots.com/lead-with-pain"&gt;post on ninjasandrobots&lt;/a&gt; (how cool is that domain name!) talks about leading with pain. It boils down to telling a story.&lt;/p&gt;
&lt;h2 id="using-stories-to-captivate"&gt;
Using stories to captivate
&lt;a class="heading-anchor" href="#using-stories-to-captivate" aria-label="Link to Using stories to captivate"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;If you tell a great story, then it sticks. I have a professor here in CMU, that uses this tactic and boy is it mesmorizing. It totally helps that he&amp;rsquo;s got the experience (MIT, previous CTO of the research dev labs of a tech giant that makes phones etc. Hint: if you&amp;rsquo;ve used a mobile phone in the past 5-8 years, you have definitely used his ex-company&amp;rsquo;s phones). We were working on a project with him on collecting sensor data. He gave us an example of how data is always valuable. The only problem, is that the value of that data is realized at a different time.&lt;/p&gt;
&lt;p&gt;He gave us this example: the fire department had a service where people could call in emergencies, along with pictures of the site. You can&amp;rsquo;t really control the inflow of data with the public, so you had people sending in pictures of dead cats around the city. Yes this was tragic, but there isn&amp;rsquo;t really much a fire department can do now with this info right? Yes and No. A few weeks later, there was an anthrax scare in the city and it was extremely important for the HAZMAT department to find the source of the outbreak. You know which creatures are most sensitive to anthrax? cats. Suddenly those dead cat pictures became tremendously useful, in tackling a major disease outbreak. That story was one of the many, that our professor told and mesmorized us into working on his project (note to self: write more posts about those stories).&lt;/p&gt;
&lt;h2 id="improving-your-memory"&gt;
Improving your memory
&lt;a class="heading-anchor" href="#improving-your-memory" aria-label="Link to Improving your memory"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I gave this goofy presentation on &lt;a href="http://en.wikipedia.org/wiki/Rock-paper-scissors-lizard-Spock"&gt;Roshambo 2.0&lt;/a&gt; (the lizard-spock variant popularized by The Big Bang theory) once and obviously the presentation required that I get the sequence right. It&amp;rsquo;s pretty tricky, even Sheldon &lt;a href="http://www.youtube.com/watch?v=tCNxvsP-Ce8"&gt;got it wrong&amp;hellip;&lt;/a&gt; &lt;a href="http://www.youtube.com/watch?v=Z2z-AbYMk5Q"&gt;multiple times&lt;/a&gt;. This was sort of a lightening talk I had to give at school and it was a casual setting, which meant the audience were my friends and they would totally rip me apart if I got it wrong. I thankfully got it right in one shot :D and it didn&amp;rsquo;t really matter if my delivery style, poise, body language etc. was great. My presentation was a success mostly because I got it right in one swoop. People came up to me and asked if i had a photographic memory? It&amp;rsquo;s actually quite the opposite. I have a horrible memory. Close friends know that I have the admirable feat of genuinely forgetting my own birthday one year (if my dear folks and brother hadn&amp;rsquo;t reminded me at 12:00am, I would have probably gone through the whole day not realizing I clocked another year). Digression aside, the way I managed to get Roshambo 2.0 right was that I weaved a crazy story in my head to follow the sequence. If you &lt;em&gt;aren&amp;rsquo;t&lt;/em&gt; interested in the story that I came up with, skip the next paragraph (why did you start reading this post though?).&lt;/p&gt;
&lt;p&gt;I imagined myself starting with a pair of scissors cutting paper.I look at the paper and then realized i had used that to cover a rock once, the same rock that I used to chuck at a lizard. That pesky lizard was a blue necked one, similar to the one that poisons Spock in a fictional Star Trek episode. In that fictional episode, Spock was so mad with Kirk&amp;rsquo;s tomfoolery, that he chucks a scissor at Kirk, but misses and lands up breaking it. Kirk to make up for his actions, decides to kill/decaptiate the same blue necked Lizard(that poisoned Spock), with a scissor. During the battle, the Lizard needed nutrition, so it decides to eat paper. In a crazy twist of serendipitous circumstances, the paper consumed was the same one that Kirk initially wrote to disprove Spock, in an experiment Spock was doing, to vaporize Rock. Oh and Rock beats Scissors. The key here is to make your own story cause this might make no sense at all, but in my head I can see the whole story playing out crystal clear.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s magical how stories can greatly help improve memory. I&amp;rsquo;m pretty sure there are scientific studies conducted by people far more experienced and knowledgeable than me, proving this point but Roshambo 2.0 was proof enough for me.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re not as excited as you were led to believe reading this post, I leave you with links to presentations given by the masters. You cannot not be impressed after this.&lt;/p&gt;
&lt;h3 id="sir-ken-robinson"&gt;
Sir Ken Robinson
&lt;a class="heading-anchor" href="#sir-ken-robinson" aria-label="Link to Sir Ken Robinson"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Doesn&amp;rsquo;t use slides, has a great sense of humor, comes up on stage and just tells his story (with an impressive British accent):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=iG9CE55wbtY"&gt;Schools kill creativity - 2006&lt;/a&gt; : His legendary TED talk.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=r9LelXa3U_I"&gt;Bring on the learning revolution - 2010&lt;/a&gt; : His follow up talk on TED.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=wX78iKhInsc"&gt;How to escape education&amp;rsquo;s death valley - 2012&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="steve-jobs"&gt;
Steve Jobs
&lt;a class="heading-anchor" href="#steve-jobs" aria-label="Link to Steve Jobs"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You know he was going to be mentioned somehwere in the post&amp;hellip;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=UF8uR6Z6KLc"&gt;Stanford Commencement Address - 2005&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=vZYlhShD2oQ"&gt;First iPhone keynote - 2007&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="randy-pausch"&gt;
Randy Pausch
&lt;a class="heading-anchor" href="#randy-pausch" aria-label="Link to Randy Pausch"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Besides the amazing stories he tells, his story alone is impressive and moving enough to captivate the audience.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=ji5_MqicxSo"&gt;The Last Lecture&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=blaK_tB_KQA"&gt;Time Management&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="for-the-techies"&gt;
For the techies:
&lt;a class="heading-anchor" href="#for-the-techies" aria-label="Link to For the techies:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Tech presentations can also be made interesting. Here are some random examples that are favorites of mine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=l9JXH7JPjR4"&gt;How to talk to developers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=lyZQPjUT5B4"&gt;Bubble Sort demonstrated by Hungarian dance&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Rock on!&lt;/p&gt;</description><guid>https://kau.sh/blog/presentations-are-easy-just-tell-stories/</guid><pubDate>Fri, 07 Jun 2013 07:00:00 GMT</pubDate></item><item><title>
A developer's life</title><link>https://kau.sh/blog/a-developers-life/</link><description>
&lt;p&gt;This is by far the funniest stuff I&amp;rsquo;ve read this year. Never fails to crack me up&amp;hellip;&lt;/p&gt;</description><guid>https://kau.sh/blog/a-developers-life/</guid><pubDate>Sat, 06 Apr 2013 07:00:00 GMT</pubDate></item><item><title>
We humans are capable of greatness</title><link>https://kau.sh/blog/we-humans-are-capable-of-greatness-carl-sagan/</link><description>
&lt;blockquote&gt;
&lt;p&gt;For All Our Failings, Despite Our Limitations and Fallibilities, We Humans Are Capable of Greatness&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Carl_Sagan"&gt;Carl Sagan&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/we-humans-are-capable-of-greatness-carl-sagan/</guid><pubDate>Sat, 06 Apr 2013 07:00:00 GMT</pubDate></item><item><title>
Comfort in Coding</title><link>https://kau.sh/blog/comfort-in-coding/</link><description>
&lt;p&gt;There&amp;rsquo;s something innately comforting about coding. The solution is not known but the variables are fixed. The ambiguities are minimal and the choices are simple. The rule book is adhered to. You mess up and you get bugs, you get it right and a solution is reached. If the solution is good, it becomes poetry.&lt;/p&gt;
&lt;p&gt;I realize why management have it tough now. The jargon, documentation, definitions, ambiguity and interpretations that they have to deal with are such a mess.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;I write this as I power through a course where in order to take a break from it all, I resort to coding.&lt;/em&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/comfort-in-coding/</guid><pubDate>Sat, 19 Jan 2013 07:00:00 GMT</pubDate></item><item><title>
New Year - 2013</title><link>https://kau.sh/blog/new-year-2013/</link><description>
&lt;p&gt;My last post didn&amp;rsquo;t exactly end in an optimistic note. Let&amp;rsquo;s correct that:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Wishing you all a happy, joyous and peaceful 2013 folks!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Mayans predicted that the world-as we know it-would end but we&amp;rsquo;re still here standing (cheers to that).&lt;/p&gt;
&lt;!-- more --&gt;
&lt;p&gt;The new year is a time to reflect and wipe the slate clean. People &lt;a href="http://ryaneshea.com/dont-make-a-new-years-resolution"&gt;advice against making new year resolutions&lt;/a&gt; but I do them anyway. I like taking a look back at my &lt;a href="https://kau.sh/tags/new-year/"&gt;previous resolutions&lt;/a&gt;. Helps me get a better understanding of my achievement or lack thereof.&lt;/p&gt;
&lt;h2 id="stuff-done-in-2012"&gt;
Stuff done in 2012:
&lt;a class="heading-anchor" href="#stuff-done-in-2012" aria-label="Link to Stuff done in 2012:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Got in to the &lt;a href="http://www.cmu.edu/silicon-valley/academics/software-engineering/index.html"&gt;masters program at Carnegie Mellon&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Learned Ruby(&amp;amp; Rails) and built a couple of sites with it. Long way to go but loving it.&lt;/li&gt;
&lt;li&gt;Screwed around with some &lt;a href="https://github.com/kaushikgopal/"&gt;small github projects&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Moved this blog along with all my old wordpress posts to Octopress.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not too bad.&lt;/p&gt;
&lt;h2 id="stuff-i-couldnt-get-done-in-2012"&gt;
Stuff I couldn&amp;rsquo;t get done in 2012:
&lt;a class="heading-anchor" href="#stuff-i-couldnt-get-done-in-2012" aria-label="Link to Stuff I couldn’t get done in 2012:"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Get back in shape (This one&amp;rsquo;s been there since my 2009 new year resolution list. But I have a good feeling 2013 will see this task done).&lt;/li&gt;
&lt;li&gt;Sound engineering (I did do a bunch of editing here and there for some folks, but this was stuff I had down and didn&amp;rsquo;t particularly learn anything new in the process).&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="stuff-i-want-to-do-in-2013"&gt;
Stuff I want to do in 2013
&lt;a class="heading-anchor" href="#stuff-i-want-to-do-in-2013" aria-label="Link to Stuff I want to do in 2013"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Get back in shape (In addition to hitting the gym planning to &lt;a href="http://www.fourhourworkweek.com/blog/2012/07/12/how-to-lose-100-pounds/"&gt;eat better&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Get more sleep (I have this bad feeling 2012 &lt;a href="http://news.bbc.co.uk/2/hi/health/6347043.stm"&gt;zapped some of my brain cells&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Get really good at Rails (looking at running through the &lt;a href="https://www.destroyallsoftware.com/screencasts"&gt;Destroy all Software&lt;/a&gt; and &lt;a href="http://railscasts.com/"&gt;Railcast&lt;/a&gt; screencasts).&lt;/li&gt;
&lt;li&gt;Get a job after my masters, &lt;a href="http://www.paulgraham.com/love.html"&gt;preferably doing something I love&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;</description><guid>https://kau.sh/blog/new-year-2013/</guid><pubDate>Tue, 01 Jan 2013 07:00:00 GMT</pubDate></item><item><title>
Music used on the new "All on iPad" commercial</title><link>https://kau.sh/blog/music-used-all-on-ipad-commercial/</link><description>
&lt;p&gt;Apple released &lt;a href="http://www.youtube.com/watch?v=rDvweiW5ZKQ"&gt;a new iPad ad&lt;/a&gt; during the Olympics. If you&amp;rsquo;re curious to know which song they used, it&amp;rsquo;s the song &lt;a href="http://www.youtube.com/watch?v=AoCljFnXVGE"&gt;&amp;ldquo;Driving&amp;rdquo; by Stolen Jars&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Two other songs i picked from their previous ads:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.youtube.com/watch?v=HAzPYXEMLUM"&gt;Goldengrove&lt;/a&gt; &amp;amp; &lt;a href="http://www.youtube.com/watch?v=Cqz3SBQfVBM"&gt;The Kiss&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/blog/music-used-all-on-ipad-commercial/</guid><pubDate>Thu, 09 Aug 2012 07:00:00 GMT</pubDate></item><item><title>
if Pixar did Justice League</title><link>https://kau.sh/blog/if-pixar-did-justice-league/</link><description>
&lt;p&gt;i would give my savings away for something like this.&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2012/07/jla-cg-concept-batman.jpg"
alt="Pixar Batman"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2012/07/jla-cg-concept-bruce-clark.jpg"
alt="Pixar Clark &amp;amp; Bruce"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2012/07/jla-cg-concept-superman.jpg"
alt="Pixar Superman"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
&lt;a class="center" href="https://www.deviantart.com/danielaraya/gallery/39918973/justice-league-project"&gt;
Daniel Araya
&lt;/a&gt;
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;</description><guid>https://kau.sh/blog/if-pixar-did-justice-league/</guid><pubDate>Sun, 29 Jul 2012 07:00:00 GMT</pubDate></item><item><title>
Learning and Looking Foolish</title><link>https://kau.sh/blog/learning-and-looking-foolish/</link><description>
&lt;blockquote&gt;
&lt;p&gt;I&amp;rsquo;m willing to look foolish, if it means I&amp;rsquo;ll learn something.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the best piece of advice I&amp;rsquo;ve ever received, courtesy a chemistry professor back in my days of IIT. I did miserably in that course but I picked up this piece of advice that&amp;rsquo;s been a guiding beacon ever since.&lt;/p&gt;</description><guid>https://kau.sh/blog/learning-and-looking-foolish/</guid><pubDate>Sun, 17 Jun 2012 07:00:00 GMT</pubDate></item><item><title>
on the new Twitter logo</title><link>https://kau.sh/blog/on-the-new-twitter-logo/</link><description>
&lt;p&gt;Twitter&amp;rsquo;s got a &lt;a href="http://blog.twitter.com/2012/06/taking-flight-twitterbird.html"&gt;new logo&lt;/a&gt;. It&amp;rsquo;s &lt;a href="http://labs.upperdog.se/twitter-logo-in-css/"&gt;clean and simple&lt;/a&gt;. But here&amp;rsquo;s a more astute observation from &lt;a href="http://www.underconsideration.com/brandnew/archives/twitter_gives_you_the_bird.php"&gt;underconsideration.com&lt;/a&gt; :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For the most part, all the news sources reporting on the revised bird have focused on its visual update, which I will get to soon, but the real story here is that Twitter has dropped its name from the logo. If you look at the opening image of this post, the change is quite drastic. And ballsy. Twitter has achieved in less than six years what Nike, Apple, and Target took decades to do: To be recognizable without a name, just an icon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;by way of the &lt;a href="http://daringfireball.net/linked/2012/06/07/twitter-bird"&gt;chairman&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/on-the-new-twitter-logo/</guid><pubDate>Fri, 08 Jun 2012 07:00:00 GMT</pubDate></item><item><title>
Stephen Fry - language</title><link>https://kau.sh/blog/stephen-fry-language/</link><description>
&lt;p&gt;Hear this recitation by Stephen Fry ((this man could read the instruction manual of a tape recorder and i would still link to it and love it)), then think about what he says.&lt;/p&gt;
&lt;p&gt;Hear it again, then meditate on what he says.&lt;/p&gt;</description><guid>https://kau.sh/blog/stephen-fry-language/</guid><pubDate>Sun, 20 May 2012 07:00:00 GMT</pubDate></item><item><title>
Earth - the pale blue dot</title><link>https://kau.sh/blog/earth-the-pale-blue-dot/</link><description>
&lt;blockquote&gt;
&lt;p&gt;for all our failings, despite our limitations and fallibilities , we humans are capable of greatness&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;from the book &lt;a href="http://www.amazon.com/gp/product/0345376595/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=httpkaushco-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0345376595"&gt;Pale Blue Dot: A Vision of the Human Future in Space&lt;/a&gt;
by astronomer Carl Sagan&lt;/p&gt;</description><guid>https://kau.sh/blog/earth-the-pale-blue-dot/</guid><pubDate>Sat, 19 May 2012 07:00:00 GMT</pubDate></item><item><title>
The Janitor from Columbia</title><link>https://kau.sh/blog/the-janitor-from-columbia/</link><description>
&lt;p&gt;Truly inspirational stuff. I see a Tom Hanks movie script already in the making.&lt;/p&gt;</description><guid>https://kau.sh/blog/the-janitor-from-columbia/</guid><pubDate>Mon, 14 May 2012 07:00:00 GMT</pubDate></item><item><title>
Use paper towels more efficiently - shake and fold</title><link>https://kau.sh/blog/use-paper-towels-more-efficiently-shake-and-fold/</link><description>
&lt;p&gt;I&amp;rsquo;m a little troubled that I love these kind of lifehack videos.&lt;/p&gt;</description><guid>https://kau.sh/blog/use-paper-towels-more-efficiently-shake-and-fold/</guid><pubDate>Fri, 04 May 2012 07:00:00 GMT</pubDate></item><item><title>
the first rule of Markdown</title><link>https://kau.sh/blog/the-first-rule-of-markdown/</link><description>
&lt;blockquote&gt;
&lt;p&gt;Frankly, I was surprised that Gruber mentioned Markdown previewing at all. If you’ve been around the Markdown community for any length of time, you know that the first rule of Markdown is: Gruber never talks about Markdown&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://www.leancrew.com/all-this/2012/04/that-markdown-and-web-look/"&gt;footnote from the awesome drdrang&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/the-first-rule-of-markdown/</guid><pubDate>Tue, 24 Apr 2012 07:00:00 GMT</pubDate></item><item><title>
Learning is more important that Knowing</title><link>https://kau.sh/blog/learning-is-more-important-that-knowing/</link><description>
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; You should mention what technologies you are using (because listing WCF or Java will save me the hassle of applying, and you the hassle of rejecting me), but don&amp;rsquo;t list specific tools, languages and frameworks as requirements.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Good developers who know Rails can learn Django or Node.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Going between Oracle, SQL Server, MySQL and Postgres is all pretty trivial.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;hellip; I remember being asked quite eagerly, during a pre-interview, if I knew a specific view engine (like erb, or haml&amp;hellip;). Seriously, a view engine!?!? A shitty programmer can learn a new view engine in 10 minutes. Or a more common one that always gets to me is listing some specific version control software. Or a specific library that solves a trivial problem (I see this all the time from the .NET world with DI and mocking frameworks)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Good programmers not only learn, but they want to learn. They often know enough about a technology so that they have a good feel for what problems it might solve; and only invest time truly learning it when it&amp;rsquo;s needed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;do read the article in its entirety. it&amp;rsquo;s pretty short and pure gold.&lt;/p&gt;</description><guid>https://kau.sh/blog/learning-is-more-important-that-knowing/</guid><pubDate>Wed, 18 Apr 2012 07:00:00 GMT</pubDate></item><item><title>
Your Android phone is 15s incorrect ...</title><link>https://kau.sh/blog/your-android-phone-is-15s-late/</link><description>
&lt;p&gt;Dr.Neil Degrasse Tyson on the Verge:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;.. the bulk of the Androids get their time from GPS satellites.. The time keeping bases for the GPS satellites was defined upto 1982 and since 1982, 15 seconds have been added to civil time and that 15s is not included in the Andorid time keeping settings (because their getting their time directly from GPS), whereas the iPhone compensates for this and puts the 15s backs in&amp;hellip;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You should watch the whole episode, Dr Neil is one of the most entertaining astro physicists on the planet.&lt;/p&gt;</description><guid>https://kau.sh/blog/your-android-phone-is-15s-late/</guid><pubDate>Thu, 29 Mar 2012 07:00:00 GMT</pubDate></item><item><title>
Good insight on interviewing programmers</title><link>https://kau.sh/blog/good-insight-on-interviewing-programmers/</link><description>
&lt;p&gt;If you&amp;rsquo;re in the business of hiring programmers, the article linked is a must read. It&amp;rsquo;s concise and a gold read. No excuse for not reading it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Avoid nano-questions&lt;/li&gt;
&lt;li&gt;A good engineer thinks abstractly in terms of designing and building systems, they think in terms of algorithms, components, and engineering design. They do not necessarily know all of the details of syntax of a given language, especially if they are used to a good IDE&amp;hellip;&lt;/li&gt;
&lt;li&gt;&amp;hellip; it is more important that I be able to tell you &lt;strong&gt;when and where&lt;/strong&gt; I should use inheritance, and &lt;strong&gt;when and where&lt;/strong&gt; I should use polymorphism, than to be able to &lt;strong&gt;spit off the definition&lt;/strong&gt;. ((bolded text my own))&lt;/li&gt;
&lt;li&gt;Basically: Any question that takes 5 seconds to answer with Google is not a good question. My favorite phone interview question? &amp;ldquo;What’s your favorite language?&amp;rdquo; followed by &amp;ldquo;What are it’s weaknesses?&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/good-insight-on-interviewing-programmers/</guid><pubDate>Thu, 08 Mar 2012 07:00:00 GMT</pubDate></item><item><title>
Will it improve upon on the silence? - Sri Sathya Sai Baba</title><link>https://kau.sh/blog/will-it-improve-upon-on-the-silence/</link><description>
&lt;blockquote&gt;
&lt;p&gt;Before you speak, think:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is it necessary?&lt;/li&gt;
&lt;li&gt;Is it true?&lt;/li&gt;
&lt;li&gt;Is it kind?&lt;/li&gt;
&lt;li&gt;Will it hurt anyone?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Will it improve on the silence&lt;/strong&gt;?&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="http://www.saibabaofindia.com/sai_baba_quotes_on_silence.html"&gt;Sri Sathya Sai Baba&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/will-it-improve-upon-on-the-silence/</guid><pubDate>Tue, 10 Jan 2012 07:00:00 GMT</pubDate></item><item><title>
how Tommy Emmanuel mics his acoustic guitar</title><link>https://kau.sh/blog/how-tommy-emmanuel-mics-his-acoustic-guitar/</link><description>
&lt;p&gt;for his professional albums, he uses a combination of 4 mics as explained in &lt;a href="http://www.youtube.com/watch?v=r1KI8JQEzz4"&gt;this youtube video&lt;/a&gt;. the song he plays in the end is Mombasa, easily one of his best compositions.&lt;/p&gt;
&lt;p&gt;on the move he records with an Apogee ONE as seen in &lt;a href="http://www.youtube.com/watch?v=tEHnMuq8F-0"&gt;this youtube video&lt;/a&gt;. for an on-the-move mic and sound capture device, the Apogee ONE is simply mind blowing.&lt;/p&gt;</description><guid>https://kau.sh/blog/how-tommy-emmanuel-mics-his-acoustic-guitar/</guid><pubDate>Sat, 07 Jan 2012 07:00:00 GMT</pubDate></item><item><title>
Programmer Competency Matrix</title><link>https://kau.sh/blog/programmer-competency-matrix/</link><description>
&lt;p&gt;definitely dated, but interesting yardsticks.&lt;/p&gt;
&lt;p&gt;via &lt;a href="http://news.ycombinator.com/item?id=3433311"&gt;Hacker News&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/programmer-competency-matrix/</guid><pubDate>Fri, 06 Jan 2012 07:00:00 GMT</pubDate></item><item><title>
Why it's important to know how to write for CS Majors</title><link>https://kau.sh/blog/why-its-important-to-know-how-to-write-for-cs-majors/</link><description>
&lt;p&gt;You&amp;rsquo;d be surprised how valuable the skill of eloquent communication is even in the field of IT. Keeping users apprised with your progress is perhaps the most important thing to do when working with clients. But keeping them apprised is tricky business and the ability to clearly put down in words,what to expect, is a basic necessity today.&lt;/p&gt;
&lt;p&gt;Even if you have nothing to do with CS or computers, you should read the linked article. Fantastic advice.&lt;/p&gt;</description><guid>https://kau.sh/blog/why-its-important-to-know-how-to-write-for-cs-majors/</guid><pubDate>Tue, 03 Jan 2012 07:00:00 GMT</pubDate></item><item><title>
Songs from TV</title><link>https://kau.sh/blog/songs-from-tv/</link><description>
&lt;p&gt;Proof that my TV watching addiction has not been in vain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In the Waiting Line by Zero 7 (&lt;a href="http://en.wikipedia.org/wiki/Needle_in_a_Haystack_%28House%29"&gt;House S03e13&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.youtube.com/watch?v=AZZSMXuGw-I&amp;amp;feature=related"&gt;Home by Zero 7&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Grounds for Divorce by Elbow (House Season 6 premier)&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m not Drowning by Steve Winwood (House s05e18)&lt;/li&gt;
&lt;li&gt;Company by Philip Glass (House s05e23)&lt;/li&gt;
&lt;li&gt;High School Composition (House s03e15 : the one with Dave Mathew) &lt;em&gt;possibly originally by &lt;a href="http://www.jonehrlich.com/"&gt;Jon Ehrlich&lt;/a&gt;&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Pride by Syntax (Bones s01e16)&lt;/li&gt;
&lt;li&gt;Babylon by David Carbonora (Mad Men s01e06)&lt;/li&gt;
&lt;li&gt;Believe by The Bravery (Prison Break s04e07)&lt;/li&gt;
&lt;li&gt;Perfect Day by The Constellations (Chuck s04e08)&lt;/li&gt;
&lt;li&gt;Cobrastyle by Teddybears (Chuck s01e01)&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/songs-from-tv/</guid><pubDate>Sat, 31 Dec 2011 07:00:00 GMT</pubDate></item><item><title>
What you need to know about memory and CPU</title><link>https://kau.sh/blog/ram-memory-cpu-processing-performance/</link><description>
&lt;p&gt;What you need to know about RAM, memory, CPU Processing and other important stuff that could tell you why your computer is slow.&lt;/p&gt;
&lt;h3 id="physical-rammemory"&gt;
Physical RAM/Memory
&lt;a class="heading-anchor" href="#physical-rammemory" aria-label="Link to Physical RAM/Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;RAM chips funcion quite literally like your Computer&amp;rsquo;s Memory. All the information that needs to be remembered when operating between different tasks (or even the same task) gets stored here. You know how people who have great memories can be awesome, that translates to the computer world as well, more memory = more awesome. So 8GB &amp;raquo; 4GB &amp;raquo; 2GB &amp;raquo; 1GB &amp;raquo; (this is 2011, don&amp;rsquo;t even consider anything less than 1GB).&lt;/p&gt;
&lt;h3 id="virtual-ram-or-virtual-memory"&gt;
Virtual RAM or Virtual Memory
&lt;a class="heading-anchor" href="#virtual-ram-or-virtual-memory" aria-label="Link to Virtual RAM or Virtual Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Just like how humans can&amp;rsquo;t store everything in their head, there are times when some of the stuff has to be written down or stored as reminders. When your computer&amp;rsquo;s physical RAM or memory is full, it begins to offload some of the information onto a special space carved out of your hard disk (your hard disk is like a big notebook, you&amp;rsquo;ve generally got plenty of space here). This carved out area is called a swapfile or Virtual Memory or VM. VM, is hard disk space that can be used as memory and VM size indicates the amount of disk space currently being used as memory.&lt;/p&gt;
&lt;h3 id="paging-outthrashing"&gt;
Paging out/Thrashing:
&lt;a class="heading-anchor" href="#paging-outthrashing" aria-label="Link to Paging out/Thrashing:"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;When you need to start writing everything onto your notebook, you&amp;rsquo;ve got a problem. You&amp;rsquo;re either not organized or you&amp;rsquo;re doing things too complex for your IQ to handle. In the computer world the process of writing stuff onto your virtual RAM is called Paging out or Thrashing. When you see too many page outs or thrasing cycles, you&amp;rsquo;ve got a problem because HDDs are much slower than RAM modules when calling back information (If you use solid state drives or SSDs, you&amp;rsquo;re going to see a marked performance difference. Even still, nothing like physical RAM). The number of gigabytes of information your operating system has moved between RAM and disk space is generally indicated by Page in/outs.&lt;/p&gt;
&lt;h3 id="page-outpage-ins"&gt;
Page Out/Page Ins
&lt;a class="heading-anchor" href="#page-outpage-ins" aria-label="Link to Page Out/Page Ins"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Page out is the movement of data from RAM to hard disk and Page in is the movement of data from hard disk to RAM. Ideally you shouldn&amp;rsquo;t have any page outs.&lt;/p&gt;
&lt;h3 id="cpu-processing"&gt;
CPU Processing
&lt;a class="heading-anchor" href="#cpu-processing" aria-label="Link to CPU Processing"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Physical RAM is the &lt;em&gt;destination&lt;/em&gt; to store the temporary data, but the part instructing everyone around on which data to store and where, is the CPU. You can think of it as the actual brain. More GHz = bigger brain = more awesome. So a 2 GHz processor is generally more awesome than a 1.5 GHz processor.&lt;/p&gt;
&lt;h3 id="cpu-processor-usage"&gt;
CPU Processor usage
&lt;a class="heading-anchor" href="#cpu-processor-usage" aria-label="Link to CPU Processor usage"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You won&amp;rsquo;t generally find your CPU processor usage peaked (if you do, you&amp;rsquo;ve got a problem). It&amp;rsquo;s ok to have spikes in your processor usage. These are the times when your computer&amp;rsquo;s brain is doing some heavy lifting, telling eveyone where to go and what to do. But if you find your usage constantly high, 100% most of the time, there&amp;rsquo;s some ugly memory leak going on that needs to be taken care of.&lt;/p&gt;
&lt;h3 id="gpu-video-card-memory"&gt;
GPU Video card memory
&lt;a class="heading-anchor" href="#gpu-video-card-memory" aria-label="Link to GPU Video card memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;For demanding games and other video graphics intensive processes, people generally have independent video cards that function like little CPUs themselves and have their own memory. The CPU part of a video card is called a GPU and the memory part is called -wait for it-memory. When doing heavy duty video editing, 3D graphics or games, you want a dedicated graphics card.&lt;/p&gt;
&lt;h3 id="real-memory"&gt;
Real Memory
&lt;a class="heading-anchor" href="#real-memory" aria-label="Link to Real Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The amount of real memory a program is using at that instant. This is real memory used from your memory chips on the RAM module.&lt;/p&gt;
&lt;h3 id="free-memory"&gt;
Free Memory
&lt;a class="heading-anchor" href="#free-memory" aria-label="Link to Free Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Good glorious free memory that is completely unused.&lt;/p&gt;
&lt;h3 id="wired-memory"&gt;
Wired Memory
&lt;a class="heading-anchor" href="#wired-memory" aria-label="Link to Wired Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Also knows as Resident Memory, the memory is locked or reserved and cannot be shared with other processes. OS/System processes, drivers, kernel code etc. tend to use this memory.&lt;/p&gt;
&lt;h3 id="active-memory"&gt;
Active Memory
&lt;a class="heading-anchor" href="#active-memory" aria-label="Link to Active Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Memory actively being used. The Real Memory values mentioned earlier should sum up to this value.&lt;/p&gt;
&lt;h3 id="inactive-memory"&gt;
Inactive Memory
&lt;a class="heading-anchor" href="#inactive-memory" aria-label="Link to Inactive Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Memory that has been used to cache disk I/O. You can think of inactive memory like free memory that&amp;rsquo;s been reserved for a recently closed application, but will be used when &amp;rsquo;true&amp;rsquo; free memory has been exhausted. If you have 1 GB of memory (for the sake of making this discussion easier) on a freshly booted system, imagine the OS uses 300 MB and you open an app that uses 200 MB. You now have a total of 500 MB used, and 500 free. If you close that app that was using 200 MB, the memory is marked inactive and you&amp;rsquo;ll now have 300 used (OS), 200 inactive, and 500 free. If you open an app that requires 600 MB, it&amp;rsquo;ll use the free memory then pull from the inactive pool.&lt;/p&gt;
&lt;h3 id="used-memory"&gt;
Used Memory
&lt;a class="heading-anchor" href="#used-memory" aria-label="Link to Used Memory"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Wired + Active + Inactive. Nothing useful here, move on.&lt;/p&gt;</description><guid>https://kau.sh/blog/ram-memory-cpu-processing-performance/</guid><pubDate>Tue, 20 Dec 2011 07:00:00 GMT</pubDate></item><item><title>
Fonts used in new BBC TV series Sherlock</title><link>https://kau.sh/blog/fonts-used-in-new-bbc-tv-series-sherlock/</link><description>
&lt;p&gt;So &lt;a href="https://web.archive.org/web/20120423232419/http://www.karthickgopal.net/incredible-depth-in-the-mundane/"&gt;my brother&lt;/a&gt; introduced me to this really awesome BBC TV Series: &lt;a href="http://en.wikipedia.org/wiki/Sherlock_%28TV_series%29"&gt;Sherlock&lt;/a&gt;. If you&amp;rsquo;re a &lt;a href="http://ask.metafilter.com/162092/The-Case-of-the-Unknown-Font"&gt;type nerd&lt;/a&gt; or design enthusiast, the very first thing you would notice is the brilliant typography used throughout the show.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re losing sleep finding out which fonts were used:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://new.myfonts.com/fonts/fw-acme/af-generation-z/"&gt;AF Generation Z&lt;/a&gt; (in the text messages)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://new.myfonts.com/fonts/p22/underground/"&gt;P22 London Underground&lt;/a&gt; (when deducing the clues)&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/fonts-used-in-new-bbc-tv-series-sherlock/</guid><pubDate>Mon, 19 Dec 2011 07:00:00 GMT</pubDate></item><item><title>
Steve Jobs</title><link>https://kau.sh/blog/steve/</link><description>
&lt;p&gt;&lt;a href="http://www.nytimes.com/2011/10/30/opinion/mona-simpsons-eulogy-for-steve-jobs.html"&gt;February 24, 1955 – October 5, 2011&lt;/a&gt;&lt;/p&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/uploads/2011/11/jobs.webp"
alt="Steve Jobs February 24, 1955 – October 5, 2011"
loading="lazy" decoding="async"
/&gt;
&lt;figcaption&gt;
Courtesy: Wikipedia
&lt;/figcaption&gt;
&lt;/div&gt;
&lt;/figure&gt;</description><guid>https://kau.sh/blog/steve/</guid><pubDate>Thu, 06 Oct 2011 07:00:00 GMT</pubDate></item><item><title>
parkour wizardry @ Tempest Freerunning Academy</title><link>https://kau.sh/blog/parkour-wizardry-tempest-freerunning-academy/</link><description>
&lt;p&gt;fantastic video, great stunts and carefully edited. must watch!&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=1fouvwilGWc"&gt;Tempest Freerunning Academy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;i just love the super smooth background score in the video. it&amp;rsquo;s a remix of the song Lights by Ellie Goulding and has become my favorite running tune. you can &lt;a href="http://www.bassnectar.net/2011/02/ellie-goulding-lights-bassnectar-remix/"&gt;download it for free here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;via: &lt;a href="http://kottke.org/11/04/parkour-school"&gt;kottke.org&lt;/a&gt;&lt;/p&gt;</description><guid>https://kau.sh/blog/parkour-wizardry-tempest-freerunning-academy/</guid><pubDate>Fri, 29 Apr 2011 07:00:00 GMT</pubDate></item><item><title>
great photography doesn't have to come only from DSLRs</title><link>https://kau.sh/blog/great-photography-doesnt-have-to-come-only-from-dslrs/</link><description>
&lt;p&gt;These stunning shots were taken with an iPhone camera:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="http://farm5.static.flickr.com/4078/4761002695_4dd17bd69e_z.jpg"&gt;iPhone Fashion Shoot&lt;/a&gt; &lt;a href="http://fstoppers.com/iphone/"&gt;- Fstoppers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://fstoppers.com/iphone/"&gt;iPhone 3g model shoot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.photoble.com/wp-content/uploads/2010/02/iphonephotosms4jah.jpg"&gt;Amazing pics taken with an iPhone&lt;/a&gt; &lt;a href="http://www.photoble.com/photo-inspiration/amazing-pictures-taken-with-an-apple-iphone/"&gt;- photoble.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.photoble.com/photo-inspiration/amazing-pictures-taken-with-an-apple-iphone/"&gt;photoble.com - Amazing pics with iPhone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description><guid>https://kau.sh/blog/great-photography-doesnt-have-to-come-only-from-dslrs/</guid><pubDate>Fri, 17 Dec 2010 07:00:00 GMT</pubDate></item><item><title>
New Year - 2010</title><link>https://kau.sh/blog/new-year-2010/</link><description>
&lt;p&gt;2009 was good. I didn&amp;rsquo;t do to well in terms of fulfilling &lt;a href="https://kau.sh/blog/2009"&gt;my resolutions last year&lt;/a&gt;, but I&amp;rsquo;ve got a great feeling about 2010. Here&amp;rsquo;s 2010&amp;rsquo;s resolutions:&lt;/p&gt;
&lt;h2 id="get-my-websiteblog-back-up"&gt;
Get my website/blog back up
&lt;a class="heading-anchor" href="#get-my-websiteblog-back-up" aria-label="Link to Get my website/blog back up"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve been saying this for too long now. 2010 WILL see this task done. Period..&lt;/p&gt;
&lt;h2 id="read-one-book-a-month"&gt;
Read one book a month
&lt;a class="heading-anchor" href="#read-one-book-a-month" aria-label="Link to Read one book a month"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I sought to do something similar in 2009, instead I tweaked my rss feeds and lived like an information junkie. No regrets though. i&amp;rsquo;ve learned and gathered much more information than I would have reading just books. The problem though is that my attention span has dwindled to an alarming low. Going to take care of that.&lt;/p&gt;
&lt;h2 id="get-fit-get-a-tattoo"&gt;
Get fit, get a Tattoo!
&lt;a class="heading-anchor" href="#get-fit-get-a-tattoo" aria-label="Link to Get fit, get a Tattoo!"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I recently watched the movie Avatar with my folks. Both my bro and I were fascinated with the movie and in particular, &lt;a href="http://news.bme.com/wp-content/uploads/2009/12/screen-shot-2009-12-20-at-125942-am.png"&gt;Sam Worthington&amp;rsquo;s tattoo&lt;/a&gt;. My mom remarked, if you can get as fit as him, then i&amp;rsquo;ll pick the tattoo for you. This was mind-blowing for me. You see my family is an extremely pious one, and the fact that my mom would even hint at the possibility of me getting a tattoo made me giddy with joy.. This isn&amp;rsquo;t going to be done in a year. But I want that tattoo so bad :)&lt;/p&gt;
&lt;p&gt;(Tried googling around for a pic of the tattoo, &lt;a href="http://news.bme.com/2009/12/20/avatar-the-movie/"&gt;this link&lt;/a&gt; is the best I could come up with. )&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Happy New Year folks ! Here&amp;rsquo;s wishing your 2010 is ten times more snappier and jolly than you hope it to be. What are you planning for this 2010?&lt;/strong&gt;&lt;/p&gt;
&lt;!-- [2]: http://www.karthickgopal.com/2009/12/avatar-man-nature-nightelves-and-innovation-why-you-must-watch/ --&gt;</description><guid>https://kau.sh/blog/new-year-2010/</guid><pubDate>Thu, 31 Dec 2009 07:00:00 GMT</pubDate></item><item><title>
New Year - 2009</title><link>https://kau.sh/blog/new-year-2009/</link><description>
&lt;p&gt;It&amp;rsquo;s New Year&amp;rsquo;s Eve and I&amp;rsquo;m all set with my resolutions. Until I started seriously blogging (which-if you believe-would be around mid 2008), I never really saw the point in drawing up these lists. It felt lame, pointless and a waste of time. But with the advent of my blogging ways, I realize that maintaining a resolution-list would help keep check and achieve my goals/ambitions much faster. Besides, 365 days from now it will be nice to see how my priorities and views might have changed.&lt;/p&gt;
&lt;p&gt;Brace yourself:&lt;/p&gt;
&lt;h2 id="start-regularly-gymming-again"&gt;
Start Regularly Gymming again
&lt;a class="heading-anchor" href="#start-regularly-gymming-again" aria-label="Link to Start Regularly Gymming again"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;In 2006-2007 I morphed from a whooping 100kg couch potato to a 70kg cardio-freak. From the start-mid 2008, I was diligent with my gymming and en-route to my distant dream 6-pack. Time to start putting up Arnold posters again.&lt;/p&gt;
&lt;h2 id="read-the-gazillion-books-just-bought"&gt;
Read the gazillion books just bought
&lt;a class="heading-anchor" href="#read-the-gazillion-books-just-bought" aria-label="Link to Read the gazillion books just bought"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;One of the many good habits my brother passed on to me, is a love for reading books. What the bugger also passed on was an insatiable need to purchase all the awesomest books around. 2008 gave us Reality Check, Outliers, Tribes, Presentation Zenâ€¦ all of which I already posses a copy of. This is apart from the many books I purchased in 2007 but never got the time to fully read.&lt;/p&gt;
&lt;h2 id="get-sketching"&gt;
Get Sketching
&lt;a class="heading-anchor" href="#get-sketching" aria-label="Link to Get Sketching"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I was at one point of time(sometime between the 7th and 9th grades) quite a decent artist. After my 10th (board exams), I completely left this and continued only to pursue my musical inclinations. I want to revive that lost interest and talent. Vector Graphics is also something I&amp;rsquo;m seriously looking at.&lt;/p&gt;
&lt;h2 id="learn-arabic"&gt;
Learn Arabic
&lt;a class="heading-anchor" href="#learn-arabic" aria-label="Link to Learn Arabic"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I love learning languages. I&amp;rsquo;ve spent a major part of my life in the middle-east and it&amp;rsquo;s a shame I haven&amp;rsquo;t picked the language up yet. The fascinating thing about Arabic is that the script is really easy. I can in fact read and write Arabic pretty well. Each time I try to speak though, my &lt;em&gt;bakala&lt;/em&gt; owner thinks I&amp;rsquo;m a retard.&lt;/p&gt;
&lt;p&gt;There is no later, only now! It is my ardent belief that the number one productivity-killer is procrastination. I&amp;rsquo;ve fallen short of quite a few of my goals this year primarily for this reason. No more!&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a modest list, one that currently seems doable. Time will tell&amp;hellip;&lt;/p&gt;</description><guid>https://kau.sh/blog/new-year-2009/</guid><pubDate>Wed, 31 Dec 2008 07:00:00 GMT</pubDate></item><item><title>
About</title><link>https://kau.sh/about/</link><description>
&lt;figure class="w-[70%] rounded-full! overflow-hidden mb-6 mx-auto block"&gt;
&lt;img src="https://kau.sh/images/kg.jpeg"
class="w-[70%] rounded-full! overflow-hidden mb-6 mx-auto block"
alt="Profile picture"
loading="lazy" decoding="async"
/&gt;
&lt;/figure&gt;
&lt;div class="social-links disable-ext-links flex flex-col items-center mb-12"&gt;
&lt;nav class="grid gap-4 justify-items-stretch
grid-cols-3
blog-content-width"&gt;
&lt;nav class="flex flex-col text-base md:text-lg"&gt;
&lt;h3 class="text-hc2! pb-1"&gt;#work&lt;/h3&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc"
href="https://fragmentedpodcast.com"&gt;podcast&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://kau.sh/ppt"&gt;talks&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc"
href="https://github.kau.sh"&gt;github&lt;/a&gt;
&lt;a class="pl-2 no-underline hover:text-hc" href="https://kau.sh/contact"&gt;contact&lt;/a&gt;
&lt;/nav&gt;
&lt;nav class="flex flex-col text-base md:text-lg"&gt;
&lt;h3 class="text-hc2! pb-1"&gt;#blog&lt;/h3&gt;
&lt;a class="pl-2 no-underline hover:text-hc" href="https://kau.sh/blog"&gt;posts&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://kau.sh/letter"&gt;newsletter&lt;/a&gt;
&lt;a class="pl-2 no-underline hover:text-hc" href="https://kau.sh/rss" target="_blank"
rel="noopener noreferrer"&gt;RSS&lt;/a&gt;
&lt;/nav&gt;
&lt;nav class="flex flex-col text-base md:text-lg
row-span-2 md:row-span-1 md:order-last"&gt;
&lt;h3 class="text-hc2! pb-1"&gt;#social&lt;/h3&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://bluesky.kau.sh"
target="_blank" rel="noopener noreferrer"&gt;bluesky&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://linkedin.kau.sh"
target="_blank" rel="noopener noreferrer"&gt;linkedin&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://threads.kau.sh"
target="_blank" rel="noopener noreferrer"&gt;threads&lt;/a&gt;
&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" href="https://twitter.kau.sh"
target="_blank" rel="noopener noreferrer"&gt;twitter&lt;/a&gt;
&lt;div&gt;&lt;a class="pl-2 text-ht-light no-underline hover:text-hc" rel="me noopener noreferrer"
target="_blank" href="https://hachyderm.io/@kaush"&gt;m&lt;/a&gt;&lt;a
class="text-ht-light hover:text-hc no-underline ext-hc" rel="me noopener noreferrer"
target="_blank" href="https://mastodon.kau.sh"&gt;astodon&lt;/a&gt;&lt;/div&gt;
&lt;/nav&gt;
&lt;/nav&gt;
&lt;/div&gt;
&lt;p&gt;I&amp;rsquo;m a Principal Engineer at Instacart leading the development of
our &lt;a href="https://www.caper.ai/"&gt;Caper&lt;/a&gt; smart carts.&lt;/p&gt;
&lt;p&gt;I started the popular Android podcast &lt;a href="https://fragmentedpodcast.com"&gt;Fragmented&lt;/a&gt; and am a &lt;a href="https://g.dev/kaushikgopal"&gt;Google Developer Expert&lt;/a&gt; for Android.&lt;/p&gt;
&lt;p&gt;I write about programming, tech, life, movies and music &lt;a href="https://kau.sh/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id="tools"&gt;
Tools
&lt;a class="heading-anchor" href="#tools" aria-label="Link to Tools"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;figure &gt;
&lt;div align="center"&gt;
&lt;img src="https://kau.sh/images/content/desk.webp"
width="450"
loading="lazy" decoding="async"
/&gt;
&lt;/div&gt;
&lt;/figure&gt;
&lt;h2 id="bits"&gt;
Bits
&lt;a class="heading-anchor" href="#bits" aria-label="Link to Bits"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="programming"&gt;
Programming
&lt;a class="heading-anchor" href="#programming" aria-label="Link to Programming"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://usgraphics.com/products/berkeley-mono"&gt;Berkeley Mono&lt;/a&gt; is my &lt;a href="https://kau.sh/tags/my-new-programming-font/"&gt;programming font&lt;/a&gt; of choice&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ghostty.org/"&gt;Ghostty&lt;/a&gt; for terminal stuff (along with tmux)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://zed.dev/"&gt;Zed&lt;/a&gt; as my main IDE &amp;amp; text editor
&lt;ul&gt;
&lt;li&gt;I switch between &lt;a href="https://www.anthropic.com/claude-code"&gt;Claude Code&lt;/a&gt; &amp;amp; &lt;a href="https://developers.openai.com/codex/cli/"&gt;Codex CLI&lt;/a&gt; for agentic coding&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.jetbrains.com/idea/"&gt;Intellij&lt;/a&gt; for Android/Kotlin development
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://firebender.com/"&gt;Firebender&lt;/a&gt; for agentic coding in Intellij&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="macos"&gt;
MacOS
&lt;a class="heading-anchor" href="#macos" aria-label="Link to MacOS"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt; for notes &amp;amp; todos&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.raycast.com/"&gt;Raycast&lt;/a&gt; is my launcher&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/how-to-firefox/"&gt;Firefox&lt;/a&gt; for browsing&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cleanshot.sjv.io/c/3994761/1715713/19944"&gt;CleanShot&lt;/a&gt; for screenshots on my mac&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.apple.com/logic-pro/"&gt;Logic Pro&lt;/a&gt; for editing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="atoms"&gt;
Atoms
&lt;a class="heading-anchor" href="#atoms" aria-label="Link to Atoms"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;h3 id="desk"&gt;
Desk
&lt;a class="heading-anchor" href="#desk" aria-label="Link to Desk"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/4eekcs2"&gt;Nordik&lt;/a&gt; Leather Desk Mat&lt;/li&gt;
&lt;li&gt;LEUCHTTURM1917 &lt;a href="https://amzn.to/3BeIJyw"&gt;Soft cover journal&lt;/a&gt; dotted&lt;/li&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3GMwvAk"&gt;Uniball Deluxe 0.5mm Micro pens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- - [Pilot Precise V5](https://amzn.to/46r8usK) Pens --&gt;
&lt;!-- - LEUCHTTURM1917 [Drehgriffel Gel pen](https://amzn.to/42IPdiY) --&gt;
&lt;h3 id="audio"&gt;
Audio
&lt;a class="heading-anchor" href="#audio" aria-label="Link to Audio"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amzn.to/3BcYFRO"&gt;Earthworks Ethos - 400&lt;/a&gt; microphone&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sounddevices.com/product/usbpre-2/"&gt;USBPre 2&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a href="#fn:1" class="footnote-ref" role="doc-noteref"&gt;1&lt;/a&gt;&lt;/sup&gt; audio interface&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/airpods-max-for-podcasters/"&gt;AirPods Max&lt;/a&gt; (yes for podcasting!)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="physical"&gt;
Physical
&lt;a class="heading-anchor" href="#physical" aria-label="Link to Physical"&gt;#&lt;/a&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I drive a &lt;a href="https://www.tesla.com/referral/kaushik13519"&gt;Tesla Model Y&lt;/a&gt; (and eagerly waiting to move to an &lt;a href="https://rivian.com/r3"&gt;R3&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="footnotes" role="doc-endnotes"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;These are no longer sold but are built like a tank. I&amp;rsquo;d probably pick the &lt;a href="https://amzn.to/4dkChDk"&gt;Motu 2&lt;/a&gt; if my USBPre 2 ever fails&amp;#160;&lt;a href="#fnref:1" class="footnote-backref" role="doc-backlink"&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</description><guid>https://kau.sh/about/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate></item><item><title>
Contact me</title><link>https://kau.sh/contact/</link><description>
&lt;blockquote&gt;
&lt;p&gt;The best way to reach me is via email – &lt;a href="mailto:blog@kau.sh"&gt;blog @ kau.sh&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;I&amp;rsquo;m on social media, but I mostly post one-way:&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.kau.sh"&gt; X &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://linkedin.kau.sh"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://threads.kau.sh"&gt;Threads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bluesky.kau.sh"&gt;Bluesky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.kau.sh"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- * [Threads](https://threads.kau.sh)
* [Mastodon](https://mastodon.kau.sh) --&gt;</description><guid>https://kau.sh/contact/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate></item><item><title>
RSS</title><link>https://kau.sh/rss/</link><description>
&lt;p&gt;Here are the different ways to subscribe to my content on this site via RSS.&lt;/p&gt;
&lt;h1 id="feed-links"&gt;
Feed Links
&lt;a class="heading-anchor" href="#feed-links" aria-label="Link to Feed Links"&gt;#&lt;/a&gt;
&lt;/h1&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://kau.sh/feed.xml"&gt;/feed.xml&lt;/a&gt; — General feed (&lt;em&gt;all&lt;/em&gt; the content I post)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/blog/feed.xml"&gt;/blog/feed.xml&lt;/a&gt; — Blog posts only&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kau.sh/letter/feed.xml"&gt;/letter/feed.xml&lt;/a&gt; — Newsletter letters only&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="custom-feeds"&gt;
Custom feeds
&lt;a class="heading-anchor" href="#custom-feeds" aria-label="Link to Custom feeds"&gt;#&lt;/a&gt;
&lt;/h2&gt;
&lt;p&gt;I use my own theme &amp;ldquo;Henry&amp;rdquo; for this site, so there&amp;rsquo;s ∞ flexibility to choose a customized feed based on your liking:&lt;/p&gt;
&lt;p&gt;Slap &lt;code&gt;/feed.xml&lt;/code&gt; or &lt;code&gt;/feed.json&lt;/code&gt; at the end of most urls here, and you&amp;rsquo;ll get a feed for that topic. For example I tag my blog posts that are tech tips as &lt;a href="https://kau.sh/tags/tip/"&gt;/tags/tip&lt;/a&gt;. You can get a custom feed for this using the link &lt;a href="https://kau.sh/tags/tip/feed.xml"&gt;/tags/tip/feed.xml&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://kau.sh/henry"&gt;Henry&lt;/a&gt; for Hugo is &lt;a href="https://github.com/kaushikgopal/henry-hugo"&gt;open source&lt;/a&gt;.&lt;/p&gt;</description><guid>https://kau.sh/rss/</guid><pubDate>Mon, 01 Jan 0001 00:00:00 GMT</pubDate></item></channel></rss>