<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Roberto Selbach</title><link>https://rselbach.com/</link><description>Recent content on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Fri, 14 Aug 2026 12:23:08 -0400</lastBuildDate><atom:link href="https://rselbach.com/index.xml" rel="self" type="application/rss+xml"/><item><title>Going Back in Time</title><link>https://rselbach.com/blog/2026-08-14-going-back-in-time/</link><pubDate>Fri, 14 Aug 2026 12:23:08 -0400</pubDate><guid>https://rselbach.com/blog/2026-08-14-going-back-in-time/</guid><description>&lt;p&gt;I&amp;rsquo;ve been trying to adapt my coding workflow to LLMs for a while. The way I use
LLMs has definitely changed over time and I&amp;rsquo;ve written about it &lt;a href="https://rselbach.com/llms-are-tools-not-replacements/"&gt;here&lt;/a&gt;
and &lt;a href="https://rselbach.com/blog/2026-05-20-update-llm-workflow/"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my experience, around late 2025/early 2026, SOTA models
crossed some threshold and made a significant leap in coding performance.
When Opus 4.5 and then later GPT-5.3-Codex came out, suddenly you could trust
the output of LLMs a lot more. Sure they still made some mistakes
(and still do) but these were significantly rarer than before. You could
&lt;em&gt;expect&lt;/em&gt; the code to be right, whereas I felt like before then my expectation
was that we&amp;rsquo;d have to iterate quite a bit.&lt;/p&gt;
&lt;p&gt;Over the months following that big leap, I started shipping so much more,
especially my own stuff. It&amp;rsquo;s incredibly easy to build whatever you want now with
LLMs. At work, the speedup was a little less dramatic since you have to be
careful with production software that&amp;rsquo;s actually being used by a ton of people,
and then there are reviews, etc. But still, LLMs became a huge part of my
workflow. I rarely wrote code by hand anymore.&lt;/p&gt;
&lt;p&gt;Until, that is, a few weeks ago when suddenly our access to modern LLMs was cut
at work. We were told that we could no longer use anything except an in-house
harness that routes us to a pool of older models. The most capable of
the models is Sonnet 4.5. We can&amp;rsquo;t select the models: the harness decides which
model to route requests to, so it&amp;rsquo;s hard to tell how often we actually even get
to use Sonnet.&lt;/p&gt;
&lt;p&gt;Needless to say, this was a bit of a shock and many an argument was had.
Regardless, it is what it is and we&amp;rsquo;ve had to adapt. And this is where that big
leap comes into play. Turns out we got used to workflows that are no longer
viable. I can no longer have an LLM write a feature. It just can&amp;rsquo;t.&lt;/p&gt;
&lt;p&gt;These LLMs now like to rewrite things 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;f&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foos&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;bar&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;f&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 style="color:#a6e22e"&gt;baz&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ctx&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;f&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;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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;range&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foos&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;f&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foos&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;i&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;bar&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;f&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;result&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;baz&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ctx&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;f&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 style="color:#a6e22e"&gt;result&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;I can guess why it&amp;rsquo;s changing the &lt;code&gt;for&lt;/code&gt; variable: it&amp;rsquo;s trying to protect against
the old Go gotcha in which the reference of the loop variable was kept after the
end of the iteration, but this hasn&amp;rsquo;t been the case for years and certainly isn&amp;rsquo;t
an issue on the Go version we require in the codebase. Why the &lt;code&gt;result&lt;/code&gt; variable
introduction is beyond me. None of these rewrites were needed or requested, the
harness or the LLM simply thought this has to change.&lt;/p&gt;
&lt;p&gt;Even things like code reviews are more challenging. I got used to having LLMs
assist in reviewing PRs, but now with these models these reviews are much less
useful. I realized I had to stop relying on them after the available models went 0 for
7: 0 actual flaws in 7 findings.&lt;/p&gt;
&lt;p&gt;The example that brought painful memories of the past was when one of the models
found a critical issue in a code snippet like this (not the actual 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Foo&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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 style="color:#66d9ef"&gt;nil&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;status&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Error&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;codes&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;InvalidArgument&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;missing foo&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;bar&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Baz&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;req&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Foo&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Baz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It reported a critical issue: if &lt;code&gt;req.Foo&lt;/code&gt; was nil, then
referencing &lt;code&gt;req.Foo.Baz&lt;/code&gt; would cause a nil-pointer dereference panic. Obviously
that is not true here because if &lt;code&gt;req.Foo&lt;/code&gt; was nil, the function would have
returned before the dereference. I pointed it out to the LLM which happily responded
&amp;ldquo;You&amp;rsquo;re absolutely right&amp;hellip;&amp;rdquo;&lt;/p&gt;
&lt;p&gt;I rarely see a modern LLMs miss a validation/initialization right above a use. The
other false positives were
mostly like this: easy to disprove at a glance. But others would be less obvious
to someone not familiar with the system. The claims were clearly wrong to me,
but proving them wrong involved some investigation, which I had to
do just in case I was wrong.&lt;/p&gt;
&lt;p&gt;All of this takes time. Individually, these false positives don&amp;rsquo;t look like
much, but they all add up. And then you have
people on the team trusting the LLM reviews and posting their findings in your
PRs, and then you need to spend time proving that no, that&amp;rsquo;s not right. It&amp;rsquo;s a
lot of wasted time for something that&amp;rsquo;s supposed to accelerate you.&lt;/p&gt;
&lt;p&gt;So, while for my personal projects LLMs feel like magic, at work I&amp;rsquo;m back in time
by maybe a year or so. I can no longer trust the output of my LLM. It stopped
accelerating me and started slowing me down. I can only safely use LLMs for small and &lt;em&gt;very&lt;/em&gt;
specific changes that I have to overexplain, review line by line, and iterate.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to see how much things have evolved in a year until you have to go
back in time.&lt;/p&gt;</description></item><item><title>谢谢你，中国！ (Thank you, China!)</title><link>https://rselbach.com/blog/2026-07-01-thank-you-china/</link><pubDate>Wed, 01 Jul 2026 18:51:58 -0400</pubDate><guid>https://rselbach.com/blog/2026-07-01-thank-you-china/</guid><description>&lt;p&gt;First things first: Happy Canada Day, everyone! Bonne fête du Canada, tout le monde !&lt;/p&gt;
&lt;p&gt;Yes, I know. Starting a post that celebrates Canada Day with a giant title thanking China might feel a bit ironic, if not entirely contradictory.
But stick with me for a minute. Because if you are a Canadian working in tech right now, navigating a world where our longest-standing friendship
and alliance has ruptured into open hostility from the US government, you should be thanking them too.&lt;/p&gt;
&lt;p&gt;For decades, the Canadian tech ecosystem operated under an unspoken assumption: we were fundamentally part of the same digital backyard as Silicon Valley.
We relied on the same infrastructure, trusted the same partners, and assumed that a shared border meant shared access to the future of technology.&lt;/p&gt;
&lt;p&gt;The rise of large language model regulation in the US has shattered that illusion. As LLMs became the foundational infrastructure for the next generation
of software, the narrative shifted toward a suffocating, centralized reality. Suddenly, it looked like access to &amp;ldquo;intelligence&amp;rdquo; would be controlled by a couple
of trillion-dollar American tech giants, operating under the tight regulatory grip and arbitrary &amp;ldquo;national security mandates&amp;rdquo; of an increasingly
authoritarian and hostile US government. That&amp;rsquo;s&amp;hellip; not great.&lt;/p&gt;
&lt;p&gt;The message to us and to the rest of the world was clear: if you want to build next-gen software, you have to rent it from them and you have to behave or they&amp;rsquo;ll turn off the tap.&lt;/p&gt;
&lt;p&gt;When the US government forced Anthropic to turn Fable and Mythos off a couple of weeks ago, it hit me how all of a sudden things could turn. And that&amp;rsquo;s when
I starting thinking about how different things could have been had China not chosen the path of opensource.&lt;/p&gt;
&lt;h2 id="the-open-source-weapon"&gt;The Open-Source Weapon&lt;/h2&gt;
&lt;p&gt;Had Chinese AI labs followed the American playbook, the trap would have snapped shut.&lt;/p&gt;
&lt;p&gt;When the US president put measures to prevent chips from being sold to China, they inadvertently scored one of the biggest own goals ever, because faced with severe hardware
restrictions and intense domestic competition, Chinese companies and research labs like Alibaba, Z.AI and Moonshot took a radically different path. While American companies felt safe
in closing their gates, shifting from open research to aggressive, closed-source monopolies, the Chinese chose to weaponize open weights.&lt;/p&gt;
&lt;p&gt;They realized the fastest way to bypass the American monopoly was to simply give the models away.&lt;/p&gt;
&lt;p&gt;And they didn&amp;rsquo;t just dump mid-tier toys. Their models are improving at a much higher pace compared to the American ones and have pretty much caught up in practical terms. They&amp;rsquo;ve
consistently commoditized the technology that the US would love to leverage against us.&lt;/p&gt;
&lt;h1 id="autonomy-through-proliferation"&gt;Autonomy through Proliferation&lt;/h1&gt;
&lt;p&gt;There is a profound irony here. China has long been known for its heavy domestic internet censorship and state oversight. Yet, they&amp;rsquo;ve inadvertently become the world&amp;rsquo;s greatest
guarantor of decentralized, open-source AI infra.&lt;/p&gt;
&lt;p&gt;By flooding the ecosystem with high-quality opensource models, they&amp;rsquo;ve made it functionally impossible for an authoritarian US government to lock down the future of software development.&lt;/p&gt;
&lt;p&gt;Once a model like &lt;a href="https://huggingface.co/zai-org/GLM-5.2"&gt;GLM 5.2&lt;/a&gt; or &lt;a href="https://huggingface.co/moonshotai/Kimi-K2.7-Code"&gt;Kimi K2.7&lt;/a&gt; are on &lt;a href="https://huggingface.co/"&gt;Hugging Face&lt;/a&gt;, downloaded, and running locally on hardware right here in Canada, the gates cannot be closed. It belongs to the global developer community.
Better yet, it can be fine-tuned, quantized, and run entirely offline.&lt;/p&gt;
&lt;p&gt;This isn&amp;rsquo;t about blind defence of the Chinese government or anything. It&amp;rsquo;s a pragmatic look at power dynamics: hegemony is the absolute enemy of innovation, and proliferation is the ultimate antidote to bullying.&lt;/p&gt;
&lt;p&gt;When global superpowers compete by releasing open infrastructure, developers win. In fact, the aggressive opensourcing of Chinese frontier models may threaten the duopoly of Anthropic and OpenAI. OpenAI has already postponed their IPO, as companies start having trouble reconciling the costs of inference charged by these labs with whatever gains in productive they&amp;rsquo;re seeing from them.&lt;/p&gt;
&lt;p&gt;Because Chinese labs chose to open up their models, the dream of an API-locked AI dystopia where Canada and other &amp;ldquo;middle powers&amp;rdquo; can be frozen out at the border is dead. We have choices, which means we have autonomy.&lt;/p&gt;
&lt;p&gt;So, to the engineers and teams pushing the boundaries of open weights on the other side of the world, from a developer celebrating a complicated Canada Day: &lt;strong&gt;谢谢你，中国。&lt;/strong&gt; You kept the future of AI open when our neighbours tried to lock it away.&lt;/p&gt;</description></item><item><title>Updated LLM Coding Workflow</title><link>https://rselbach.com/blog/2026-05-20-update-llm-workflow/</link><pubDate>Wed, 20 May 2026 18:51:58 -0400</pubDate><guid>https://rselbach.com/blog/2026-05-20-update-llm-workflow/</guid><description>&lt;p&gt;Back in January &lt;a href="https://rselbach.com/llms-are-tools-not-replacements/"&gt;I posted about how I view LLMs&lt;/a&gt;, which included my workflow of doing LLM-assisted coding. To summarize, my workflow was:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reverse rubber-ducking&lt;/li&gt;
&lt;li&gt;Planning and writing a spec file&lt;/li&gt;
&lt;li&gt;Implement each phase of the plan, one by one&lt;/li&gt;
&lt;li&gt;Validation and commit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I can say lots have changed since January. For one, models are significantly better and more reliable. As well, I feel like I&amp;rsquo;ve got better at steering them. If I look at the above list by itself, without details, it doesn&amp;rsquo;t feel like things changed so much, but look closer and it&amp;rsquo;s a whole new world. My current workflow is an evolution of the above.&lt;/p&gt;
&lt;p&gt;Before continuing, let me make something clear: this is my &lt;em&gt;professional&lt;/em&gt; workflow. It&amp;rsquo;s what I use to write production code on cloud services.&lt;/p&gt;
&lt;h2 id="phase-0-reverse-rubber-ducking"&gt;Phase 0: Reverse Rubber-ducking&lt;/h2&gt;
&lt;p&gt;I still have this, but I no longer use a chat interface. I start directly with an agent (almost always &lt;a href="https://opencode.ai"&gt;OpenCode&lt;/a&gt;) and I now first get the agent to engage with the code before anything else. Let&amp;rsquo;s say I need to make a change to the flux capacitors, so I go and tell the agent what I think happens:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is cloud-service-foo and it handles requests to create farbelizer connectors. I believe it then nimbolizes the farbelizers before sending them to cloud-service-bar that processes them through the flux capacitors. Check what the actual flow is and summarize it for me.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most of the times &amp;ndash; though not always &amp;ndash; I know exactly how the flow works, but I do this to sort of prime the LLM for discussing what I want to change. I just found that it tends to work well for me; better then just telling it directly what I want to change.&lt;/p&gt;
&lt;p&gt;An indirect effect of doing this is that sometimes it will tell me something that doesn&amp;rsquo;t meet my understanding, so I ask details to figure out if it&amp;rsquo;s really something I missed or just something the LLM got wrong.&lt;/p&gt;
&lt;p&gt;When I know the LLM has the context, I will say something like&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I have an issue where if the farbelizer connector starts with &amp;ldquo;foo&amp;rdquo;, then the flux capacitors should suppress the harmonic back-feeding before it reaches the primary gimbal housing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I often add a little more about what the actual goal is, but it&amp;rsquo;s something like this. This usually causes the LLM to tell me what it thinks should be done. It will sometimes ask a question or two, but eventually it will give me a solution. Many times the solution is one I know I don&amp;rsquo;t want due to some constraint and I will tell it. Sometimes I steer it a bit more and tell it what I think we should do.&lt;/p&gt;
&lt;p&gt;And then when I&amp;rsquo;m happy, I&amp;rsquo;ll say:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Plan a series of independent PRs to implement this. List which ones can be done in parallel vs linearly&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That&amp;rsquo;s it. That&amp;rsquo;s the entire plan phase now. I no longer need a spec file.&lt;/p&gt;
&lt;h2 id="phase-1-implementation"&gt;Phase 1: Implementation&lt;/h2&gt;
&lt;p&gt;Given the list of PRs, I will ask it to implement them either a few in parallel or, if there&amp;rsquo;s a dependency, one by one. I also now let the LLM agent commit its changes. (if they were in parallel, I also let it push the changes.)&lt;/p&gt;
&lt;p&gt;Again, that&amp;rsquo;s it?&lt;/p&gt;
&lt;h2 id="phase-2-validation"&gt;Phase 2: Validation&lt;/h2&gt;
&lt;p&gt;I then test the work locally, I still insist on doing that because I don&amp;rsquo;t want to cause a SEV. I&amp;rsquo;ll then push the branches and review the diffs myself in GitHub before asking for others to review: I want to avoid wasting people&amp;rsquo;s time.&lt;/p&gt;
&lt;h3 id="you-still-have-to-watch-them"&gt;You still have to watch them&lt;/h3&gt;
&lt;p&gt;I had an interesting interaction with GPT 5.5 a few weeks ago, where it wrote code that was akin 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;attempt&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&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;for&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;attempt&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;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;attempt&lt;/span&gt; &amp;gt; &lt;span style="color:#a6e22e"&gt;maxAttempts&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 style="color:#a6e22e"&gt;errTooManyAttemps&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;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fetchData&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;ctx&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;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#66d9ef"&gt;switch&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:#a6e22e"&gt;errors&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Is&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;errTimeout&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Warning: Timeout occurred. Retrying...&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;case&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;errors&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Is&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;context&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Canceled&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34; -&amp;gt; Context was canceled. Exiting...&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;default&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 style="color:#a6e22e"&gt;err&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;time&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sleep&lt;/span&gt;(&lt;span style="color:#ae81ff"&gt;100&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;time&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Millisecond&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;When I saw that, I immediately knew it didn&amp;rsquo;t look right, so I asked the agent about the case with the &lt;code&gt;context.Canceled&lt;/code&gt; and it happily explained to me that it would log the error and then return with &lt;code&gt;default:&lt;/code&gt;. I said, no, it won&amp;rsquo;t, that&amp;rsquo;s not how Go switches work. And it insisted! &amp;ldquo;I understand your confusion, but because there is no &lt;code&gt;break&lt;/code&gt; statement, the code will simply fall through the next case.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;No, it forking won&amp;rsquo;t! So I told it to prove it by writing a test that returned a context canceled. It did, caught the infinite loop and conceded.&lt;/p&gt;
&lt;p&gt;My point? They can still make mistakes. I have to check them.&lt;/p&gt;
&lt;h2 id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That said, I will concede that the LLMs are so much better now and that these errors are getting more and more rare. My flow is much quicker than before. I still review code like a caveman, I still make sure the LLM gets what I want it to do. But I basically killed the entire &amp;ldquo;plan&amp;rdquo; step. It&amp;rsquo;s just not needed. And I almost never write code by hand.&lt;/p&gt;</description></item><item><title>Your App Subscription Is Now My Weekend Project</title><link>https://rselbach.com/your-sub-is-now-my-weekend-project/</link><pubDate>Sun, 18 Jan 2026 20:13:59 +0000</pubDate><guid>https://rselbach.com/your-sub-is-now-my-weekend-project/</guid><description>&lt;p&gt;I pay for a lot of small apps. One of them was &lt;a href="https://wisprflow.ai"&gt;Wispr Flow&lt;/a&gt; for dictation. That&amp;rsquo;s $14 CAD/month that I was paying until I had a few lazy days visiting my mother. And then on the afternoon of New Year&amp;rsquo;s Day, I vibecoded &lt;a href="https://github.com/rselbach/jabber"&gt;Jabber&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now, don&amp;rsquo;t get me wrong, Jabber is not &amp;ldquo;production quality.&amp;rdquo; I would never sell it as a product or even recommend it to other people, but it does what &lt;em&gt;I&lt;/em&gt; needed from Wispr Flow, and it does &lt;em&gt;exactly&lt;/em&gt; the way I want it to. For free.&lt;/p&gt;
&lt;p&gt;At work, I&amp;rsquo;m often asked to make small videos showing some support agent how something works, or sharing some knowledge with new team members, or just a regular demo of something. In the past, I used to use &lt;a href="https://loom.com"&gt;Loom&lt;/a&gt;, which costs $15/month. So after creating Jabber, I got excited and vibecoded &lt;a href="https://github.com/rselbach/reel"&gt;Reel&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Reel does &lt;em&gt;exactly&lt;/em&gt; what I wanted Loom to do: I can record my camera, I can move it around, and I get to trim the video after it&amp;rsquo;s done (I don&amp;rsquo;t remember being able to do that with Loom).&lt;/p&gt;
&lt;p&gt;Then just yesterday, a friend of mine was telling me how he got tired of paying for &lt;a href="https://typora.io"&gt;Typora&lt;/a&gt; and decided to vibecode his own Markdown editor. And &lt;em&gt;that&lt;/em&gt; gave me the idea of creating an editor for my blog.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_78d5019e463792fd.webp 320w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_c43c6101a7a73158.webp 480w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_bcf469546854ae82.webp 640w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_5e753896967024b0.webp 800w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_6ed8313eb1c9d29e.webp 960w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_386a480e1a7889dd.webp 1100w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_2090bdb842f89417.webp 1280w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_be11856be330c4b8.webp 1600w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_48867d25b3635d9d.webp 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z.png"
 alt=""
 loading="lazy"
 decoding="async" width="2484" height="1458" srcset="https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_478d7be13c06585c.png 320w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_3bbb1b9395c99769.png 480w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_689f99c891599ed3.png 640w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_94e5c0e2ce8f3565.png 800w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_93c0ec5ddcee15f8.png 960w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_4acaf30c0475fa51.png 1100w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_5d28c9253e530c8a.png 1280w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_8b4b8e96b6fe762a.png 1600w, https://rselbach.com/your-sub-is-now-my-weekend-project/image-2026-01-18T21-04-02Z_hu_63288fb36f989a69.png 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s &lt;a href="https://github.com/rselbach/hugora"&gt;Hugora&lt;/a&gt;! Yes, horrible name, but who cares? It&amp;rsquo;s just for me. I get to edit my &lt;a href="https://gohugo.io"&gt;Hugo&lt;/a&gt; blog &lt;em&gt;just the way I like&lt;/em&gt;. It even shows my site theme.&lt;/p&gt;
&lt;p&gt;You see the pattern here?&lt;/p&gt;
&lt;p&gt;All of these $10/month apps are suddenly a weekend project for me. I&amp;rsquo;m an engineer, but I have never written a single macOS application. I&amp;rsquo;ve never even read Swift code in my life, and yet, I now can get an app up and running in a couple of hours. This is &lt;em&gt;crazy&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Last year, a &lt;a href="https://medium.com/@stephanemboghossian/software-is-becoming-a-commodity-273741174d04"&gt;Medium post predicted&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Most standalone apps will be “features, not products” in the long run — easy to copy and bundle into larger offerings.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And I think we&amp;rsquo;re there. I don&amp;rsquo;t know what that means for the future of our industry, but it does seem like a big shift.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m still skeptical of vibecoding in general. As I mentioned above, I would not trust my vibecoding enough to make these into products. If something goes wrong, &lt;em&gt;I&lt;/em&gt; don&amp;rsquo;t know how to fix it. Maybe my LLM friends can, but I don&amp;rsquo;t know. But vibecoding is 100% viable for personal stuff like this: &lt;strong&gt;we now have apps on demand.&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Being Specific when Pairing with Bots</title><link>https://rselbach.com/being-specific-when-pairing-with-bots/</link><pubDate>Wed, 14 Jan 2026 13:12:00 -0500</pubDate><guid>https://rselbach.com/being-specific-when-pairing-with-bots/</guid><description>&lt;p&gt;A couple of days ago I posted about &lt;a href="https://rselbach.com/llms-are-tools-not-replacements/"&gt;my workflow&lt;/a&gt; and I made light fun of something I do:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I also give it context to save time. The agents nowadays are very smart and can find their way, but I can shortcut that by giving it hints “in &lt;code&gt;internal/foo/foo.go&lt;/code&gt; there’s a function called &lt;code&gt;DoFoo()&lt;/code&gt; and it does this and that and I want it to do that other thing before that” or whatever. Less tokens, faster iteration. &lt;strong&gt;This is probably astrology for nerds, pure superstition at this point, but I still do it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Turns out, maybe it&amp;rsquo;s not really astrology for nerds? Today, &lt;a href="https://x.com/sqs/status/2011432209165799547?s=20"&gt;Quinn Slack shared&lt;/a&gt; an article about &lt;a href="https://ampcode.com/how-to-pair-with-an-agent"&gt;How To Pair with an Agent&lt;/a&gt; and in it, the author says &lt;em&gt;&amp;ldquo;The more you can specify, the better&amp;rdquo;&lt;/em&gt; and gives this example of a good, specified prompt:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Specified prompt:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Build a new API endpoint for user notifications. Follow the pattern in &lt;code&gt;src/api/messages.ts&lt;/code&gt; as your reference. Run the API tests after each step. Don&amp;rsquo;t move on until they pass.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You gave it a reference to follow and a way to check its own work. Now you can step away. Let the agent iterate until the tests pass.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So maybe it&amp;rsquo;s not astrology for nerds, but a good practice? I always felt like it gave me better results, but I wasn&amp;rsquo;t 100% sure if it wasn&amp;rsquo;t just a superstition. Nice to see it&amp;rsquo;s probably not.&lt;/p&gt;</description></item><item><title>Thoughts on Amp's ad-supported business model</title><link>https://rselbach.com/amps-ad-supported-business-model/</link><pubDate>Tue, 13 Jan 2026 14:19:23 +0000</pubDate><guid>https://rselbach.com/amps-ad-supported-business-model/</guid><description>&lt;p&gt;I&amp;rsquo;m agent-agnostic, in which I don&amp;rsquo;t use only one. I keep changing from time to time. &lt;a href="https://rselbach.com/llms-are-tools-not-replacements/"&gt;My earliest forays into agentic programing&lt;/a&gt; were with &lt;a href="https://claude.ai/claude-code"&gt;Claude Code&lt;/a&gt;, which was then and still is probably the gold standard. But since then I&amp;rsquo;ve tried quite a few: &lt;a href="https://chatgpt.com/features/codex/"&gt;Codex CLI&lt;/a&gt; (good models, barebones agent), &lt;a href="https://factory.ai"&gt;Droid&lt;/a&gt; (not a fan), &lt;a href="https://opencode.ai"&gt;OpenCode&lt;/a&gt; (big fan!), and &lt;a href="https://ampcode.com"&gt;Amp&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using Amp for a while, but only from time to time to see its evolution. With the move to Opus 4.5, I found that Amp has become very capable and I started using it more and more until it became my go-to agent.&lt;/p&gt;
&lt;p&gt;The one downside of Amp is that it can get expensive. Since it&amp;rsquo;s not tied to any of the labs, it needs to charge API pricing, which can get expensive if you use it a lot, &lt;a href="https://x.com/thdxr/status/2002076272248361063"&gt;though maybe less than most people think&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But it&amp;rsquo;s undeniable that there is a psychological impact at seeing money constantly being drawn as you use, even if at the end of the day you&amp;rsquo;d spend the same as with a subscription.&lt;/p&gt;
&lt;p&gt;These are challenges the Amp people have been working on for a while. Then not that long ago, they came up with a first attempt: an ad-supported free tier. You could use Amp up to $10 worth of API per day as long as you agreed to see ads on your agent.&lt;/p&gt;
&lt;p&gt;To be clear, &lt;em&gt;ads are optional&lt;/em&gt;. You only see the ads if you choose to and if you do, you get $10 worth of inference per day, using some cheaper models. This is how the ads appear:&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/amps-ad-supported-business-model/amp_hu_9673fff176ee1d6a.webp 320w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_14c12858541a97e.webp 480w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_48565f2511c82ae.webp 640w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_c5ea9ffc0256a587.webp 800w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_632f3bd0e076edd9.webp 960w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_ace846ed036a1847.webp 1100w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_6ca448b9c43f90fa.webp 1280w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_1aa1282a28039c26.webp 1600w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_a2f1316732abdb8c.webp 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/amps-ad-supported-business-model/amp.png"
 alt=""
 loading="lazy"
 decoding="async" width="2836" height="1988" srcset="https://rselbach.com/amps-ad-supported-business-model/amp_hu_abea963ca0a5c6a4.png 320w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_1ef74a2160024bb1.png 480w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_94b9271bcfd9b3a.png 640w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_c859162e5922d6ba.png 800w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_54eb671290100749.png 960w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_1c171955b1ced642.png 1100w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_238a0ec6367568b0.png 1280w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_a1d8fa7fd82874e5.png 1600w, https://rselbach.com/amps-ad-supported-business-model/amp_hu_f73ddc34e776dec1.png 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Personally, I find them unobstructive, but opinions may vary. Now, you may notice that I emphasized the fact that these ads are options. The reason why I did so is that it appears that the idea of ads hits a nerve in some people. Ever since their free tier came out, I&amp;rsquo;ve seen several tweets of people announcing their refusal to ever try Amp because they don&amp;rsquo;t want ads.&lt;/p&gt;
&lt;p&gt;Now Amp came up with a next step, whereas paying customers can also enable Amp Free and get those $10 a day of inference and their balance will only be drawn from once they exhaust their free allowance. That&amp;rsquo;s the equivalent of $300 a month of free inference. Not only that, but you can use that free allowance with Opus 4.5.&lt;/p&gt;
&lt;p&gt;I understand the aversion to ads. I share it. But $300/month is an incredible value to ignore. So I decided to enabled Amp Free and use Amp exclusively this month to see how much I will have
spent by the end of the month. My suspicion is that I&amp;rsquo;ll spend less than my Claude Max subscription.&lt;/p&gt;
&lt;p&gt;But something that I think is very important to remember is that there&amp;rsquo;s a whole world of engineers outside of the developed world. For a developer in, say, South America, the cost of a
Claude or Codex subscription is prohibitive. This is keeping a whole world of engineers out of the LLM revolution. Amp&amp;rsquo;s approach offers a way for them to have access to
premium models they otherwise wouldn&amp;rsquo;t have access to.&lt;/p&gt;</description></item><item><title>LLMs Are Tools, Not Replacements</title><link>https://rselbach.com/llms-are-tools-not-replacements/</link><pubDate>Mon, 12 Jan 2026 20:19:23 +0000</pubDate><guid>https://rselbach.com/llms-are-tools-not-replacements/</guid><description>&lt;p&gt;I’ve been meaning to write this post for a bit, but never found the right time. I guess this is it. Until sometime last year, I was more or less an AI-skeptic. I say more or less because I was always very interested in the technology. I built my own LLM to learn about it and I thought then, as I do now, that the &lt;em&gt;technology&lt;/em&gt; is incredible.&lt;/p&gt;
&lt;p&gt;And yet, I had tried using LLMs to help with coding and my experiences were not great. I used LLMs to write one-off scripts for me, they were very good at that. But whenever I tried to use them to help me write “production code”, they would hallucinate or get stuck in “bug loop”. I felt like I was spending more time dealing with the aftermath than I’d do writing it all by hand. I even disabled Copilot autocomplete because I felt like it was distracting.&lt;/p&gt;
&lt;p&gt;Fast forward to today and most of my code is written by LLMs. How this change happened is a combination of how much the tooling improved but also the recognition that I was holding it wrong.&lt;/p&gt;
&lt;p&gt;Now, don’t get me wrong. This post is not meant to convince anyone of anything. I’m not selling anything here. This post is for engineers who are curious about how others work with LLMs and trying to find their own workflow. I’ll show you exactly how I work now and how it works &lt;em&gt;for me&lt;/em&gt;.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="the-bug-that-changed-my-mind"&gt;The bug that changed my mind&lt;/h2&gt;
&lt;p&gt;As mentioned, I was a bit of a skeptic. I knew LLMs were good at writing one-off scripts and I was using them a lot for that, but not more than that. Then one day someone asked for help with a bug.&lt;/p&gt;
&lt;p&gt;We had this multicell architecture and we had a proxy/multiplexer that would decide where any given request should be routed to. Once that decision was made, the request would be proxied to an ALB using a custom transport. The ALB had resource mappings to know where inside a given cell things were hosted, so the custom transport requested a URL from the ALB, the ALB responded with a redirect to the actual destination inside the cell it belonged to. The custom transport would require the request and make it to the correct destination.&lt;/p&gt;
&lt;p&gt;The bug: seemingly at random, some requests would succeed and some would not and no one could figure out why. So I started looking and quickly found that it wasn’t random at all: requests with bodies would fail. When I saw that, I immediately thought it was the custom transport eating the body, except I remembered writing that transport and found it hard to believe the issue was there. And upon looking at the code, it seemed fine. I added logging and went about trying to reproduce the issue. The code seemed correct, but the issue was still there.&lt;/p&gt;
&lt;p&gt;After a while, I decided to try Claude Code. I launched it on the repo and explained the problem. I’ll admit I did not have high expectations, but hoped that maybe it could give me some insight that would help. To my surprise, in about 40s it came back saying it had found the issue: the transport was eating the request bodies. My first reaction was being frustrated because I knew I had already looked at it and the issue was not there. I thought Claude was being dumb. Except I noticed it was showing code that didn’t look like what I was looking at. Long story short: at some point, someone had copied and pasted some code and added a second custom transport somewhere where it shouldn’t, and &lt;em&gt;that&lt;/em&gt; transport had a bug.&lt;/p&gt;
&lt;p&gt;I didn’t fully convert then, but I started paying more attention. I began using LLMs for debugging and code reviews, things where being wrong was mostly harmless and I could verify the output easily. Over time, that expanded. Now we’re here.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="the-mistake-i-made-early-on"&gt;The mistake I made early on&lt;/h2&gt;
&lt;p&gt;When I first tried AI coding tools, I treated them like code generators. Describe what you want, get code back, paste it in, repeat. This was the intuitive way to use them, and it’s wrong as far as I am concerned.&lt;/p&gt;
&lt;p&gt;For those one-off scripts I mentioned before, I recognize now that I was “vibe coding” them. But that was fine because they were only going to be used by me. But I don’t let LLMs write unsupervised code that I need to ship for others. So the problem is that generated code requires review. Review requires understanding. If you didn’t think through the implementation yourself, you’re now reading code you don’t fully understand, looking for bugs you can’t anticipate, in an approach you didn’t choose. You’re doing &lt;em&gt;more&lt;/em&gt; cognitive work than if you’d just written it yourself, and the code is probably worse.&lt;/p&gt;
&lt;p&gt;The mental shift that made everything click for me was that LLMs are tools, just like LSPs were tools, and pre-LLM autocomplete was a tool. They’re not a replacement, but a complement. A junior engineer who has read everything but never built anything. Lots of talent but absolutely not trusted unsupervised.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="my-workflow"&gt;My workflow&lt;/h2&gt;
&lt;p&gt;This is how &lt;em&gt;I&lt;/em&gt; work with LLMs. I found that this works very well for me. I am aware that it is a much more involved workflow than a lot of people’s.&lt;/p&gt;
&lt;h3 class="wp-block-heading" id="phase-0-reverse-rubber-ducking"&gt;Phase 0: Reverse Rubber-ducking&lt;/h3&gt;
&lt;p&gt;I don’t start in an agent. I start in &lt;a href="https://claude.ai"&gt;Claude&lt;/a&gt;, just chatting.&lt;/p&gt;
&lt;p&gt;Before I write any code, I want to understand the domain. If I’m implementing auto-updates for a macOS app, I am asking Claude about how Sparkle works. Not “implement auto-updates for me”, but “how does Sparkle choose when to prompt the user?” or whatever. I want to know the concepts, gotchas, tradeoffs, etc. I often talk about some other app and ask “how does X do this?”&lt;/p&gt;
&lt;p&gt;This is basically rubber-ducking in reverse. I’m building my own mental model through conversation. By the time I’m ready to touch the code, I actually understand what I’m about to do. This matters because it means I now can review what the LLM produces. I develop an intuition for what to expect, which in turn lets me quickly spot when something is wrong.&lt;/p&gt;
&lt;p&gt;This phase gives me confidence, and that matters. And of course, this is mostly for areas I am not already familiar with. But even when am familiar, I find that these conversations give me insights or what I need to ask when doing the plan.&lt;/p&gt;
&lt;h3 class="wp-block-heading" id="phase-1-plan"&gt;Phase 1: Plan&lt;/h3&gt;
&lt;p&gt;Now I move to an agent. Lately I’ve been using &lt;a href="https://ampcode.com"&gt;Amp&lt;/a&gt;, but the specific tool matters less than the process. This could be Claude Code, Codex CLI, etc. My process is tool-agnostic.&lt;/p&gt;
&lt;p&gt;I don’t say “build me X.” Instead, I start another conversation, mostly a Q&amp;amp;A. “How would you approach this?”, “What are the steps?”, etc. I challenge it when something sounds off. I often ask the LLM to pushback to my ideas if it thinks they’re not good. I may still insist but it’s good to have some pushback here and there. We go back and forth until I’m satisfied with the approach.&lt;/p&gt;
&lt;p&gt;Then I ask it to split the plan into the smallest self-contained, testable phases. This is critical. I want each phase to be something I can review, run, and validate before moving on. Those codebase-wide big changes re where things go off the rails.&lt;/p&gt;
&lt;p&gt;Finally, I have it write everything to a &lt;code&gt;spec.md&lt;/code&gt; file. This serves two purposes: (1) it’s a reference I can point the LLM to if context gets lost, and (2) it’s documentation of what we decided and why. For longer projects, this is how I resume after a break. I also make manual adjustments to this plan when needed, though this is getting more and more rare.&lt;/p&gt;
&lt;h3 class="wp-block-heading" id="phase-2-implement-each-phase-of-the-plan-one-by-one"&gt;Phase 2: Implement each phase of the plan, one by one&lt;/h3&gt;
&lt;p&gt;Now the agent starts writing code, one phase at a time.&lt;/p&gt;
&lt;p&gt;I watch the diffs as they flow in and because I was part of the planning and did my homework in Phase 0, I know what to expect. A quick glance usually is enough to tell me if it’s writing what we discussed or going off-script. That’s why the prep work matters: review is fast when you understand what you’re looking at.&lt;/p&gt;
&lt;p&gt;I also give it context to save time. The agents nowadays are very smart and can find their way, but I can shortcut that by giving it hints “in &lt;code&gt;internal/foo/foo.go&lt;/code&gt; there’s a function called &lt;code&gt;DoFoo()&lt;/code&gt; and it does this and that and I want it to do that other thing before that” or whatever. Less tokens, faster iteration. This is probably astrology for nerds, pure superstition at this point, but I still do it. &lt;em&gt;(Hi, it&amp;rsquo;s me, from the future: &lt;a href="https://rselbach.com/being-specific-when-pairing-with-bots/"&gt;maybe it&amp;rsquo;s not astrology?&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here’s a little trick I’ve started using: cross-agent reviews. Once Amp finishes a phase, I’ll ask Claude Code or Codex to review the diff. Different models and harnesses catch different things. It’s not foolproof, but it’s cheap and occasionally catches something I missed.&lt;/p&gt;
&lt;h3 class="wp-block-heading" id="phase-3-validation-commit-and-handoff"&gt;Phase 3: Validation, commit, and handoff&lt;/h3&gt;
&lt;p&gt;Once a phase looks good, I test it. I run and do what I can to validate it. I’ve mostly reviewed the code both by myself and using an LLM.&lt;/p&gt;
&lt;p&gt;If something is wrong, I iterate with the agent. I point out the problem and let it fix it. This usually works and only very occasionally I have to take over and fix it myself.&lt;/p&gt;
&lt;p&gt;When I’m happy, I commit. This is an easy rollback point if something goes wrong afterwards. At this point I use Amp’s &lt;code&gt;/handoff&lt;/code&gt; command to start a fresh context for the next phase. This is a forced boundary: the agent will start clean (though it can reference the previous phase in Amp), it will re-read the spec and we continue. This helps prevent context rot, which is where long sessions start to drift.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="trust-boundaries"&gt;Trust Boundaries&lt;/h2&gt;
&lt;p&gt;I rely on LLMs heavily but I don’t &lt;em&gt;trust&lt;/em&gt; them.&lt;/p&gt;
&lt;p&gt;These are the lines I don’t let them cross:&lt;/p&gt;
&lt;ul class="wp-block-list"&gt;
 &lt;li&gt;
 &lt;strong&gt;Nothing ships without my review.&lt;/strong&gt; I read every line before it goes in. I am too anxious to ship something I don’t understand. That prep work from Phase 0 is not just about understanding, but about making review fast enough that this is sustainable
 &lt;/li&gt;
 &lt;li&gt;
 &lt;strong&gt;Don’t let the LLM write tests unsupervised.&lt;/strong&gt; I learned this one the hard way. When a test fail, LLMs often “fix” the test to make it pass. I’ve heard this is less likely nowadays but I’ve been burned and trust isn’t easily restored. So there. Now I’m extremely careful about letting them modify test code. Only thing I do like to use LLMs for in testing is asking them “do the tests cover the case where this, this, and this happen?” Helps finding holes in the coverage.
 &lt;/li&gt;
 &lt;li&gt;
 &lt;strong&gt;Debugging is still mostly me.&lt;/strong&gt; This is ironic, given that debugging a bug was my entry point into using LLMs more and more, but I’ve found that for my day-to-day debugging, I’m usually faster on my own. I reach for an LLM if I’m stuck, not as a first resort. Maybe this is muscle memory or maybe the tooling is weaker here. Either way, I don’t force it.
 &lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="wp-block-heading" id="what-still-doesnt-work-well"&gt;What still doesn’t work well&lt;/h2&gt;
&lt;p&gt;I want to be honest about the limitations, because the hype around these tools is exhausting.&lt;/p&gt;
&lt;p&gt;I don’t think they’re good at complex refactoring across many files. The agent loses the thread. It will make changes that are locally correct but globally inconsistent. For big refactors, I still do a lot of manual work. I feel like the quality of code after an LLM-assisted refactor is not great quality.&lt;/p&gt;
&lt;p&gt;Also, anything requiring deep context about the codebase’s history. Why is this weird workaround here? What’s the implicit contract this function has with its callers? The agent doesn’t know, heck most people don’t either, but whereas a human might be reluctant, LLMs will happily remove that code that seemed inconsequential but that now breaks some contract with a client.&lt;/p&gt;
&lt;p&gt;And the final one can be controversial, but I think they’re bad at novel architecture decisions. Don’t get me wrong, ask an LLM to design something and it will, but then you ask it “oh but what if…” and it will immediately “yes good point” and redesign it all. It just goes along with whatever you last said. It doesn’t know how to make decisions. It shouldn’t be surprising given how LLMs work, but our brains tend to anthropomorphize everything and then these things become counterintuitive. So I still have to think about architecture myself.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="the-real-lesson"&gt;The Real Lesson&lt;/h2&gt;
&lt;p&gt;These tools have changed a lot — GPT 5.2 and Opus 4.5 are watershed moments IMO — but not as much as my own approach did. I stopped trying to skip the thinking part and started using LLMs to enhance it. The agent participates in discovery, planning, obviously implementation, and also reviews, but I am still driving.&lt;/p&gt;
&lt;p&gt;If you bounced off these tools, it might be worth trying again with a different approach, it’s all I’m saying.&lt;/p&gt;
&lt;p&gt;I’ve found that my workflow is more work upfront, but dramatically less work overall. More importantly, it lets me focus on the interesting parts and helps me with the drudgery.&lt;/p&gt;</description></item><item><title>Trying out Codex CLI</title><link>https://rselbach.com/trying-out-codex-cli/</link><pubDate>Mon, 25 Aug 2025 11:34:28 +0000</pubDate><guid>https://rselbach.com/trying-out-codex-cli/</guid><description>&lt;p&gt;A while ago I was a little skeptical of AI-assisted coding. Mostly because my experience had been with CoPilot autocomplete and it was really not good. I still avoid AI autocomplete to this day, even if I can see it got better because I still find it distracting and often still not great.&lt;/p&gt;
&lt;p&gt;That said, &lt;a href="https://www.anthropic.com/claude-code"&gt;Claude Code&lt;/a&gt; shook my world view and I’ve been daily driving it ever since. I need to write a post about how I use this agent, but tl;dr I use it for the boring parts of coding and to help me read and review code (especially my own) instead of using it to write feature code.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_7d1366156bb333ee.webp 320w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_25fea0e5ee6b544.webp 480w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_fa4a148aecef7291.webp 640w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_210a95795089566e.webp 800w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_6ea1cecfcd5f7ebc.webp 960w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_5a1fcc0795ef888f.webp 1100w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_995980500d33bd5.webp 1280w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/trying-out-codex-cli/distracted-old-time.png"
 alt=""
 loading="lazy"
 decoding="async" width="1536" height="1024" srcset="https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_3070d215d3059a4b.png 320w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_8f334f205d6ea739.png 480w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_2b17926e19ed5b50.png 640w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_b729aa366ed6a4ac.png 800w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_b18a34b1464796e7.png 960w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_263d62b6158552a2.png 1100w, https://rselbach.com/trying-out-codex-cli/distracted-old-time_hu_982506bf92a111cd.png 1280w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I have been happy with Claude Code, but I also heard very good things about the new GPT-5 model for coding and wanted to check it out. Enter the &lt;a href="https://github.com/openai/codex"&gt;Codex CLI&lt;/a&gt;. It’s OpenAI’s answer to Claude Code.&lt;/p&gt;
&lt;p&gt;I am approaching this with a very open mind. I completely understand that it is early times in Codex CLI land and thus I did not expect it to have feature parity with Claude. I’m ok with that, just to get that out of the way.&lt;/p&gt;
&lt;h2 id="the-onboarding-was-rough.wp-block-heading"&gt;The onboarding was rough&lt;/h2&gt;
&lt;p&gt;My first experience with it was that it wouldn’t install due to an issue in the post-install of a dependency (&lt;code&gt;ripgrep&lt;/code&gt;, which, I must say, I already had installed.) I went to file a ticket and say that someone else had &lt;a href="https://github.com/openai/codex/issues/2501"&gt;already done so&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;No matter!&lt;/em&gt; I thought. I figured out how to get around it and then decided to try it. I opened a local repo and typed &lt;code&gt;/init&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Codex decided it wanted to run tests to check the status of the repo. Fair enough, go ahead. It then failed to compile my Go code, claiming the Go toolchain wasn’t available. I was confused by that, so I closed Codex CLI and ran &lt;code&gt;go version&lt;/code&gt;, all good. I ran my tests, all passed. &lt;em&gt;Wut?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I tried again and this time I told it that I checked and I had the toolchain installed. It tried again, no dice. It kept trying until eventually I stopped and did some digging. That’s when I learned that &lt;strong&gt;Codex CLI runs inside a sandbox and doesn’t share my shell’s environment&lt;/strong&gt;. Ok, that was a little upsetting. So I asked Codex CLI how we could provision the sandbox with Go. It proceeded to look for Go &lt;strong&gt;&lt;span style="color: red"&gt;1.13&lt;/span&gt;&lt;/strong&gt;, which was release over &lt;em&gt;six years ago&lt;/em&gt;. It asked me to download the tarball and leave it in a certain directory and it would take from there.&lt;/p&gt;
&lt;p&gt;Ok, time for some more digging. It’s a this point that I must point out that the Codex CLI documentation is basically non-existent, and being a relatively newcomer, there’s not a lot of resources out there. Again, I get it, let’s just get through this initial steps.&lt;/p&gt;
&lt;p&gt;I keep at it until I figure the issue: though my shell’s &lt;code&gt;PATH&lt;/code&gt; includes Go 1.25, the sandbox’s did not. I couldn’t quite figure out why but I did manage to get it working by telling GPT where to find the Go binaries.&lt;/p&gt;
&lt;h2 id="once-it-got-working.wp-block-heading"&gt;Once it got working&lt;/h2&gt;
&lt;p&gt;Now, once I got it working, things went a lot smoother. I quickly got used to the differences from Claude Code (and they are many) and got somewhat comfortable with it. I got GPT to analyze my code and look for bugs and it found a minor one that had escaped Claude for a long time. That was cool.&lt;/p&gt;
&lt;p&gt;I found that it tends to be a little noisier than Claude Code, because CC tends to hide somethings behind its quirky verbs (“lampoonig…”, etc) This isn’t necessarily a negative, just different and something to get used to.&lt;/p&gt;
&lt;p&gt;I miss the TODO lists that Claude Code creates and follows. Again, not a huge deal. The part that it needs to improve is tool calling. More than once I saw it calling some Go tool with bad parameters. And also, it doesn’t seem to quite grasp the error messages.&lt;/p&gt;
&lt;p&gt;Case in point, I asked it to run a linter, so it started running &lt;code&gt;golangci-lint&lt;/code&gt;, but it ran it at the root of the repo, where there are no Go files, and without parameters, which resulted in an error “No Go files”. It didn’t seem to understand this error and concluded &lt;code&gt;golangci-lint&lt;/code&gt; wasn’t installed.&lt;/p&gt;
&lt;p&gt;It then entered a loop trying the same command over and over again until I interrupted it and told it to pass &lt;code&gt;./...&lt;/code&gt; to include subdirectories. It then tried again with the parameters, but bizarrily this time it decided that the &lt;code&gt;golangci-lint&lt;/code&gt; would be in &lt;code&gt;./bin&lt;/code&gt;, which is not true at all. So I had to tell it where to find it. And then it worked fine.&lt;/p&gt;
&lt;h2 id="conclusion.wp-block-heading"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;It’s early days and it’s clear there’s some ground to make up if they want to catch up, but I also remember the early days of Claude Code. The CC team iterated quickly and we got to where we are today, and I’m hoping the Codex team will do the same. They seem very active in answering questions on X, so I have hope.&lt;/p&gt;
&lt;p&gt;I’m hopefuly and interested. I’ll keep an eye on it.&lt;/p&gt;</description></item><item><title>Cedilla in Fedora 2025</title><link>https://rselbach.com/cedilla-in-fedora-2025/</link><pubDate>Fri, 09 May 2025 11:29:08 +0000</pubDate><guid>https://rselbach.com/cedilla-in-fedora-2025/</guid><description>&lt;p&gt;Years ago &lt;a href="http://localhost:1313/cedilha-no-fedora-25/"&gt;I posted about getting the c-cedilla (ç) working in Fedora&lt;/a&gt; when using the US International keyboard with deadkeys. This has been a struggle for decades now and every time I set up a new Linux installation, I need to look it up.&lt;/p&gt;
&lt;p&gt;That wouldn’t be such a problem, weren’t for the fact that the way to accomplish this seemingly simple task keeps changing over the years, so most of the information you find online is awfully out of date.&lt;/p&gt;
&lt;p&gt;So for 2025, based on my experience with Fedora 42 (the current version at time of writing) – I suspect it would work similarly with other distros, but I cannot confirm it – this is how I did it.&lt;/p&gt;
&lt;p&gt;First add these two lines to &lt;code&gt;/etc/environment&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export GTK_IM_MODULE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;cedilla
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;export QT_IM_MODULE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;cedilla
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then in your home dir, add a file named &lt;code&gt;.XCompose&lt;/code&gt; with 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;dead_acute&amp;gt; &amp;lt;C&amp;gt;			: &amp;#34;Ç&amp;#34;	U0106 # LATIN CAPITAL LETTER C WITH CEDILLA
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;dead_acute&amp;gt; &amp;lt;c&amp;gt;			: &amp;#34;ç&amp;#34;	U0107 # LATIN SMALL LETTER C WITH CEDILLA
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then reboot and it should just work. To be fair, this is the easiest it’s been for years to get this done.&lt;/p&gt;
&lt;p&gt;As a bonus, there are a few other changes that I’ve made in my &lt;code&gt;.XCompose&lt;/code&gt; file to solve some annoyances I have with the &lt;code&gt;US-intl&lt;/code&gt; keyboard in Linux. When I type fast, I tend to accidentally end up with a lot of mistakenly accented consonants that I don’t need in any of the languages I write in.&lt;/p&gt;
&lt;p&gt;These are, of course, entirely up to you if you want them and have nothing to do with the cedilla. &lt;a href="https://github.com/rselbach/c-cedilla-fix"&gt;You can find them on Github.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hopefully the search engine gods will help someone out there find this when they need it. Might just be me in the not-too-distant future.&lt;/p&gt;</description></item><item><title>How to Make :x Just Save Instead of Save and Quit</title><link>https://rselbach.com/how-to-make-x-just-save-instead-of-save-and-quit/</link><pubDate>Sat, 26 Apr 2025 11:25:42 +0000</pubDate><guid>https://rselbach.com/how-to-make-x-just-save-instead-of-save-and-quit/</guid><description>&lt;p&gt;I’m going to leave this here in case someone out there is looking for the same thing, because this one was hard to figure out for me.&lt;/p&gt;
&lt;p&gt;I’ve been using &lt;a href="https://www.vim.org/"&gt;vim&lt;/a&gt; for a long time. Decades. Although it has never been my main editor (I used to be a proud &lt;a href="https://emacs.org/"&gt;emacs&lt;/a&gt; guy), vim is the editor I always go to when I need to edit something quickly.&lt;/p&gt;
&lt;p&gt;My main editor these days is &lt;a href="https://code.visualstudio.com/"&gt;Visual Studio Code&lt;/a&gt;, with the &lt;a href="https://github.com/VSCodeVim/Vim/"&gt;VsCodeVim&lt;/a&gt; extension. I still use vim often from the vscode integrated terminal, which is ironic.&lt;/p&gt;
&lt;p&gt;Anyway, after so many years using vim, I have developed muscle memory that is hard to let go of. I am very used to doing quick edits and using &lt;code&gt;:x&lt;/code&gt; to save and exit. It’s a hard habit to quit.&lt;/p&gt;
&lt;p&gt;But when working on vscode, this behaviour is not exactly ideal. I don’t need to close the active editor, so I decided to try and modify that behaviour by only saving the file instead of saving and closing the editor.&lt;/p&gt;
&lt;p&gt;After a lot of trial and error and reading the extension source code, I finally found the solution:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;vim.normalModeKeyBindingsNonRecursive&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;before&amp;#34;&lt;/span&gt;: [ &lt;span style="color:#e6db74"&gt;&amp;#34;:&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;x&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;lt;enter&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:#f92672"&gt;&amp;#34;commands&amp;#34;&lt;/span&gt;: [ &lt;span style="color:#e6db74"&gt;&amp;#34;:w&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:#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:#e6db74"&gt;&amp;#34;vim.commandLineModeKeyBindingsNonRecursive&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;&amp;#34;before&amp;#34;&lt;/span&gt;: [ &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 style="color:#f92672"&gt;&amp;#34;commands&amp;#34;&lt;/span&gt;: [ &lt;span style="color:#e6db74"&gt;&amp;#34;:w&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:#960050;background-color:#1e0010"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In both cases, I am replacing &lt;code&gt;:x&lt;/code&gt; with &lt;code&gt;:w&lt;/code&gt;, which is the behaviour I want. But this needs to be done in two different ways. When the editor is in NORMAL mode, we need to look for the entire sequence of commands (&lt;code&gt;:x&amp;lt;enter&amp;gt;&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;But if you type in &lt;code&gt;:&lt;/code&gt; and wait a second, the editor will enter command line mode, in which we then need to capture the command (&lt;code&gt;x&lt;/code&gt;) and then replace it with the &lt;code&gt;:w&lt;/code&gt; command.&lt;/p&gt;
&lt;p&gt;Hopefully this will show up on a search engine or AI output that will help someone out there not have to spend as much time as I did trying to look for this solution.&lt;/p&gt;</description></item><item><title>X-Touch Mini for Flight Simulation</title><link>https://rselbach.com/x-touch-mini-for-flight-simulation/</link><pubDate>Tue, 09 May 2023 14:24:04 +0000</pubDate><guid>https://rselbach.com/x-touch-mini-for-flight-simulation/</guid><description>&lt;p&gt;Since I was a kid, I’ve loved aviation. Being poor and all, I could never dream of pursuing it in real life, so flightsimming has been my “cheap” fix for many years. I put cheap in quotes because this is an expensive hobby, even if you don’t overdo it. Although I spend quite a lot of money on software, I try to keep things in check on the hardware department, as flightsim equipment can be &lt;em&gt;very&lt;/em&gt; expensive. For GA flying, it would be great to have a &lt;a href="https://realsimgear.com/products/realsimgear-g1000-suite-for-x-plane-p3d-and-fsx"&gt;G1000&lt;/a&gt;, but at $2,199 USD, that’s a no from me.&lt;/p&gt;
&lt;p&gt;Also, I’ve long flown the Boeing 737NG series, and setting up the MCP (the autopilot panel) with the mouse is quite the immersion killer, not to mention quite hard during busy phases of a flight. But € 1,299 is also not worth it, in my opinion.&lt;/p&gt;
&lt;p&gt;That said, I’d really like some hardware controls. And that’s when I ran into the &lt;a href="https://www.behringer.com/product.html?modelCode=P0B3M"&gt;Behringer X-Touch Mini&lt;/a&gt;. The X-Touch Mini is not made for flightsimming, though; it’s a MIDI controller and as such, it doesn’t have the “niche tax.” I got it from Amazon at $180 CAD.&lt;/p&gt;
&lt;p&gt;&lt;a href="./IMG_0847-8.png"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_15f998a9fde4d8c9.webp 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_73ea8555a000eaa0.webp 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_a0b4c1a85bd6c458.webp 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_bc1475077797d891.webp 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_542bcb1c2892c930.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768.png"
 alt=""
 loading="lazy"
 decoding="async" width="1024" height="768" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_a30040ab92f01446.png 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_89b993afb2b2ba8a.png 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_55b427a15938e2ba.png 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_38825d2e219846b1.png 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/IMG_0847-8-1024x768_hu_f158a2ff2ae54de9.png 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With some tinkering, I could make this control many planes, from the B737 to the Twin Otter. It’s great. I’ve long used &lt;a href="https://www.spadnext.com/home.html"&gt;SPAD.neXt&lt;/a&gt; to control all my planes for two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I like tinkering with LVARs and also most third-party planes don’t expose all their controls to the simulator&lt;/li&gt;
&lt;li&gt;I like the fact that it autoswitches to the correct control profile for whatever plane I’m using&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As an example, here’s how I set up a knob to control the checklists on the Honda Jet.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://rselbach.com/x-touch-mini-for-flight-simulation/screenshot-2023-05-06-084634/"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_a376e70484da283b.webp 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_f34bb6559d3295c8.webp 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_10c02eb05a9f7777.webp 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_38120228f52507e8.webp 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_f19adb183dea2148.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559.png"
 alt="A screenshot of SPAD.neXt"
 loading="lazy"
 decoding="async" width="1024" height="559" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_c0168320b6fab216.png 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_5dbdd57fc2c7de28.png 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_3f78683e0ce55058.png 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_45cd4c5e714f196d.png 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-06-084634-1024x559_hu_ac77b55f0faca93d.png 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s amazing! Also, I’ve been flying the ATR72-600 lately. Great plane! Also, it is similar enough to the Bombardier DHC8 (a.k.a. Dash-8) that it scratches my itch to fly regional Canadian routes, so I followed the excellent &lt;a href="https://www.youtube.com/watch?v=YS-2_swi-18"&gt;Les O’Reilly’s tutorial on setting the X-Touch Mini up for the ATR 72-600&lt;/a&gt;. Seriously, if you want to learn SPAD.neXt, &lt;a href="https://www.youtube.com/@LesOReilly"&gt;check out his channel&lt;/a&gt;, it’s great.&lt;/p&gt;
&lt;p&gt;However, I ran into an issue.&lt;/p&gt;
&lt;h2 id="x-touch-mini-leds-not-working"&gt;X-Touch Mini Leds not working&lt;/h2&gt;
&lt;p&gt;I could not get my leds to work with SPAD.neXt. No matter what I sent to the channel, the leds would not respond. I rewatched Les’ video, searched forums all over and never saw anyone having the same issue. I started suspecting a hardware problem. Eventually, I downloaded the editor from &lt;a href="https://www.behringer.com/product.html?modelCode=P0B3M"&gt;the Behringer’s website,&lt;/a&gt; solely for the purpose of seeing if I could get the leds to activate with it, to discard a hardware issue. This is when I found this —&lt;/p&gt;
&lt;p&gt;&lt;a href="./Screenshot-2023-05-09-at-10.17.23-AM.png"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_7557756b0a2ec39f.webp 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_7d8df576d9fbf91e.webp 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_736592ca27e7b1d7.webp 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_4958531eb67a3feb.webp 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_864557f93dfae248.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911.png"
 alt="Screenshot of the X-Touch Mini editor with the Global CH field highlighted"
 loading="lazy"
 decoding="async" width="1024" height="911" srcset="https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_b52741ba1b23b425.png 320w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_ae6edfd5c0203b04.png 480w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_82924941e21f19b6.png 640w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_a0b8a1a602012716.png 800w, https://rselbach.com/x-touch-mini-for-flight-simulation/Screenshot-2023-05-09-at-10.17.23-AM-1024x911_hu_d12b502e03a5be32.png 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For some reason, my X-Touch Mini came with the global channel set to channel 12, instead of channel 1 as, it seems, is the normal setting. This is why none of the settings worked, so if you run into the same issue, now you know. So to fix this, there are two possibilities:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change all your SPAD.neXt settings to send the command to channel 11 (the channels are actually 0-based, so channel 12 in the UI is actually channel 11 in SPAD.neXt); or&lt;/li&gt;
&lt;li&gt;Change the global channel in the Behringer editor to 1 — which will be default channel 0 in SPAD.neXT. This is what I’ve done.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once that was done, everything worked perfectly. The LEDs change status even if a channel happens inside the simulator, so you can rely on them to know the current status of your automatics and navigation/comms. Really happy with the setup.&lt;/p&gt;</description></item><item><title>Impressions on the Keychron Q6</title><link>https://rselbach.com/impressions-on-the-keychron-q6/</link><pubDate>Sat, 29 Apr 2023 22:52:41 +0000</pubDate><guid>https://rselbach.com/impressions-on-the-keychron-q6/</guid><description>&lt;p&gt;I’ve had a soft spot for mechanical keyboards for a long time. It’s a cliché, I know. I’m not a fan of loud mechanical keyboards, mind you. I’ve had my hands on &lt;a href="https://www.cherrymx.de/en/cherry-mx/mx-original/mx-blue.html"&gt;Cherry MX Blues&lt;/a&gt; and found them to be so loud as to be a distraction during calls. And I found the &lt;a href="https://www.cherrymx.de/en/cherry-mx/mx-original/mx-red.html"&gt;Cherry MX Reds&lt;/a&gt; to be, well, too quiet. I found the Goldilocks zone to be in the &lt;a href="https://www.gateron.co/products/gateron-switch-set?variant=40017397448793"&gt;Gateron MX Browns&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have also come to particularly like the &lt;a href="https://keychron.com"&gt;Keychron&lt;/a&gt; keyboards. They have a bit of a shaky reputation online, but I’ve never had any problems with my Keychrons and I adore them. This &lt;a href="https://www.keychron.com/products/keychron-k8-tenkeyless-wireless-mechanical-keyboard"&gt;&lt;strong&gt;Keychron K8&lt;/strong&gt;&lt;/a&gt; with Gateron Brows keys has been my main keyboard for the last 2-3 years.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_152bf0cc5f119c2a.webp 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_d59cbe9e97b75f17.webp 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_8084779d6d6022ab.webp 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_a29c016ebffd2249.webp 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_2f5bacf10ed7b2e5.webp 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_f917414e1a78edca.webp 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_e07cc4b6c2601275.webp 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_519083df592c7491.webp 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_5403014124e796c0.webp 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled.jpeg"
 alt="Picture of a Keychron K8 keyboard"
 loading="lazy"
 decoding="async" width="2560" height="1920" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_463f916f50d464e0.jpeg 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_647b12900d5aafac.jpeg 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_7fa2c17480803b0e.jpeg 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_16f818bb952474c5.jpeg 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_14cb7ad0e3ccffde.jpeg 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_3eef1d2e2006f796.jpeg 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_1a2cf84498e16ebb.jpeg 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_ea0e63cdd5dd4b17.jpeg 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0827-scaled_hu_c90666dd94e96507.jpeg 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;It is an awesome keyboard. I like how it feels, I like how it sounds, and I like how it &lt;em&gt;clicks&lt;/em&gt;. It’s great.&lt;/p&gt;
&lt;p&gt;The only thing my K8 lacked was a numpad, and since I have a certain passion for flight simulation, a numpad is something useful. I could buy a USB numpad. But where’s the fun in that? So this was the beginning of my search for a new keyboard.&lt;/p&gt;
&lt;p&gt;After some online conversations, I settled for another Keychron favourite, the &lt;a href="https://www.keychron.com/products/keychron-k4-wireless-mechanical-keyboard-version-2"&gt;K4&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_f1ba7c3d243e2f87.webp 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_b27c36e98712bc91.webp 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_9d1c2c7495effc56.webp 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_418ed3a321d00a20.webp 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_bdec488ec9453ad5.webp 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_cf0f4bc1b0368456.webp 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_a7b1955d4d9d004c.webp 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_b33133b73f0d0f73.webp 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_f436b5f61041ea61.webp 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled.jpeg"
 alt="A picture of a Keychron K4 keyboard"
 loading="lazy"
 decoding="async" width="2560" height="1920" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_39acd882d5897dc8.jpeg 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_e521688214e1ec1e.jpeg 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_7f8b7401f57ba525.jpeg 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_b5b39f53d5f99099.jpeg 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_f55ac861ced6efbb.jpeg 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_3fa9e49e062c732f.jpeg 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_e3f89b77bb94b239.jpeg 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_c5ba9ee391509c48.jpeg 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0828-scaled_hu_71337c831d40a6ec.jpeg 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;And this keyboard feels &lt;em&gt;amazing&lt;/em&gt;. I cannot stress this enough: it’s the most pleasurable clicking experience of my life. In theory, it has the same Gateron G Pro Browns as my K8, but for some reason, it feels better. I’m not sure how else to put this: they feel less metallic-y. I don’t know how else to put it.&lt;/p&gt;
&lt;p&gt;Great size, great feel, great quality. I fell in love with this keyboard the moment I unboxed it. Except it has a terrible flaw that might be evident from the picture, but that I never noticed until I started using it. Do you see it? It’s right there by the arrow keys. Do you see it now?&lt;/p&gt;
&lt;p&gt;There’s no spacing. The arrows are right under the Enter key but there’s no padding space between them and, say, the 0 key on the numpad. Years of muscle memory down the drain. I could not reliably hit the arrows without looking. I tried. The layout of the Del, End, PgUp, PgDown keys was also a bit foreign to me, although I was able to adapt relatively quickly to them. The arrows keys though, I simply could not. I replaced them with some texturized rubber keys but I was still unable to hit them reliably. It was a pity.&lt;/p&gt;
&lt;p&gt;Enter the mighty &lt;a href="https://www.keychron.com/products/keychron-q6-qmk-custom-mechanical-keyboard"&gt;QMK Q6&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_e195ae1f64df8002.webp 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_44fb04664ad39063.webp 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_536c324ca6caa3b9.webp 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_b142b1ede2a8ba56.webp 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_c61e62e3bd01882d.webp 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_79f5bf2b6d0f782a.webp 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_f64b4def9c955ad.webp 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_ae5693a8c39ac673.webp 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_3cf1b22277d8c75b.webp 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled.jpeg"
 alt="A picture of a Keychron Q6 keyboard"
 loading="lazy"
 decoding="async" width="2560" height="1920" srcset="https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_657f5aa59fa32575.jpeg 320w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_390798d20d3e971c.jpeg 480w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_16598edf4a245c40.jpeg 640w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_59d967b0e95d4713.jpeg 800w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_b2f4a9236c136d08.jpeg 960w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_32ebc9d21944511.jpeg 1100w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_88ad227a36a38316.jpeg 1280w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_df28c21ac757ad8f.jpeg 1600w, https://rselbach.com/impressions-on-the-keychron-q6/IMG_0829-scaled_hu_e94a03432eeb36b9.jpeg 2048w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;This keyboard is a beast! It weights 2.5Kg, which is insane. But OMG it feels so, so good. If I had to choose, I would still pick the &lt;em&gt;feel&lt;/em&gt; of the K4, but it’s close. The Q6 has better keycaps though.&lt;/p&gt;
&lt;p&gt;In terms of sound, it sounds very close to the K8, maybe a tad softer but it’s hard to tell. In terms of feel, it’s between the K4 and K8. It feels closer to the K8 than to the K4, but yeah, somewhat in the middle between the two.&lt;/p&gt;
&lt;p&gt;I love it. I credit it for the fact that I am writing this right now: I just feel like typing! I really like this. As an additional bonus, its firmware’s source code is open source. I don’t plan to use it for anything, but it’s good to know.&lt;/p&gt;
&lt;p&gt;However, it is not perfect. Don’t get me wrong: I’m very happy with this keyboard but it does have one extremely questionable design decision that I honestly can’t understand: it doesn’t have feet. It just lies flat on the desk which feels quite awkward to me. I cannot understand the decision to do that. I solved it with some cheap adhesive rubber feet, so all is good, but why Keychron decided to ship it like this is beyond. So be aware of this.&lt;/p&gt;
&lt;p&gt;Other than that, I absolutely love it.&lt;/p&gt;</description></item><item><title>In which I reminisce about the last few years</title><link>https://rselbach.com/in-which-i-reminisce-about-the-last-few-years/</link><pubDate>Sat, 29 Apr 2023 09:54:15 +0000</pubDate><guid>https://rselbach.com/in-which-i-reminisce-about-the-last-few-years/</guid><description>&lt;p&gt;I just checked and it’s been exactly 1,594 days since I last posted on this blog. That’s 4 years, 4 months, and 12 days. This was, as is often the case with these, not planned. When I last wrote something here, I was working in a team set up as an R&amp;amp;D lab. Work felt quite fun and exciting and writing about it felt natural.&lt;/p&gt;
&lt;p&gt;I then changed to jobs to a startup where things felt a tad different. It was a weird time for me: I met some great people there, people I still talk to and call friends. We put together a small team where I got to do some of the most fun work. Some of the people in that team I still talk to every single day. We’re still trying to put the group back together in some form in another company. And yet, my time in that company, outside that small team, made me feel quite small and inadequate. Writing about it did not feel natural.&lt;/p&gt;
&lt;p&gt;I then joined HashiCorp, a company I’ve admired for years. I won’t go as far as saying this was a dream of mine, but when I got the offer, it sort of felt like it. I’ve been here for about two and a half years now and I’ve met some extremely brilliant people, and a few that I can call friends. I should have written about it. I wish I had. But by this time, the writing habit was already gone and life does what life does.&lt;/p&gt;
&lt;p&gt;What else happened over the last few? Well, we became Canadian citizens. That was a blast, even if pandemic-style remove ceremony was a bit awkward.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://lh3.googleusercontent.com/DS7Ey_xk_Mu2CLgrLlld0-53fdizwU1UAS-TtWp8X0z7vLi2x62-g6jw07CqlkUX40B87UNXT1hJLZRMwzqnQqw29hxnvg7GJl2oAx0GqiTrIFLeGudXlSzAkd1ERwN4ZOp5xALbGsg4bYuBFczrp7g8qtdezSpMshoQEXgkIACiH_Y0cJLLPCirfgykA5GTffWEb-4wdLNFgPKcTi7tZ7FENwugfTGz9MtbRKMVX1WVawlfz7LSBiANwYX2HLtTyxU3qE4_Q-R3DhzgBmdgJESyz_5yMqELQqe5wUtxEweaT-N6Zu15Whf9vFhgNH_22JOhcBcBSSYVUVu7G1AhzWC_y6FYmy4G_46Bu99wmhb1ZNtnFjJQOJLFNhYpO0Lsd_k0jfdUnigEPBe3A7Gwto8cdJJVmsiEpMFikiSjGXnfA6UZHhkx_RPTshOuFTuUQDJVu-YdlXdRyz2Ew4d7XaZCewe3bKeNyIyQ8XjN-ED1MmrXzZ764WPzgdnt2tZ82XFr4GQCWuGaYM56rMg29-Og__y1viEHvWvLA2T9YmVsogHeczixSbMwX7LBvCW-pyDQ5guwX3fab9_tRL0pc0-xRd6SZDDMAtFxx9JZ13xSOVSzR7bBSgSJDVH44lzhFGiB1eVDvADbODmxInPvMOts8d20u4M6XsZuEzBvUrVq3msbQhTgLqJIKTylmvGySNKCmgRCibg-5F0hlk5yCrwSYH5z4YadGWWasiikp497SX3WGyqc-p5DaOx18rCOoxaUvrtjnrdh16-ku9lzbK6wr7pLjWlOPjfkgZLx3fgaMAA2YWQYQs8QLJDjGz7OyY3dNeDrfM8gKerRc34wtVP000vX58FZBv7tcK4KaI3idfCSrUVZN_u4r8W1l4P-2qR2eJagztyeO4jXuwem_tlXHrZfos8ctySnNVzKO682pA=w2642-h1982-s-no?authuser=0"
 alt=""
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;We bought a house and got a new dog, Loki. He’s an English Cocker Spaniel, as would be expected of us, as he’s our 5th —&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://lh3.googleusercontent.com/rfZ9UNxEiV6mPiwQIckzbaM1dFLt5roHuFK94xTtotc5Y--tRdduuIkvzI4o9xe7_w2wjsqRI6nYb7foqHl7Vlp3B7AwrCBonidDsXplt6awsnFctD8ebrrF2evr3f8b6gICYKOF0C7sJdENONokFpwUa2-XJ3fe7kXyDRSyCOhCMf8U97jkyiFfIwNlmnVZi3sDylVzmWzKsANEKMiBMs11JMm63QApOj1N3NSc4B7nvcsXVuuWDMpE6Z1uKlrxHm3f61_a4SOQlQVnq-jh0BPI_ogZh65cG1jKEKZIp8L1R4pgVIqLIJ2x6163jm1pcHADfLSNdcvNnhuO8BEduvjKSocDRzebjhdwxfDGdGRdXzNBrsHPy9-Ij-FDAejak_Z4726joXDoA6gZ0mx88UaKPbWOSQBrOqe5Gcfiyg_P8wt4BEekfONqOfhQxW--NZcOk8tJT8b6I-1D13dbwOaZ24mmlkUGigDew9hi3UV--Uv00-m9Zd6Gq7bY4_PYk54XtvDlk7ywu4NDLYCzZq1GC_xFp2tvUdjjH2fB2iw-cb4jkKZb98mFk5CV57tedubaQg_gFVaDZ5E4IXLz40DiKQ5K8QO76RuYc7yRVwR-Aj5fAKAtebJPEAVr74xnt5bb5EadpPgQTmD_VyK6M41YfkoWuTws6hKSvrIXz4GKfwOdx9R0sWkm-HL7Olbepnkb2dmtE90xheyi0M0vaKyP5O5WzzaIrVnxofdTbAlmtTHSu39JmHtQZklBEqVMyuldq0z5S0BRo-oC1PiahhVZ4YuLhn675rGN_5Xqi6g8Mhq_5TV1jVfk1uRLpUVGe28NfJDIMLObdHCQojgOw3CwFj29kXNwkrqZMWG9nfW2-6OuU5fQWv28sNfoVOb_w6eFM9nQgETKpzXPoRNeMTlxDkulzZ2IlZaBDBHUUSJ_jw=w2642-h1982-s-no?authuser=0"
 alt=""
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;All in all, I can’t complain. On the other hand, I am getting older, which sucks, let me tell you that.&lt;/p&gt;
&lt;p&gt;Anyway, I’d like to get back to writing a bit, I used to enjoy it quite a bit. We’ll see. Hopefully it won’t be another four years until the next post.&lt;/p&gt;</description></item><item><title>The Last of Us Part II</title><link>https://rselbach.com/the-last-of-us-part-ii/</link><pubDate>Sat, 20 Jun 2020 14:05:33 +0000</pubDate><guid>https://rselbach.com/the-last-of-us-part-ii/</guid><description>&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 I&amp;#8217;ve finished the game last night. I haven&amp;#8217;t stopped thinking about it ever since. It was, to be very honest, a transformative experience, as far as videogames go. I understand why some people hate it and I&amp;#8217;m sorry because I understand how much it sucks when you want to enjoy something but can&amp;#8217;t. Art is subjective and no one is right or wrong.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 That said, I want to talk about what I&amp;#8217;ve experienced. Again, this is &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;my&lt;/em&gt; experience with the game. I&amp;#8217;m sure yours will be different and it is &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;fine&lt;/em&gt;. If you hate this game, you&amp;#8217;re not wrong. You feel what you feel.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 With that out of the way, let me begin with the least controversial theme: the gameplay.
&lt;/p&gt;
&lt;h2 class="_7T4UafM1PdBGycd5na9nF" id="gameplay"&gt;Gameplay&lt;/h2&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 I thought the gameplay was an improvement on the first game. I don&amp;#8217;t mean only the added functionality like ropes, and dogs tracking you. But the mission structures were more varied and some of them were, well, epic.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 The sky bridge was awesome. So was trying to get near the sniper (Tommy) by advancing behind cover. The stalkers were a fun addition which gave me a lot of jump scares. That huge new infected monster at ground zero was&amp;#8230; argh! Kill it now! The entire time spent on the seraphite island was amazing.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 I also loved little things like figuring out the code for the safes.
&lt;/p&gt;
&lt;h2 class="_7T4UafM1PdBGycd5na9nF" id="the-story"&gt;The Story&lt;/h2&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 This is what is dividing people, and it&amp;#8217;s fine. Art that everybody agrees on is boring. The way I see the story, it is one of mirrored character journeys (as much of popular culture is.) Through three characters, we see the same journey at different points. I think this is genius. Here&amp;#8217;s how I figure.
&lt;/p&gt;
&lt;h3 class="_1qeIAgB0cPwnLhDF9XSiJM" id="joel"&gt;&lt;strong class="_12FoOEddL7j_RgMQN0SNeU"&gt;Joel&lt;/strong&gt;&lt;/h3&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 First all all, yes, it sucked to see him killed. That&amp;#8217;s the point: we are supposed to be angry that he&amp;#8217;s dead, that he&amp;#8217;s killed in such a way. This is very much intended: it puts us in the place of Ellie. It is perfectly expected that you&amp;#8217;d feel &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;rage&lt;/em&gt; towards his killers. I think it becomes a problem when that rage is instead directed towards the game itself, a work of fiction, because then it makes it really hard to appreciate the rest of it.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Let&amp;#8217;s talk about his character journey, shall we? In the first part, we meet Joel at the onset of the pandemic. We don&amp;#8217;t know much about him before then but all signs point to an average joe and a good dad. Then his daughter gets murdered and we skip 20 years, by which time we are to understand that Joel is &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;not&lt;/em&gt; a good man anymore. We learn that he was a killer, a robber, smuggler. He says he killed innocent people, he robbed (and presumably killed) good samaritans. He absolutely did not care for Ellie in the least at first. He wanted to abandon her to the soldiers and run when they caught up to them in Boston. It was Tess who made him stop.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 In the first game, Joel was a &amp;#8220;bad person,&amp;#8221; a broken man who then had his redemption by protecting (and ultimately caring for) Ellie. (More on the &amp;#8220;bad person&amp;#8221; in quotes later.)
&lt;/p&gt;
&lt;h3 class="_1qeIAgB0cPwnLhDF9XSiJM" id="abby"&gt;&lt;strong class="_12FoOEddL7j_RgMQN0SNeU"&gt;Abby&lt;/strong&gt;&lt;/h3&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Abby&amp;#8217;s journey is the same as Joel&amp;#8217;s. Her dad gets murdered by a smuggler and that breaks her. She becomes a &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;bad person.&lt;/em&gt; We&amp;#8217;re made to understand that prior to the events in Seattle, she was a cold-hearted killer. She rose though the ranks of the WLF by becoming the &amp;#8220;top scar killer&amp;#8221; and even her friends think she&amp;#8217;s, to steal Mel&amp;#8217;s description, piece of sh*t. She&amp;#8217;s a murderer.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 And then comes Lev. At first, she too doesn&amp;#8217;t care too much. He is just a scar. You see how Abby dehumanizes the seraphites all the time, the difficulty she has with ever calling them anything other than &amp;#8220;scars.&amp;#8221; She leaves them there but then she feels guilty and, reluctantly at first, goes back to them.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Abby was a &amp;#8220;bad person&amp;#8221; who had her redemption by protecting and caring for Lev. In a way, Abby&amp;#8217;s journey is what we would have seen if we were able to follow Joel through those 20 skipped years.
&lt;/p&gt;
&lt;h3 class="_1qeIAgB0cPwnLhDF9XSiJM" id="ellie"&gt;&lt;strong class="_12FoOEddL7j_RgMQN0SNeU"&gt;Ellie&lt;/strong&gt;&lt;/h3&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 This is where things get interesting. Ellie is living in Jackson the life that Joel and Abby lived &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;before&lt;/em&gt; their own traumatic events. When Joel dies, this matches the deaths of Joel&amp;#8217;s daughter and Abby&amp;#8217;s father. This is where Ellie is going to turn bad.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 And we see that transformation unfold. We see &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;our Ellie&lt;/em&gt; slowly go down the same path that Abby and Joel took years before and that we never got to see. She is consumed by rage.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Ellie is now becoming a &amp;#8220;bad person&amp;#8221; as well. But she&amp;#8217;s not all the way there yet.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 She&amp;#8217;s also consumed by remorse. When she tortures Nora, she comes back to Dina visibly shaking and saying &amp;#8220;I made her talk&amp;#8230;&amp;#8221; She&amp;#8217;s devastated about what she&amp;#8217;s done. Later she kills Owen and pregnant Mel, and again, that &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;breaks&lt;/em&gt; her. And yet, she cannot stop.
&lt;/p&gt;
&lt;h3 class="_1qeIAgB0cPwnLhDF9XSiJM" id="ellie-vs-abby-why-didn8217t-ellie-kill-her"&gt;&lt;strong class="_12FoOEddL7j_RgMQN0SNeU"&gt;Ellie vs Abby: why didn’t Ellie kill her?&lt;/strong&gt;&lt;/h3&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 The final confrontation is amazing. Abby is now in a different place, she&amp;#8217;s now where Joel was after Ellie. It&amp;#8217;s hard for us to forgive her, because we have seen her kill Joel, but nevertheless, that&amp;#8217;s the place she is in. When Ellie cuts her down from that beach post, her first reaction is to run and cut Lev down. Lev is her Ellie. She doesn&amp;#8217;t want to fight Ellie, she only wants to save Lev, just like Joel only cared about saving Ellie at the end.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 But Ellie still can&amp;#8217;t let go. She needs this! Or so she thinks. And so they fight. The fight fell heavy and, to me, very real. It was amazing and painful to watch and even worse to participate in. I did not want to fight Abby. All I wanted was for those two women to find peace now.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 And then Ellie is about to kill her and she remembers Joel. More specifically, she remembers her very last conversation with Joel, about her inability to forgive but willingness to try. She also sees the changed man, who changed &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;for and because&lt;/em&gt; of her, the man who went from &amp;#8220;bad&amp;#8221; to &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;a &amp;#8220;good&amp;#8221; person&lt;/em&gt;. Killing Abby &amp;#8212; who&amp;#8217;s now on her own redemption path &amp;#8212; will only turn Ellie into the bad person that Abby and Joel were.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 And Ellie stops the cycle. She will &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;not&lt;/em&gt; go down the same road. She honours Joel by refusing to become a bad person, something Joel would never have wanted for her. She will honour Joel by going back to the life &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;both of them&lt;/em&gt; wanted for her.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Will she get it? Will JJ and Dina be waiting back in Jackson? We may never know, but I sure hope so.
&lt;/p&gt;
&lt;h3 id="8220bad-people8221"&gt;“Bad people”&lt;/h3&gt;
&lt;p&gt;I think the biggest takeaway for me in this painful, yet wonderful journey of a game is how none of the protagonists were good or bad. Everybody is the hero of their own story. Since we played the first game as Joel with Ellie, that is &lt;em&gt;our&lt;/em&gt; story and we are entirely on their side. But they were not “good,” not when it comes to the many lives they took over the course of their journey. We saw all those kills as completely justified: they were goons, they were going to shoot us! But then, from their perspective, they were doing the same thing we were. And the thing is, that doesn’t mean everyone is equally justified. It only means that &lt;em&gt;from their own points of view&lt;/em&gt;, Ellie and Joel were the villains.&lt;/p&gt;
&lt;p&gt;If doesn’t matter if we believe Joel was justified in taking Ellie from the hospital: from the point of view of Abby, her loving dad was murdered. It’s also not about convincing you and me that there are not moral absolutes. It’s that none of this matters &lt;em&gt;to the characters themselves&lt;/em&gt;.&lt;/p&gt;
&lt;h2 class="_7T4UafM1PdBGycd5na9nF" id="conclusions"&gt;Conclusions&lt;/h2&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 Again, I&amp;#8217;ve finished this game almost 24 hours ago and I am still thinking about it. This game made me &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;feel&lt;/em&gt; so many feelings. It made me mad, sad, and happy. Art that gets you to feel something is, well, good art in my book.
&lt;/p&gt;
&lt;p class="_1qeIAgB0cPwnLhDF9XSiJM"&gt;
 If you hated this game, it&amp;#8217;s fine. We can&amp;#8217;t all love the same thing. I am not trying to convince anyone, just sharing what &lt;em class="_7s4syPYtk5hfUIjySXcRE"&gt;I&lt;/em&gt; felt. I absolutely loved it with all my heart. This game will stay with me for a long time.
&lt;/p&gt;</description></item><item><title>Zero values in Go and Lazy Initialization</title><link>https://rselbach.com/zero-values-in-go-and-lazy-initialization/</link><pubDate>Mon, 17 Dec 2018 13:40:11 +0000</pubDate><guid>https://rselbach.com/zero-values-in-go-and-lazy-initialization/</guid><description>&lt;p&gt;I’m a big fan of the way Go does &lt;em&gt;zero values&lt;/em&gt;, meaning it initializes every variable to a default value. This is in contrast with the way other languages such as, say, C behave. For instance, the printed result of the following C program is unpredictable.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;

int main(void) {
 int i;
 printf(&amp;quot;%d\n&amp;quot;, i);
 return 0;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The value of &lt;code&gt;i&lt;/code&gt; will be whatever happens to be at the position in memory where the compiler happened to allocate the variable. Contrast this with the equivalent program in Go —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import &amp;quot;fmt&amp;quot;

func main() {
 var i int
 fmt.Println(i)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will always print `` because &lt;code&gt;i&lt;/code&gt; is initialized by the compiler to the default value of an &lt;code&gt;int&lt;/code&gt;, which happens to be 0.&lt;/p&gt;
&lt;p&gt;This happens for &lt;em&gt;every&lt;/em&gt; variable of &lt;em&gt;any&lt;/em&gt; type, including our own custom types. What’s even cooler is that this is done recursively, so if you the fields inside a &lt;code&gt;struct&lt;/code&gt; will also be themselves initialized.&lt;/p&gt;
&lt;p&gt;I strive to make all my zero values useful, but it’s not always that simple. Sometimes you need to use different default values for your fields, or maybe you need to initialize one of those fields. This is especially important when we remember that &lt;strong&gt;the zero value of a pointer is nil&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine the following type —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Foobar struct {
 db *DB
}

func NewFoobar() *Foobar {
 return &amp;amp;Foobar{db: DB.New()}
}

func (f *Foobar) Get(key string) (*Foo, error) {
 foo, err := db.Get(key)
 if err != nil {
 return nil, err
 }
 return foo, nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the example above, our zero value is no longer useful: we’d cause a runtime error because &lt;code&gt;db&lt;/code&gt; will be &lt;code&gt;nil&lt;/code&gt; inside the &lt;code&gt;Get()&lt;/code&gt; function. We’re forced to call &lt;code&gt;NewFoobar()&lt;/code&gt; before using our functions.&lt;/p&gt;
&lt;p&gt;But there’s a simple trick to make the &lt;code&gt;Foobar&lt;/code&gt; zero value useful again. As it turns out, being lazy sometimes pays off. Our technique is called &lt;em&gt;lazy initialization&lt;/em&gt; —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Foobar struct {
 dbOnce sync.Once
 db *DB
}

// lazy initialize db
func (f *Foobar) lazyInit() {
 f.dbOnce.Do(func() {
 f.db = DB.New()
 })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We added a &lt;a href="https://golang.org/pkg/sync/#Once"&gt;&lt;code&gt;sync.Once&lt;/code&gt;&lt;/a&gt; to our type. From the Go docs:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Once an object that will perform exactly one action.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The function we pass to &lt;a href="https://golang.org/pkg/sync/#Once.Do"&gt;&lt;code&gt;sync.Once.Do()&lt;/code&gt;&lt;/a&gt; is guaranteed to run &lt;em&gt;once&lt;/em&gt; and &lt;em&gt;only once&lt;/em&gt;, so it is perfect for initializations. Now we can call &lt;code&gt;lazyInit()&lt;/code&gt; at the top of our exported function and it will ensure &lt;code&gt;db&lt;/code&gt; is initialized —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func (f *Foobar) Get(key string) (*Foo, error) {
 f.lazyInit()

 foo, err := db.Get(key)
 if err != nil {
 return nil, err
 }
 return foo, nil
}

...

var f Foobar
foo, err := f.Get(&amp;quot;baz&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are now free to use our zero value with no additional initialization. I love it.&lt;/p&gt;
&lt;p&gt;Of course, it is not always possible to use zero values. For example, our &lt;code&gt;Foobar&lt;/code&gt; assumes a magical object &lt;code&gt;DB&lt;/code&gt; that can be initialized by itself, but in real life we probably need to connect to an external database, authenticate, etc and then pass the created &lt;code&gt;DB&lt;/code&gt; to our &lt;code&gt;Foobar&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Still, using lazy initialization allows us to make a lot of objects’ zero values useful that would otherwise not be.&lt;/p&gt;</description></item><item><title>Playing with Go module proxies</title><link>https://rselbach.com/go-proxies/</link><pubDate>Wed, 29 Aug 2018 19:13:50 +0000</pubDate><guid>https://rselbach.com/go-proxies/</guid><description>&lt;p&gt;(This article has been graciously &lt;a href="https://vectorified.com/ru-go-proxies"&gt;translated to Russian here&lt;/a&gt;. Huge thanks to &lt;a href="https://vectorified.com/"&gt;Akhmad Karimov&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;I wrote a &lt;a href="https://rselbach.com/intro-to-go-modules/"&gt;brief introduction to Go modules&lt;/a&gt; and in it I talked briefly about Go modules proxies and now that &lt;a href="https://blog.golang.org/go1.11"&gt;Go 1.11 is out&lt;/a&gt;, I thought I’d play a bit these proxies to figure our how they’re supposed to work.&lt;/p&gt;
&lt;h1 id="why"&gt;Why&lt;/h1&gt;
&lt;p&gt;One of the goals of Go modules is to provide reproducible builds and it does a very good job by fetching the correct and expected files from a repository.&lt;/p&gt;
&lt;p&gt;But what if the &lt;a href="https://blog.github.com/2016-02-03-january-28th-incident-report/"&gt;servers&lt;/a&gt; &lt;a href="https://blog.bitbucket.org/2012/09/19/post-mortem-on-our-availability-earlier-today/"&gt;are&lt;/a&gt; &lt;a href="https://about.gitlab.com/2017/02/10/postmortem-of-database-outage-of-january-31/"&gt;offline&lt;/a&gt;? What if the repository &lt;a href="https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/"&gt;simply vanishes&lt;/a&gt;?&lt;/p&gt;
&lt;p&gt;One way teams deal with these risks is by vendoring the dependencies, which is fine. But Go modules offers another way: the use of a module proxy.&lt;/p&gt;
&lt;h1 id="the-download-protocol"&gt;The Download Protocol&lt;/h1&gt;
&lt;p&gt;When Go modules support is enabled and the &lt;code&gt;go&lt;/code&gt; command determines that it needs a module, it first looks at the local cache (under &lt;code&gt;$GOPATH/pkg/mods&lt;/code&gt;). If it can’t find the right files there, it then goes ahead and fetches the files from the network (i.e. from a remote repo hosted on Github, Gitlab, etc.)&lt;/p&gt;
&lt;p&gt;If we want to control what files &lt;code&gt;go&lt;/code&gt; can download, we need to tell it to go through our proxy by setting the &lt;code&gt;GOPROXY&lt;/code&gt; environment variable to point to our proxy’s URL. For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOPROXY=http://gproxy.mycompany.local:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The proxy is nothing but a web server that responds to the module download protocol, which is a very simple API to query and fetch modules. The web server may even serve static files.&lt;/p&gt;
&lt;p&gt;A typical scenario would be the &lt;code&gt;go&lt;/code&gt; command trying to fetch &lt;code&gt;github.com/pkg/errors&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/go-proxies/goproxyseq_hu_e2116515e438fb80.webp 320w, https://rselbach.com/go-proxies/goproxyseq_hu_602695c5108d673f.webp 480w, https://rselbach.com/go-proxies/goproxyseq_hu_8acd6e31448bec74.webp 640w, https://rselbach.com/go-proxies/goproxyseq_hu_1aa63827aa9a757b.webp 800w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/go-proxies/goproxyseq.png"
 alt=""
 loading="lazy"
 decoding="async" width="800" height="334" srcset="https://rselbach.com/go-proxies/goproxyseq_hu_f79ab5f401ce078b.png 320w, https://rselbach.com/go-proxies/goproxyseq_hu_a8ec5017b275f4af.png 480w, https://rselbach.com/go-proxies/goproxyseq_hu_81914c2798a4c428.png 640w, https://rselbach.com/go-proxies/goproxyseq_hu_c2beb9149487fbac.png 800w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The first thing &lt;code&gt;go&lt;/code&gt; will do is ask the proxy for a list of available versions. It does this by making a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/{module name}/@v/list&lt;/code&gt;. The server then responds with a simple list of versions it has available:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;v0.8.0
v0.7.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;go&lt;/code&gt; will determine which version it wants to download — the latest unless explicitly told otherwise&lt;sup id="fnref-1943-0"&gt;&lt;a href="#fn-1943-0"&gt;1&lt;/a&gt;&lt;/sup&gt;. It will then request information about that given version by issuing a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/{module name}/@v/{module revision}&lt;/code&gt; to which the server will reply with a JSON representation of the &lt;code&gt;struct&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type RevInfo struct {
 Version string // version string
 Name string // complete ID in underlying repository
 Short string // shortened ID, for use in pseudo-version
 Time time.Time // commit time
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So for instance, we might get something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
 &amp;quot;Version&amp;quot;: &amp;quot;v0.8.0&amp;quot;,
 &amp;quot;Name&amp;quot;: &amp;quot;v0.8.0&amp;quot;,
 &amp;quot;Short&amp;quot;: &amp;quot;v0.8.0&amp;quot;,
 &amp;quot;Time&amp;quot;: &amp;quot;2018-08-27T08:54:46.436183-04:00&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;go&lt;/code&gt; command will then request the module’s &lt;code&gt;go.mod&lt;/code&gt; file by making a &lt;code&gt;GET&lt;/code&gt; request to &lt;code&gt;/{module name}/@v/{module revision}.mod&lt;/code&gt;. The server will simply respond with the contents of the &lt;code&gt;go.mod&lt;/code&gt; file (e.g. &lt;code&gt;module github.com/pkg/errors&lt;/code&gt;.) This file may list additional dependencies and the cycle restarts for each one.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;go&lt;/code&gt; command will request the actual module by getting &lt;code&gt;/{module name}/@v/{module revision}.zip&lt;/code&gt;. The server should respond with a byte blob (&lt;code&gt;application/zip&lt;/code&gt;) containing a zip archive with the module files where each file &lt;em&gt;must&lt;/em&gt; be prefixed by the full module path &lt;em&gt;and&lt;/em&gt; version (e.g. &lt;code&gt;github.com/pkg/errors@v0.8.0/&lt;/code&gt;), i.e. the archive should contain:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;github.com/pkg/errors@v0.8.0/example_test.go
github.com/pkg/errors@v0.8.0/errors_test.go
github.com/pkg/errors@v0.8.0/LICENSE
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And &lt;em&gt;not&lt;/em&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;errors/example_test.go
errors/errors_test.go
errors/LICENSE
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This seems like a lot when written like this, but it’s in fact a very simple protocol that simply fetches 3 or 4 files:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The list of versions (only if &lt;code&gt;go&lt;/code&gt; does not already know which version it wants)&lt;/li&gt;
&lt;li&gt;The module metadata&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;go.mod&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;The module zip itself&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="creating-a-simple-local-proxy"&gt;Creating a simple local proxy&lt;/h1&gt;
&lt;p&gt;To try out the proxy support, let’s create a very basic proxy that will serve static files from a directory. First we create a directory where we will store our in-site copies of our dependencies. Here’s what I have in mine:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ find . -type f
./github.com/robteix/testmod/@v/v1.0.0.mod
./github.com/robteix/testmod/@v/v1.0.1.mod
./github.com/robteix/testmod/@v/v1.0.1.zip
./github.com/robteix/testmod/@v/v1.0.0.zip
./github.com/robteix/testmod/@v/v1.0.0.info
./github.com/robteix/testmod/@v/v1.0.1.info
./github.com/robteix/testmod/@v/list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; &lt;/p&gt;
&lt;p&gt;These are the files our proxy will serve. You can &lt;a href="https://github.com/robteix/go-proxy-blog"&gt;find these files on Github&lt;/a&gt; if you’d like to play along. For the examples below, let’s assume we have a &lt;code&gt;devel&lt;/code&gt; directory under our home directory; adapt accordingly.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cd $HOME/devel
$ git clone https://github.com/robteix/go-proxy-blog.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our proxy server is simple (it could be even simpler, but I wanted to log the requests):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
 &amp;quot;flag&amp;quot;
 &amp;quot;log&amp;quot;
 &amp;quot;net/http&amp;quot;
)

func main() {
 addr := flag.String(&amp;quot;http&amp;quot;, &amp;quot;:8080&amp;quot;, &amp;quot;address to bind to&amp;quot;)
 flag.Parse()

 dir := &amp;quot;.&amp;quot;
 if flag.NArg() &amp;gt; 0 {
 dir = flag.Arg(0)
 }

 log.Printf(&amp;quot;Serving files from %s on %s\n&amp;quot;, dir, *addr)

 h := handler{http.FileServer(http.Dir(dir))}

 panic(http.ListenAndServe(*addr, h))
}

type handler struct {
 h http.Handler
}

func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 log.Println(&amp;quot;New request:&amp;quot;, r.URL.Path)
 h.h.ServeHTTP(w, r)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now run the code above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go run proxy.go -http :8080 $HOME/devel/go-proxy-blog
2018/08/29 14:14:31 Serving files from /home/robteix/devel/go-proxy-blog on :8080

$ curl http://localhost:8080/github.com/robteix/testmod/@v/list
v1.0.0
v1.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leave the proxy running and move to a new terminal. Now let’s create a new test program. we create a new directory &lt;code&gt;$HOME/devel/test&lt;/code&gt; and create a file named &lt;code&gt;test.go&lt;/code&gt; inside it with the following code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main

import (
 &amp;quot;github.com/robteix/testmod&amp;quot;
)

func main() {
 testmod.Hi(&amp;quot;world&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, inside this directory, let’s enable Go modules:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go mod init test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we set the &lt;code&gt;GOPROXY&lt;/code&gt; variable:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOPROXY=http://localhost:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let’s try building our new program:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build
go: finding github.com/robteix/testmod v1.0.1
go: downloading github.com/robteix/testmod v1.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if you check the output from our proxy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2018/08/29 14:56:14 New request: /github.com/robteix/testmod/@v/list
2018/08/29 14:56:14 New request: /github.com/robteix/testmod/@v/v1.0.1.info
2018/08/29 14:56:14 New request: /github.com/robteix/testmod/@v/v1.0.1.mod
2018/08/29 14:56:14 New request: /github.com/robteix/testmod/@v/v1.0.1.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So as long as &lt;code&gt;GOPROXY&lt;/code&gt; is set, &lt;code&gt;go&lt;/code&gt; will only download files from out proxy. If I go ahead and delete the repository from Github, things will continue to work.&lt;/p&gt;
&lt;h1 id="using-a-local-directory"&gt;Using a local directory&lt;/h1&gt;
&lt;p&gt;It is interesting to note that we don’t even need our &lt;code&gt;proxy.go&lt;/code&gt; at all. We can set &lt;code&gt;GOPROXY&lt;/code&gt; to point to a directory in the filesystem and things will still work as expected:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export GOPROXY=file://home/robteix/devel/go-proxy-blog
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we do a &lt;code&gt;go build&lt;/code&gt; now&lt;sup id="fnref-1943-1"&gt;&lt;a href="#fn-1943-1"&gt;2&lt;/a&gt;&lt;/sup&gt;, we’ll see &lt;em&gt;exactly&lt;/em&gt; the same thing as with the proxy:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ go build
go: finding github.com/robteix/testmod v1.0.1
go: downloading github.com/robteix/testmod v1.0.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course, in real life, we probably will prefer to have a company/team proxy server where our dependencies are stored, because a local directory is not really much different from the local cache that &lt;code&gt;go&lt;/code&gt; already maintains under &lt;code&gt;$GOPATH/pkg/mod&lt;/code&gt;, but still, nice to know that it works.&lt;/p&gt;
&lt;p&gt;There is a &lt;a href="https://github.com/gomods/athens"&gt;project called Athens&lt;/a&gt; that is building a proxy and that aims — if I don’t misunderstand it — to create a central repository of packages à la npm.&lt;/p&gt;
&lt;li id="fn-1943-0"&gt;
 Remember that &lt;code&gt;somepackage&lt;/code&gt; and &lt;code&gt;somepackage/v2&lt;/code&gt; are treated as different packages.&amp;#160;&lt;a href="#fnref-1943-0"&gt;&amp;#8617;&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-1943-1"&gt;
 That&amp;#8217;s not strictly true as now that we&amp;#8217;ve already built it once, &lt;code&gt;go&lt;/code&gt; has cached the module locally and will not go to the proxy (or the network) at all. You can still force it by deleting &lt;code&gt;$GOPATH/pkg/mod/cache/download/github.com/robteix/testmod/&lt;/code&gt; and &lt;code&gt;$GOPATH/pkg/mod/github.com/robteix/testmod@v1.0.1&lt;/code&gt;)&amp;#160;&lt;a href="#fnref-1943-1"&gt;&amp;#8617;&lt;/a&gt; &lt;/fn&gt;&lt;/footnotes&gt;</description></item><item><title>Introduction to Go Modules</title><link>https://rselbach.com/intro-to-go-modules/</link><pubDate>Sat, 18 Aug 2018 18:08:20 +0000</pubDate><guid>https://rselbach.com/intro-to-go-modules/</guid><description>&lt;p&gt;This post is also available in other languages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Russian: &lt;a href="https://howtorecover.me/vvedenie-v-go-modules"&gt;Введение в модули Go&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Uzbek: &lt;a href="https://pngflare.com/uz-go-modules"&gt;Go modullariga kirish&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The upcoming version 1.11 of the Go programming language will bring experimental support for &lt;em&gt;modules&lt;/em&gt;, a new dependency management system for Go. A few days ago, &lt;a href="https://rselbach.com/playing-with-go-modules/"&gt;I wrote a quick post about it&lt;/a&gt;. Since that post went live, things changed a bit and as we’re now very close to the new release, I thought it would be a good time for another post with a more hands-on approach. So here’s what we’ll do: we’ll create a new package and then we’ll make a few releases to see how that would work.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="creating-a-module"&gt;Creating a Module&lt;/h2&gt;
&lt;p&gt;So first things first. Let’s create our package. We’ll call it “testmod”. An important detail here: **this directory should be **&lt;strong&gt;&lt;em&gt;outside&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;your&lt;/strong&gt; &lt;strong&gt;&lt;code&gt;$GOPATH&lt;/code&gt;&lt;/strong&gt; &lt;strong&gt;because by default, the modules support is disabled inside it&lt;/strong&gt;. Go modules is a first step in potentially eliminating &lt;code&gt;$GOPATH&lt;/code&gt; entirely at some point.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ mkdir testmod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cd testmod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our package is very simple:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testmod&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;import&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;fmt&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;// Hi returns a friendly greeting&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;string&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 style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Hi, %s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&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 package is done but it is still not a &lt;em&gt;module&lt;/em&gt;. Let’s change 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod init github.com/robteix/testmod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: creating new go.mod: module github.com/robteix/testmod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a new file named &lt;code&gt;go.mod&lt;/code&gt; in the package directory with the following contents:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module github.com/robteix/testmod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Not a lot here, but this effectively turns our package into a &lt;em&gt;module&lt;/em&gt;. We can now push this code to a repository:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git init
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git add *
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git commit -am &lt;span style="color:#e6db74"&gt;&amp;#34;First commit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git push -u origin master
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Until now, anyone willing to use this package would &lt;code&gt;go get&lt;/code&gt; 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go get github.com/robteix/testmod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this would fetch the latest code in &lt;code&gt;master&lt;/code&gt;. This still works, but we should probably stop doing that now that we have a Better Way™. Fetching &lt;code&gt;master&lt;/code&gt; is inherently dangerous as we can never know for sure that the package authors didn’t make change that will break our usage. That’s what modules aims at fixing.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="quick-intro-to-module-versioning"&gt;Quick Intro to Module Versioning&lt;/h2&gt;
&lt;p&gt;Go modules are &lt;em&gt;versioned&lt;/em&gt;, and there are some particularities with regards to certain versions. You will need to familiarize yourself with the concepts behind &lt;a href="https://semver.org"&gt;semantic versioning&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;More importantly, Go will use repository tags when looking for versions, and some versions are different of others: e.g. versions 2 and greater should have a different import path than versions 0 and 1 (we’ll get to that.) As well, by default Go will fetch the latest &lt;em&gt;tagged version&lt;/em&gt; available in a repository. This is an important gotcha as you may be used to working with the master branch. What you need to keep in mind for now is that to make a release of our package, we need to tag our repository with the version. So let’s do that.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="making-our-first-release"&gt;Making our first release&lt;/h2&gt;
&lt;p&gt;Now that our package is ready, we can release it to the world. We do this by using version tags. Let’s release our version 1.0.0:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git tag v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git push --tags
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This creates a tag on my Github repository marking the current commit as being the release 1.0.0.&lt;/p&gt;
&lt;p&gt;Go doesn’t enforce that in any way, but a good idea is to also create a new branch (“v1”) so that we can push bug fixes 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout -b v1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git push -u origin v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can work on &lt;code&gt;master&lt;/code&gt; without having to worry about breaking our release.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="using-our-module"&gt;Using our module&lt;/h2&gt;
&lt;p&gt;Now we’re ready to use the module. We’ll create a simple program that will use our new package:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&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;import&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;fmt&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:#e6db74"&gt;&amp;#34;github.com/robteix/testmod&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:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;testmod&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;roberto&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;Until now, you would do a &lt;code&gt;go get github.com/robteix/testmod&lt;/code&gt; to download the package, but with modules, this gets more interesting. First we need to enable modules in our new program.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod init mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As you’d expect from what we’ve seen above, this will have created a new &lt;code&gt;go.mod&lt;/code&gt; file with the module name in 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module mod
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Things get much more interesting when we try to build our new program:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: finding github.com/robteix/testmod v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: downloading github.com/robteix/testmod v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As we can see, the &lt;code&gt;go&lt;/code&gt; command automatically goes and fetches the packages imported by the program. If we check our &lt;code&gt;go.mod&lt;/code&gt; file, we see that things have changed:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module mod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;require github.com/robteix/testmod v1.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And we now have a new file too, named &lt;code&gt;go.sum&lt;/code&gt;, which contains hashes of the packages, to ensure that we have the correct version and files.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;github.com/robteix/testmod v1.0.0 h1:9EdH0EArQ/rkpss9Tj8gUnwx3w5p0jkzJrd5tRAhxnA=
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;github.com/robteix/testmod v1.0.0/go.mod h1:UVhi5McON9ZLc5kl5iN2bTXlL6ylcxE9VInV71RrlO8=
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading" id="making-a-bugfix-release"&gt;Making a bugfix release&lt;/h2&gt;
&lt;p&gt;Now let’s say we realized a problem with our package: the greeting is missing ponctuation! People are mad because our friendly greeting is not friendly enough. So we’ll fix it and release a new version:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-diff" data-lang="diff"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;// Hi returns a friendly greeting
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;func Hi(name string) string {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;- return fmt.Sprintf(&amp;#34;Hi, %s&amp;#34;, name)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;+ return fmt.Sprintf(&amp;#34;Hi, %s!&amp;#34;, name)
&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;We made this change in the v1 branch because it’s not relevant for what we’ll do for v2 later, but in real life, maybe you’d do it in &lt;code&gt;master&lt;/code&gt; and then back-port it. Either way, we need to have the fix in our v1 branch and mark it as a new release.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git commit -m &lt;span style="color:#e6db74"&gt;&amp;#34;Emphasize our friendliness&amp;#34;&lt;/span&gt; testmod.go
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git tag v1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git push --tags origin v1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading" id="updating-modules"&gt;Updating modules&lt;/h2&gt;
&lt;p&gt;By default, Go will not update modules without being asked. This is a Good Thing™ as we want predictability in our builds. If Go modules were automatically updated every time a new version came out, we’d be back in the uncivilized age pre-Go1.11. No, we need to &lt;em&gt;tell&lt;/em&gt; Go to update a modules for us.&lt;/p&gt;
&lt;p&gt;We do this by using our good old friend &lt;code&gt;go get&lt;/code&gt;:&lt;/p&gt;
&lt;ul class="wp-block-list"&gt;
 &lt;li&gt;
 run &lt;code&gt;go get -u&lt;/code&gt; to use the latest &lt;i&gt;minor or patch&lt;/i&gt; releases (i.e. it would update from 1.0.0 to, say, 1.0.1 or, if available, 1.1.0)
 &lt;/li&gt;
 &lt;li&gt;
 run &lt;code&gt;go get -u=patch&lt;/code&gt; to use the latest &lt;i&gt;patch&lt;/i&gt; releases (i.e., would update to 1.0.1 but &lt;i&gt;not&lt;/i&gt; to 1.1.0)
 &lt;/li&gt;
 &lt;li&gt;
 run &lt;code&gt;go get package@version&lt;/code&gt; to update to a specific version (say, &lt;code&gt;github.com/robteix/testmod@v1.0.1&lt;/code&gt;)
 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the list above, there doesn’t seem to be a way to update to the latest &lt;em&gt;major&lt;/em&gt; version. There’s a good reason for that, as we’ll see in a bit.&lt;/p&gt;
&lt;p&gt;Since our program was using version 1.0.0 of our package and we just created version 1.0.1, &lt;em&gt;any&lt;/em&gt; of the following commands will update us to 1.0.1:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go get -u
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go get -u&lt;span style="color:#f92672"&gt;=&lt;/span&gt;patch
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go get github.com/robteix/testmod@v1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After running, say, &lt;code&gt;go get -u&lt;/code&gt; our &lt;code&gt;go.mod&lt;/code&gt; is changed 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module mod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;require github.com/robteix/testmod v1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading" id="major-versions"&gt;Major versions&lt;/h2&gt;
&lt;p&gt;According to semantic version semantics, a major version is &lt;em&gt;different&lt;/em&gt; than minors. Major versions can break backwards compatibility. From the point of view of Go modules, a major version is a &lt;em&gt;different package&lt;/em&gt; completely. This may sound bizarre at first, but it makes sense: two versions of a library that are not compatible with each other are two different libraries.&lt;/p&gt;
&lt;p&gt;Let’s make a major change in our package, shall we? Over time, we realized our API was too simple, too limited for the use cases of our users, so we need to change the &lt;code&gt;Hi()&lt;/code&gt; function to take a new parameter for the greeting language:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testmod&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;import&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;errors&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;&amp;#34;fmt&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;// Hi returns a friendly greeting in language lang&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;lang&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) (&lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;, &lt;span style="color:#66d9ef"&gt;error&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;switch&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;lang&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;en&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Hi, %s!&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;), &lt;span style="color:#66d9ef"&gt;nil&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;pt&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Oi, %s!&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;), &lt;span style="color:#66d9ef"&gt;nil&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;es&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;¡Hola, %s!&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;), &lt;span style="color:#66d9ef"&gt;nil&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;fr&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;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Sprintf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Bonjour, %s!&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;), &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;default&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 style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;errors&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;unknown language&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Existing software using our API will break because they (a) don’t pass a language parameter and (b) don’t expect an error return. Our new API is no longer compatible with version 1.x so it’s time to bump the version to 2.0.0.&lt;/p&gt;
&lt;p&gt;I mentioned before that some versions have some peculiarities, and this is the case now. **Versions 2 **&lt;strong&gt;&lt;em&gt;and over&lt;/em&gt;&lt;/strong&gt; &lt;strong&gt;should change the import path.&lt;/strong&gt; They are different libraries now.&lt;/p&gt;
&lt;p&gt;We do this by appending a new &lt;em&gt;version path&lt;/em&gt; to the end of our module name.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module github.com/robteix/testmod/v2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The rest is the same as before, we push it, tag it as v2.0.0 (and optionally create a v2 branch.)&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git commit testmod.go -m &lt;span style="color:#e6db74"&gt;&amp;#34;Change Hi to allow multilang&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git checkout -b v2 &lt;span style="color:#75715e"&gt;# optional but recommended&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;#34;module github.com/robteix/testmod/v2&amp;#34;&lt;/span&gt; &amp;gt; go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git commit go.mod -m &lt;span style="color:#e6db74"&gt;&amp;#34;Bump version to v2&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git tag v2.0.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ git push --tags origin v2 &lt;span style="color:#75715e"&gt;# or master if we don&amp;#39;t have a branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 class="wp-block-heading" id="updating-to-a-major-version"&gt;Updating to a major version&lt;/h2&gt;
&lt;p&gt;Even though we have released a new incompatible version of our library, existing software &lt;em&gt;will not break&lt;/em&gt;, because it will continue to use the existing version 1.0.1. &lt;code&gt;go get -u&lt;/code&gt; &lt;em&gt;will not&lt;/em&gt; get version 2.0.0.&lt;/p&gt;
&lt;p&gt;At some point, however, I, as the library user, may want to upgrade to version 2.0.0 because maybe I was one of those users who needed multi-language support.&lt;/p&gt;
&lt;p&gt;I do it but modifying my program accordingly:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&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;import&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;fmt&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;&amp;#34;github.com/robteix/testmod/v2&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:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;g&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testmod&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Roberto&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;pt&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; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		panic(&lt;span style="color:#a6e22e"&gt;err&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;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;g&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;And then when I run &lt;code&gt;go build&lt;/code&gt;, it will go and fetch version 2.0.0 for me. Notice how even though the import path ends with “v2”, Go will still refer to the module by its proper name (“testmod”).&lt;/p&gt;
&lt;p&gt;As I mentioned before, the major version is for all intents and purposes a completely different package. Go modules does not link the two at all. That means we can use two incompatible versions in the same binary:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;import&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;fmt&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;&amp;#34;github.com/robteix/testmod&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;testmodML&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;github.com/robteix/testmod/v2&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:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;testmod&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Roberto&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;g&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;testmodML&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Hi&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;Roberto&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;pt&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; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		panic(&lt;span style="color:#a6e22e"&gt;err&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;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;g&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 eliminates a common problem with dependency management: when dependencies depend on different versions of the same library.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="tidying-it-up"&gt;Tidying it up&lt;/h2&gt;
&lt;p&gt;Going back to the previous version that uses only testmod 2.0.0, if we check the contents of &lt;code&gt;go.mod&lt;/code&gt; now, we’ll notice something:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;module mod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;require github.com/robteix/testmod v1.0.1
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;require github.com/robteix/testmod/v2 v2.0.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default, Go does not remove a dependency from &lt;code&gt;go.mod&lt;/code&gt; unless you ask it to. If you have dependencies that you no longer use and want to clean up, you can use the new &lt;code&gt;tidy&lt;/code&gt; command:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod tidy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we’re left with only the dependencies that are really being used.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="vendoring"&gt;Vendoring&lt;/h2&gt;
&lt;p&gt;Go modules ignores the &lt;code&gt;vendor/&lt;/code&gt; directory by default. The idea is to &lt;em&gt;eventually&lt;/em&gt; do away with vendoring[^0]. But if we still want to add vendored dependencies to our version control, we can still do 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod vendor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will create a &lt;code&gt;vendor/&lt;/code&gt; directory under the root of your project containing the source code for all of your dependencies.&lt;/p&gt;
&lt;p&gt;Still, &lt;code&gt;go build&lt;/code&gt; will ignore the contents of this directory by default. If you want to build dependencies from the &lt;code&gt;vendor/&lt;/code&gt; directory, you’ll need to ask for 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go build -mod vendor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I expect many developers willing to use vendoring will run &lt;code&gt;go build&lt;/code&gt; normally on their development machines and use &lt;code&gt;-mod vendor&lt;/code&gt; in their CI.&lt;/p&gt;
&lt;p&gt;Again, Go modules is moving away from the idea of vendoring and towards using a Go module proxy for those who don’t want to depend on the upstream version control services directly.&lt;/p&gt;
&lt;p&gt;There are ways to guarantee that &lt;code&gt;go&lt;/code&gt; will not reach the network at all (e.g. &lt;code&gt;GOPROXY=off&lt;/code&gt;) but these are the subject for a future blog post.&lt;/p&gt;
&lt;h2 class="wp-block-heading" id="conclusion"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This post may seem a bit daunting, but I tried to explain a lot of things together. The reality is that now Go modules is basically transparent. We import package like always in our code and the &lt;code&gt;go&lt;/code&gt; command will take care of the rest.&lt;/p&gt;
&lt;p&gt;When we build something, the dependencies will be fetched automatically. It also eliminates the need to use &lt;code&gt;$GOPATH&lt;/code&gt; which was a roadblock for new Go developers who had trouble understanding why things had to go into a specific directory.&lt;/p&gt;
&lt;p&gt;&lt;del datetime="2018-08-21T13:06:20+00:00"&gt;Vendoring is (unofficially) being deprecated in favour of using proxies.&lt;/del&gt;[^0] I may do a separate post about the Go module proxy. &lt;em&gt;(Update: &lt;a href="https://rselbach.com/go-proxies/"&gt;it’s live&lt;/a&gt;.) [^0]: I think this came out a bit too strong and people left with the impression that vendoring is being removed right now. It isn’t. Vendoring still works, albeit slightly different than before. There seems to be a &lt;em&gt;desire&lt;/em&gt; to replace vendoring with something better, which may or may not be a proxy. But for now this is just it: a desire for a better solution. Vendoring is not going away until a good replacement is found (if ever.)&lt;/em&gt;&lt;/p&gt;</description></item><item><title>New licence plate</title><link>https://rselbach.com/new-licence-plate/</link><pubDate>Mon, 30 Jul 2018 12:51:35 +0000</pubDate><guid>https://rselbach.com/new-licence-plate/</guid><description>&lt;p&gt;&lt;em&gt;Update: the licence plate application has since been refused. The reason given is that they don’t allow offensive messages. All I can think of is that they misread it as being “GOP HER,” which doesn’t mean anything but they may have assumed it was some code, new slang or something.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I live in Quebec and only recently the province opened registrations for personalized license plates. At first I didn’t even consider it, but this morning I impulse-bought one:&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/new-licence-plate/image_hu_af4c421f0774952e.webp 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/new-licence-plate/image.png"
 alt=""
 loading="lazy"
 decoding="async" width="320" height="161" srcset="https://rselbach.com/new-licence-plate/image_hu_4849fcc2a045b8d9.png 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;This will of course be a homage to my favourite mascot, &lt;a href="https://blog.golang.org/gopher"&gt;Go’s&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Playing With Go Modules</title><link>https://rselbach.com/playing-with-go-modules/</link><pubDate>Fri, 20 Jul 2018 15:42:36 +0000</pubDate><guid>https://rselbach.com/playing-with-go-modules/</guid><description>&lt;p&gt;&lt;em&gt;Update: much of this article has been rendered obsolete by changes made to Go modules since. &lt;a href="https://rselbach.com/intro-to-go-modules/"&gt;Check this more recent post that’s up to date&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Had some free time in my hands, so I decided to check out the new Go modules. For those unaware, the next Go release (1.11) will include a new functionality that aims at addressing package management called “modules”. It will still be marked as experimental, so things may very well change a lot.&lt;/p&gt;
&lt;p&gt;Here’s how it works. Let’s say we create a new package:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rselbach@wile ~/code $ mkdir foobar
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rselbach@wile ~/code $ cd foobar/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our &lt;code&gt;foobar.go&lt;/code&gt; will be very simple:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;foobar&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;import&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;fmt&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;&amp;#34;io&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:#e6db74"&gt;&amp;#34;github.com/pkg/errors&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:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;WriteGreet&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;w&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;io&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Writer&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt;) &lt;span style="color:#66d9ef"&gt;error&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;_&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fprintln&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;w&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Hello&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&lt;/span&gt;); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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 style="color:#a6e22e"&gt;errors&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Wrapf&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;Could not greet %s&amp;#34;&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;name&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;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&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 we’re using Dave Cheney’s excellent &lt;code&gt;errors&lt;/code&gt; package. Until now, &lt;code&gt;go get&lt;/code&gt; would fetch whatever it found on that package’s repository. If Dave ever decided to change something drastic in the package, our code would probably break without us even knowing about it until too late.&lt;/p&gt;
&lt;p&gt;That’s where Go modules come into play, as it will help us (1) formalize the dependency and (2) lock a particular version of it to our code. First we need to turn on the support for modules in our package. We do this by using the new &lt;code&gt;go mod&lt;/code&gt; command and giving out module a name:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod -init -module github.com/rselbach/foobar
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: creating new go.mod: module github.com/rselbach/foobar
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After that, you will notice that a new file called &lt;code&gt;go.mod&lt;/code&gt; will be created in our package root. For now, it only contains the module name we gave it above:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module github.com/rselbach/foobar
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it does more than that, for now the &lt;code&gt;go&lt;/code&gt; command is aware that we are in a module-aware package and will behave accordingly. See what happens when we first try to build 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: finding github.com/pkg/errors v0.8.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: downloading github.com/pkg/errors v0.8.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It sees that we are using an external package and then it goes out, finds the latest version of it, downloads it, and adds it to our &lt;code&gt;go.mod&lt;/code&gt; file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;module github.com/rselbach/foobar

require github.com/pkg/errors v0.8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From now one, whenever someone uses our package, Go will also download version 0.8.0 of Dave’s errors package. If version 0.9.0 completely breaks compatibility, it will not break our code for it will continue to be compiled with correct version.&lt;/p&gt;
&lt;p&gt;If we change &lt;code&gt;go.mod&lt;/code&gt; to require version 0.7.0, our next &lt;code&gt;go build&lt;/code&gt; will fetch it as 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go build
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: finding github.com/pkg/errors v0.7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go: downloading github.com/pkg/errors v0.7.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you’re wondering where the packages are, they’re stored under &lt;code&gt;$GOPATH/src/mod&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ ls -l ~/go/src/mod/github.com/pkg/
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dr-xr-xr-x 13 rselbach staff 416 20 Jul 07:44 errors@v0.7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dr-xr-xr-x 14 rselbach staff 448 20 Jul 07:40 errors@v0.8.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;What about vendoring?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Good thing you asked. This is where I don’t like Go modules’ approach. It specifically aims at doing away with vendoring. Versioning is obviously taken care of by the modules functionality itself while disponibility is supposed to be taken care of by something like caching proxies.&lt;/p&gt;
&lt;p&gt;I don’t see it. First of all, not everybody is Google. Maintaining cache proxies is extra work that many organizations don’t have the resources for. Also, what about when we’re working from home? Or on the commute? Sure, a VPN solves this, but then it’s one more thing we need to maintain.&lt;/p&gt;
&lt;p&gt;That doesn’t mean you can’t vendor with Go modules. It’s simply not the default way it works. Let’s see how it works though. Let’s say we want to vendor our dependencies:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ go mod -vendor
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rselbach@wile ~/code/foobar $ ls -la
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total 24
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 6 rselbach staff 192 20 Jul 08:01 .
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 33 rselbach staff 1056 20 Jul 07:14 ..
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 rselbach staff 252 20 Jul 07:22 foobar.go
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 rselbach staff 72 20 Jul 07:43 go.mod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-rw-r--r-- 1 rselbach staff 322 20 Jul 07:44 go.sum
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;drwxr-xr-x 4 rselbach staff 128 20 Jul 08:01 vendor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice how &lt;code&gt;go mod&lt;/code&gt; now has created a &lt;code&gt;vendor&lt;/code&gt; directory in our module root. Inside it you’ll find the source code for the modules we are using. It also contains a file with a list of packages and versions:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ cat vendor/modules.txt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;# github.com/pkg/errors v0.7.0
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;github.com/pkg/errors
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can now add this to our repository. But it’s not all done. Surprisingly, when modules support is enabled, the &lt;code&gt;go&lt;/code&gt; command will ignore our &lt;code&gt;vendor&lt;/code&gt; directory completely. In order to actually use our vendor directory, we need to explicitely tell &lt;code&gt;go build&lt;/code&gt; to use 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;go build -v -getmode=vendor
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This essentially emulates the behaviour of previous version of Go.&lt;/p&gt;</description></item><item><title>An LRU in Go (Part 2)</title><link>https://rselbach.com/an-lru-in-go-part-2/</link><pubDate>Fri, 15 Jun 2018 15:41:49 +0000</pubDate><guid>https://rselbach.com/an-lru-in-go-part-2/</guid><description>&lt;p&gt;So we created a concurrency-safe LRU in the last post, but it was too slow when used concurrently because of all the locking.&lt;/p&gt;
&lt;p&gt;Reducing the amount of time spent waiting on locks is actually not trivial, but not undoable. You can use things like the &lt;code&gt;sync/atomic&lt;/code&gt; package to cleverly change pointers back and forth with basically no locking needed. However, our situation is more complicated than that: we use two separate data structures that need to be updated atomically (the list itself and the index.) I don’t know that we can do that with no mutexes.&lt;/p&gt;
&lt;p&gt;We can, however, easily lessen the amount of time spent waiting on mutexes by using &lt;em&gt;sharding&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You see, right now we have a single &lt;code&gt;list.List&lt;/code&gt; to hold the data and a &lt;code&gt;map&lt;/code&gt; for the index. This means that when one goroutine is trying to add, say, &lt;code&gt;{&amp;quot;foo&amp;quot;: &amp;quot;bar&amp;quot;}&lt;/code&gt;, it has to wait until another finishes updating &lt;code&gt;{&amp;quot;bar&amp;quot; : &amp;quot;baz&amp;quot; }&lt;/code&gt; because even though the two keys are not related at all, the lock needs to protect the entire list and index.&lt;/p&gt;
&lt;p&gt;A quick and easy way to go around this is to split the data structures into shards. Each shard has its own data structures:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;shard&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;len&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int32&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;sync&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Mutex&lt;/span&gt; &lt;span style="color:#75715e"&gt;// protects the index and list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}]&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Element&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the index for our list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;List&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the actual list holding the data&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;And so now, our LRU looks a bit different:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the max number of items to hold&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;nshards&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#75715e"&gt;// number of shards&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;shardMask&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int64&lt;/span&gt; &lt;span style="color:#75715e"&gt;// mask used to select correct shard for key&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;shards&lt;/span&gt; []&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;shard&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 methods in LRU are now also much simpler as all they do is call an equivalent method in a given shard:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;val&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;shard&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;val&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 now it’s time to figure out how to select the correct shard for a given key. In order to prevent two different values of the same key to be stored in two different shards, we need a given key to always return the same shard. We do this by passing a byte representation of the key through the &lt;a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function"&gt;Fowler–Noll–Vo hash function&lt;/a&gt; to generate a hash as an &lt;code&gt;int32&lt;/code&gt; number. We do a logical &lt;code&gt;AND&lt;/code&gt; between this hash and a shard mask.&lt;/p&gt;
&lt;p&gt;The hard part is actually getting a byte representation of a key. It would be trivial if the key was of a fixed type (say, &lt;code&gt;int&lt;/code&gt; or &lt;code&gt;string&lt;/code&gt;) but we actually use &lt;code&gt;interface{}&lt;/code&gt;, which gives us a bit more work to do. The &lt;code&gt;shard()&lt;/code&gt; function actually is a large &lt;code&gt;type switch&lt;/code&gt; that tries to find the quickest way possible to find the byte representation of any given type.&lt;/p&gt;
&lt;p&gt;For instance, if the type is &lt;code&gt;int&lt;/code&gt;, we 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;il&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;strconv&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;IntSize&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;intBytes&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;) []&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; make([]&lt;span style="color:#66d9ef"&gt;byte&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;il&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;16&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;24&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;il&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;8&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;5&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;40&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;48&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt;[&lt;span style="color:#ae81ff"&gt;7&lt;/span&gt;] = byte(&lt;span style="color:#a6e22e"&gt;i&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;56&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; &lt;span style="color:#a6e22e"&gt;b&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;We do similar things to all of the known types. We also check for custom types that provide a &lt;code&gt;String() string&lt;/code&gt; method (i.e. implement the &lt;code&gt;Stringer&lt;/code&gt; interface.) This should allow for getting the byte representation for the vast majority of types people are likely to want to use as a key. If, however, the type is unknown and is not a Stringer (nor has a &lt;code&gt;Bytes() []byte&lt;/code&gt; method), then we fall back to using &lt;a href="https://golang.org/pkg/encoding/gob/#Encoder"&gt;gob.Encoder&lt;/a&gt;, which will work but is &lt;em&gt;very&lt;/em&gt; slow to make it realistic usable.&lt;/p&gt;
&lt;p&gt;So what does all of this does for us? Now we never lock the entire LRU when doing operations, instead locking only small portions of it when needed, this results in my less time spent waiting for mutexes on average.&lt;/p&gt;
&lt;p&gt;We can play around with how many shards we want depending on how much data we store, but we can potentially have thousands of very small shards to provide very fine locking. Of course, since dealing with sharding does have a small overhead, at some point it is not worth adding more.&lt;/p&gt;
&lt;p&gt;The following benchmarks were done with 10000 shards and without concurrency —&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_new-4 1000000 1006 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_existing-4 10000000 236 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_found-4 5000000 571 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_not_found-4 10000000 289 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_found-4 3000000 396 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_not_found-4 10000000 299 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this with 10000 shards with concurrent access —&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAddParallel/mostly_new-4 2000000 719 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAddParallel/mostly_existing-4 10000000 388 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGetParallel/mostly_found-4 10000000 147 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGetParallel/mostly_not_found-4 20000000 126 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemoveParallel/mostly_found-4 10000000 142 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemoveParallel/mostly_not_found-4 20000000 338 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Still, each shard still locks completely at every access, we might do better than that.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/rselbach/lru_blogpost/tree/master/sharded/lru"&gt;Source code on Github.&lt;/a&gt;&lt;/p&gt;</description></item><item><title>An LRU in Go (Part 1)</title><link>https://rselbach.com/an-lru-in-go-part-1/</link><pubDate>Wed, 13 Jun 2018 14:35:39 +0000</pubDate><guid>https://rselbach.com/an-lru-in-go-part-1/</guid><description>&lt;p&gt;In my job, I often catch myself having to implement a way to keep data in memory so I can use them at a later time. Sometimes this is for caching purposes, sometimes it’s to keep track of what I’ve sent some other service earlier so I can create a diff or something. Reasons are plenty.&lt;/p&gt;
&lt;p&gt;I noticed that I keep writing versions of the same thing over and over and when we’re doing that, maybe it’s time to try and create a more generic solution. So let’s start simple, shall we? What we want is to implement a least-recently-used list (LRU) with no fuss.&lt;/p&gt;
&lt;h2 id="naïve-lru"&gt;Naïve LRU&lt;/h2&gt;
&lt;p&gt;We start with a simple and very naïve implementation (&lt;a href="https://github.com/rselbach/lru_blogpost/tree/master/naive/lru"&gt;see the full implementation on Github&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the max number of items to hold&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}]&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Element&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the index for our list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;List&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the actual list holding the data&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;We don’t have much there, we have a doubly-linked list &lt;code&gt;l&lt;/code&gt; to hold the data and a &lt;code&gt;map&lt;/code&gt; to serve as an index. In true Go fashion, we create an initializer —&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;(),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt;: make(&lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}]&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Element&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&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:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;l&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 nice. Package users will initialize the list with something like &lt;code&gt;l := lru.New(1000)&lt;/code&gt; and joy will spread thoughout the land. But I strongly believe the empty value of a struct should also be usable, which is not the case 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;var&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;world&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will panic because neither &lt;code&gt;l&lt;/code&gt; nor &lt;code&gt;idx&lt;/code&gt; are initialized. My go-to solution for this is to always provide a simples unexported initialization function that is run at the beginning of each exported method and makes sure the initialization is done.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;lazyInit&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;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;==&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;New&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt; = make(&lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}]&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Element&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt;&lt;span style="color:#f92672"&gt;+&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now, let’s see how we’d add a new item to the list —&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;lazyInit&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;// first let&amp;#39;s see if we already have this key&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;le&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;ok&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;]; &lt;span style="color:#a6e22e"&gt;ok&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// update the entry and move it to the front&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;le&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Value&lt;/span&gt;.(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;entry&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;val&lt;/span&gt; = &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;MoveToFront&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;le&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt;[&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;] = &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;PushFront&lt;/span&gt;(&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;entry&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;val&lt;/span&gt;: &lt;span style="color:#a6e22e"&gt;v&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:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &amp;gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Len&lt;/span&gt;() &amp;gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;removeOldest&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;
&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;We call &lt;code&gt;lazyInit&lt;/code&gt; to make sure everything is initialized than we check whether the key already is in our list. If it is, we only need to update the value and move it to the front of the list as it’s now become the most-recently-used.&lt;/p&gt;
&lt;p&gt;If, however, the key was not already in our index, then we need to create a new entry and push it to the list. By the way, this is the first time we see &lt;code&gt;entry&lt;/code&gt;, which is an unexported type that we actually store in our list.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;entry&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;key&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;val&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&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;Finally, we check whether by adding the new element, we just passed the capacity of the list (if cap is less than 1, we treat as there being no limit to capacity, by the way). If we are over capacity, we go ahead and delete the oldest element in the list.&lt;/p&gt;
&lt;p&gt;This is a naïve implementation, but it’s also very fast.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_new-4 2000000 742 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_existing-4 10000000 153 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_found-4 10000000 222 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_not_found-4 20000000 143 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_found-4 10000000 246 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_not_found-4 20000000 142 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Getting and removing elements is in the same ballpark as accessing a built-in Go &lt;code&gt;map&lt;/code&gt;. Getting an existing item adds a bit of overhead on top of accessing the &lt;code&gt;map&lt;/code&gt; because it also needs to move the element to the front of the list, but that’s a very fast pointer operation so it’s not that bad.&lt;/p&gt;
&lt;p&gt;Adding has to check for an existing item as well as adding/moving the list element, so it’s relatively slower, but still good enough for my immediate needs.&lt;/p&gt;
&lt;p&gt;There is a big problem though. This implementation is not at all safe for concurrent use. Sure, package users could protect it with a &lt;code&gt;sync.Mutex&lt;/code&gt; but we may want to hide the complexity from them.&lt;/p&gt;
&lt;p&gt;The (once again naïve) solution is to protect the list and map accesses with a &lt;code&gt;sync.Mutex&lt;/code&gt; of our own.&lt;/p&gt;
&lt;h2 id="concurrent-safe-naïve-lru"&gt;Concurrent-safe Naïve LRU&lt;/h2&gt;
&lt;p&gt;Not much needs to be changed to make our naïve implementation safe for concurrent use (&lt;a href="https://github.com/rselbach/lru_blogpost/tree/master/naive-mutex/lru"&gt;full implementation on Github&lt;/a&gt;.). We add a mutex —&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;cap&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the max number of items to hold&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;sync&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Mutex&lt;/span&gt; &lt;span style="color:#75715e"&gt;// protects the idem and list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;idx&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;map&lt;/span&gt;[&lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}]&lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Element&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the index for our list&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;list&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;List&lt;/span&gt; &lt;span style="color:#75715e"&gt;// the actual list holding the data&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;And at the top of every exported entrypoint we do something 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;func&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;l&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;LRU&lt;/span&gt;) &lt;span style="color:#a6e22e"&gt;Add&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;k&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;v&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;interface&lt;/span&gt;{}) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Lock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;defer&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Unlock&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;l&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;lazyInit&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Et voilà, we’re safe for concurrent use. But let’s see what the benchmarks tell us.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_new-4 2000000 819 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAdd/mostly_existing-4 10000000 200 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_found-4 10000000 277 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGet/mostly_not_found-4 10000000 219 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_found-4 5000000 325 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemove/mostly_not_found-4 10000000 220 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Overall the code is slightly slower due to the (for now minimal) overhead of the mutex operations. However, although we are safe for concurrent use, we’re not really taking advantage of it, the entire code is completely sequential. An &lt;code&gt;Add&lt;/code&gt; will prevent anyone from getting an item. Even a call to &lt;code&gt;Len()&lt;/code&gt; locks everyone out.&lt;/p&gt;
&lt;p&gt;This will be clear if we modify the tests to run in parallel.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAddParallel/mostly_new-4 1000000 1040 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkAddParallel/mostly_existing-4 5000000 433 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGetParallel/mostly_found-4 5000000 293 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkGetParallel/mostly_not_found-4 10000000 289 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemoveParallel/mostly_found-4 5000000 305 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BenchmarkRemoveParallel/mostly_not_found-4 10000000 291 ns/op
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;On average, the calls will take longer because most of the times they’ll have to wait for another operation to finish and free the mutex.&lt;/p&gt;
&lt;p&gt;In the next post, we’ll try and cut on the locking considerably.&lt;/p&gt;</description></item><item><title>How to use FileServer with Gorilla’s Subrouter</title><link>https://rselbach.com/how-to-use-fileserver-with-gorillas-subrouter/</link><pubDate>Wed, 29 Nov 2017 16:57:06 +0000</pubDate><guid>https://rselbach.com/how-to-use-fileserver-with-gorillas-subrouter/</guid><description>&lt;p&gt;I’ve just spent much more time than I ever wanted to get this right, so here’s how I did it for future reference.&lt;/p&gt;
&lt;p&gt;I have a function that returns an &lt;code&gt;http.Handler&lt;/code&gt;, kind of like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func Handler(prefix string) http.Handler {
 r := mux.NewRouter().PathPrefix(prefix).Subrouter()
 r.HandleFunc(&amp;quot;/foo/&amp;quot;, fooHandler).Methods(&amp;quot;GET&amp;quot;)
 r.HandleFunc(&amp;quot;/bar/&amp;quot;, barHandler).Methods(&amp;quot;GET&amp;quot;)
 return r
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;prefix&lt;/code&gt; could be something like “/api/” or “/ui” or whatever, so this &lt;code&gt;http.Handler&lt;/code&gt; will serve &lt;code&gt;/api/foo&lt;/code&gt; and &lt;code&gt;/api/bar&lt;/code&gt;, for example. So far so good.&lt;/p&gt;
&lt;p&gt;Now, I also want to serve some static files under the prefix’s “root” (again, something like “/api/”.) My initial thinking was something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;r.Handle(&amp;quot;/&amp;quot;, http.StripPrefix(prefix, http.Dir(&amp;quot;./somedir&amp;quot;)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It works fine for anything under the root of “./somedir” but it failed with a 404 error for anything under different subdirectories. I found some answers online, but they never seemed to use a subrouter and thus didn’t work for me at all.&lt;/p&gt;
&lt;p&gt;I finally figured it out:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;r.PathPrefix(&amp;quot;/&amp;quot;).Handler(http.StripPrefix(prefix, http.FileServer(http.Dir(&amp;quot;./somedir&amp;quot;))))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It wasn’t obvious to me at all.&lt;/p&gt;</description></item><item><title>SciTech links (November 4 2017)</title><link>https://rselbach.com/scitech-links-november-4-2017/</link><pubDate>Sat, 04 Nov 2017 19:34:16 +0000</pubDate><guid>https://rselbach.com/scitech-links-november-4-2017/</guid><description>&lt;p&gt;China wants &lt;a href="http://en.people.cn/n3/2017/1102/c90000-9288036.html"&gt;to be the first country to build a practical space-based solar power station&lt;/a&gt;. Space-based solar power would presumably be much more sustainable and clean than fossil fuels and more efficient than the current sustainable energy sources we have on Earth. There are many problems still to be solved before these power plants can exist so China’s expectations are more wishful thinking than based on reality.&lt;/p&gt;
&lt;p&gt;Astronomers have found &lt;a href="https://arxiv.org/abs/1710.11099"&gt;a massive gas-giant that is astonishingly 23.9% as massive as the star it orbits&lt;/a&gt;. For comparison, Jupiter is the largest planet in our solar system but its mass is only 0.09% of that of the Sun.&lt;/p&gt;
&lt;p&gt;An &lt;a href="http://rapidcityjournal.com/news/local/communities/belle_fourche/fifty-three-year-old-nuclear-missile-accident-revealed/article_653347a4-04e8-5c29-a778-3a6a6c3cfd84.html"&gt;interesting article&lt;/a&gt; about an accident involving a Minuteman I nuclear missile 53 years ago. A short-circuit caused one of the retrorockets to fire and as a result the cone containing the nuclear warhead detached and fell into the silo. Ultimately the warhead casing was intact and nothing more serious happened, but the story reminded me of &lt;a href="https://en.wikipedia.org/wiki/1980_Damascus_Titan_missile_explosion"&gt;the Damascus Incident&lt;/a&gt; in which a fuel leak on the Titan caused a &lt;em&gt;major&lt;/em&gt; accident. Eric Schlosser’s book, &lt;a href="https://www.amazon.ca/Command-Control-Damascus-Accident-Illusion/dp/1594202273/ref=sr_1_1/131-2349598-2291116?s=books&amp;amp;ie=UTF8&amp;amp;qid=1509731190&amp;amp;sr=1-1"&gt;Command and Control: Nuclear Weapons, the Damascus Accident, and the Illusion of Safety&lt;/a&gt;, is a fantastic tale of this and other “broken arrow” incidents.&lt;/p&gt;</description></item><item><title>Science and tech links (October 21, 2017)</title><link>https://rselbach.com/science-and-tech-links-october-21-2017/</link><pubDate>Sat, 21 Oct 2017 12:11:54 +0000</pubDate><guid>https://rselbach.com/science-and-tech-links-october-21-2017/</guid><description>&lt;p&gt;Rodney Brooks writes about the &lt;a href="https://www.technologyreview.com/s/609048/the-seven-deadly-sins-of-ai-predictions/"&gt;7 Deadly Sins of AI Predictions&lt;/a&gt;. The text articulates a lot of my personal doubts about much of what I see on the news regarding AI. Some people seemed to read this article as a rebutal of AI itself, which I find puzzling as I did not read that at all. If anything, Brooks seems to believe AI will be much bigger than we can possible imagine. He does talk about &lt;em&gt;predictions&lt;/em&gt; being flawed. I can’t argue with that.&lt;/p&gt;
&lt;p&gt;The Canadian Space Agency is worried about Canada’s vulnerability to solar flares and the like. As a first step, it has published &lt;a href="https://buyandsell.gc.ca/cds/public/2017/09/05/58a932796831d9786a1d4e91a5efa27d/rfp_9f045-17-0348.pdf"&gt;a request for proposals to run studies on the effects of space weather events on the Canadian infrastructure&lt;/a&gt; (PDF). The study won’t focus only on cataclysmic scenarios but also at more-routine effects of space weather, like solar wind, flares that affect radio frequencies, radiation storms that send particles towards us and geomagnetic storms that can disrupt satellite systems and other technologies. If you know me, you’ll know a solar flare frying the electrical grid is my nightmare scenario.&lt;/p&gt;
&lt;p&gt;After six years in service, the first Chinese space station, Tiangong 1,&lt;br&gt;
&lt;a href="https://www.theguardian.com/science/2017/oct/13/tiangong-1-chinese-space-station-will-crash-to-earth-within-months"&gt;is set to fall back to earth sometime in the next few months.&lt;/a&gt; Still not clear exactly when, it should happen late this year or early 2018. I’d love to get my hand at a piece of that. As a space exploration geek, I find it incredibly sad that the Chinese space program is relatively secret. I’d love to know more.&lt;/p&gt;</description></item><item><title>Caddy and the Importance of Licenses</title><link>https://rselbach.com/caddy-and-the-importance-of-licenses/</link><pubDate>Sat, 16 Sep 2017 13:27:57 +0000</pubDate><guid>https://rselbach.com/caddy-and-the-importance-of-licenses/</guid><description>&lt;p&gt;I haven’t commented on the recent brouhaha caused by &lt;a href="https://caddyserver.com"&gt;Caddy&lt;/a&gt;‘s &lt;a href="https://caddyserver.com/blog/accouncing-caddy-commercial-licenses"&gt;decision to offer commercial licences&lt;/a&gt;, so I’ll do it briefly here before moving to the important part.&lt;/p&gt;
&lt;p&gt;I am fine with it. I don’t &lt;em&gt;love&lt;/em&gt; it, but it’s fine. The Caddy authors have every right to try to profit from their work. Best of luck to them, they deserve it. Do I think they mangled the announcement? Yes. Do I think the amount of vitriol out there was justified? No. But again, it’s fine.&lt;/p&gt;
&lt;p&gt;But I want to talk about something else and I’ll use this episode to illustrate it. Matt Holt published his thoughts on the experience in &lt;a href="https://caddy.community/t/the-realities-of-being-a-foss-maintainer/2728"&gt;The Realities of Being a FOSS Maintainer&lt;/a&gt;. It’s a nice read, but there is something there that I think we should not overlook.&lt;/p&gt;
&lt;p&gt;Midway through Matt’s post, he clarifies the situation with their build server, that they removed[note]Most likely made private[/note] from Github.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;To clarify, the Caddy build server was once open source, but we closed it up in the interest of focusing the technical attention of our community and our limited development resources (mostly time) on Caddy itself. The build server is not generalizable, and only exists to serve the Caddy project. As such, we’re taking it under our wings to develop and maintain it as needed. If you find some old source code still online, be aware that no license file was added to the code, and we have not granted others any license to use it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This highlights the importance of checking the license of “FOSS” software. Being open source means something. It doesn’t just mean “hosted on Github.” Just because you find a piece of code on Github, it doesn’t mean you can freely use it. It sucks, but as the above paragraph shows, it matters.&lt;/p&gt;
&lt;p&gt;What Matt is saying here is that although his build server was open source, it no longer is and if you have the code, you were never granted any license to use it. This cannot be, of course. Either it was never open source to begin with, or you were granted a license to use it. Which one is it?&lt;/p&gt;
&lt;p&gt;Since Matt makes it clear that “no license file was added to the code,” that means it’s the former: it was never open source, no matter what he says now. Whether intentionally or not, people were misled into thinking it was.&lt;/p&gt;
&lt;p&gt;People would find the code on Github and assume it was open source. That’s why checking the license is important. A project without a license is &lt;em&gt;not&lt;/em&gt; open source and you are at risk.&lt;/p&gt;
&lt;p&gt;I’ve seen small projects on Github before with no license information at all. It always made me uncomfortable. Now I see I was right.&lt;/p&gt;
&lt;p&gt;I want to make clear this is not about Caddy or Matt. Again, I’m fine with their decision. My points are general:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Properly license your open source software.&lt;/li&gt;
&lt;li&gt;Check the license of software you use.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don’t, this will get back to bite you.&lt;/p&gt;</description></item><item><title>Returns in Go and C#</title><link>https://rselbach.com/returns-in-go-and-c/</link><pubDate>Fri, 15 Sep 2017 15:55:40 +0000</pubDate><guid>https://rselbach.com/returns-in-go-and-c/</guid><description>&lt;p&gt;Whenever someone posts anything related to the Go programming language on Hacker News, it never takes long before someone complains about error handling. I find it interesting because it is &lt;em&gt;exactly&lt;/em&gt; one of things I like the most about Go.&lt;/p&gt;
&lt;p&gt;I don’t want to &lt;em&gt;specifically&lt;/em&gt; talk about error handling though. I want to talk about a feature that is intrinsically tied to it in Go: the ability of functions to return multiple values&lt;/p&gt;
&lt;p&gt;For instance, in Go it is common and idiomatic to write functions like this —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func Divide(a, b float64) (float64, error) {
 if b == 0 {
 return 0.0, errors.New(&amp;quot;divide by zero&amp;quot;)
 }
 return a / b, nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So the caller would do:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;result, err := Divide(x, y)
if err != nil {
 // do error handling...
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Some people deplore this. I absolutely love it. I find it so much clearer than, for instance, what we often have to do in C#. You see, C# didn’t have multiple returns (until very recently; see below) so you ended up with a few options.&lt;/p&gt;
&lt;p&gt;First, you can simple throw exceptions.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public SomeObject GetObjectById(int id) {
 if (!SomeObjectRepo.Has(id))
 throw new ArgumentOutOfRangeException(nameof(id));
 // ...
}
...
try
{
 var obj = GetObjectById(1);
 // do something with obj
}
catch (ArgumentOutOfRangeException ex)
{
 // error handling
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I find the flow difficult to read. Particularly because variables are scoped within the try-catch so often you need to first declare something above the &lt;code&gt;try&lt;/code&gt; and then test it after the &lt;code&gt;catch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A second option is to return &lt;code&gt;null&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public SomeObject GetObjectById(int id)
{
 if (!SomeObjectRepo.Has(id))
 return null;

 // go get the object
}
...
var obj = GetObjectById(1);
if (obj == null) 
{
 // do error handling
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This looks closer to what I like but it still has some serious downsides. You don’t get any error information. What made it fail? I don’t know. As well, this doesn’t work for non-nullable types. A method returning a, say, &lt;code&gt;int&lt;/code&gt; cannot return &lt;code&gt;null&lt;/code&gt;. Sure, you could return &lt;code&gt;int?&lt;/code&gt; instead of &lt;code&gt;int&lt;/code&gt; and then test for &lt;code&gt;.HasValue&lt;/code&gt; but that’s cumbersome and artificial.&lt;/p&gt;
&lt;p&gt;A third option is the use of a generic return type. Something like —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Result&amp;lt;T&amp;gt;
{
 public T Value {get;protected set;}
 public Exception Exception {get; protected set;}

 public bool IsError =&amp;gt; Exception != null;

 public Result() : this(default(T)) {}
 public Result(T value)
 {
 Value = value;
 }

 public static Result&amp;lt;T&amp;gt; MakeError(Exception exception)
 {
 return new Result&amp;lt;T&amp;gt;
 {
 Value = default(T),
 Exception = exception
 };
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could then use this to return values like —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Result&amp;lt;int&amp;gt; Divide(int a, int b)
{
 if (b == 0)
 {
 return Result&amp;lt;int&amp;gt;.MakeError(new DivideByZeroException());
 }

 return new Result&amp;lt;int&amp;gt;(a / b);
}
...
var res = Divide(8, 4);
if (res.IsError)
{
 // do error handling, e.g.
 throw res.Exception;
}
// do something with res.Value (2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works, but it looks &lt;em&gt;artificial&lt;/em&gt;. You need to create instances of &lt;code&gt;Result&amp;lt;T&amp;gt;&lt;/code&gt; all around all the time. It is not that bad if your codebase uses this throughout and it becomes automatic for all programmers envolved. When it’s an exception to the rule, it is horrible.&lt;/p&gt;
&lt;p&gt;A very similar solution is to return something like &lt;code&gt;Tuple&amp;lt;T1, T2, ...&amp;gt;&lt;/code&gt; —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Tuple&amp;lt;int,Exception&amp;gt; Divide(int a, int b)
{
 if (b == 0)
 return new Tuple&amp;lt;int,Exception&amp;gt;(0, new DivideByZeroException());
 return new Tuple&amp;lt;int,Exception&amp;gt;(a/b, null);
}
...
var res = Divide(1, 2);
if (res.Item2 != null) // Item2 is the exception
{
 // do error handling
}
// do something with res.Item1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same principle. It’s ugly and artificial, but it will come back to us.&lt;/p&gt;
&lt;p&gt;The way the C# authors found to work around this problem is the idiomatic try-pattern, which consists in creating non-exception-throwing versions of methods. For example, if we go back to the first C# example above (&lt;code&gt;GetObjectById()&lt;/code&gt;), we could create a second method like so —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public bool TryGetObjectById(int id, out SomeObject result) {
 try 
 {
 result = GetObjectById(id);
 return true;
 }
 catch
 {
 result = default(SomeObject);
 return false;
 }
}
...
SomeObject result;
if (!TryGetObjectById(1, out result))
{
 // do error handling
}
// do something with result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that ever since C# 7.0 you can declare the &lt;code&gt;out&lt;/code&gt; variable directly inside the method call as such —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!TryGetObjectById(1, out var result))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which spares you of declaring your &lt;code&gt;out&lt;/code&gt; variables arguably at the expense of clarity.&lt;/p&gt;
&lt;p&gt;This method is idiomatic and found everywhere in the .NET Framework. I actually like it but it still has the problem of losing important information, namely what caused the method to fail: all you get is &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In C# 7.0, the language authors came up with a new solution: they added syntactic sugar to the language to make the tuple solution a bit more appealing —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public (int, Exception) Divide(int a, int b)
{
 if (b == 0)
 return (0, new DivideByZeroException());

 return (a / b, null);
}
...
var (res, err) = Divide(1, 2);
if (err != null) 
{
 // do error handling
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Suddenly this becomes very familiar to a Go programmer. In the background, this is using a tuple. In fact, you can check that this is so by using the method above like this —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var res = Divide(1, 2);
if (res.Item2 != null)
 // do error handling
// use res.Item1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will see that &lt;code&gt;res&lt;/code&gt; is of type &lt;code&gt;System.ValueTuple&lt;/code&gt;. Also, if you create a library in C# 7.0 and then try to use it with a program in older versions of C#, you will see that the exposed type of the method is a tuple. This is actually nice because it means this big language change is backwards compatible.&lt;/p&gt;
&lt;p&gt;All that said, I haven’t seen many uses of the new tuple returns in C# code in the wild. Maybe it’s just early (C# 7.0 has been out for only a few months.) Or maybe the try-pattern is simply way too ingrained in the way of doing things in C#. It’s more idiomatic.&lt;/p&gt;
&lt;p&gt;I sure prefer the new (Go-like) way.&lt;/p&gt;</description></item><item><title>Happy Birthday Canada</title><link>https://rselbach.com/happy-birthday-canada/</link><pubDate>Sat, 01 Jul 2017 18:30:48 +0000</pubDate><guid>https://rselbach.com/happy-birthday-canada/</guid><description>&lt;p&gt;My adopted country is celebrating its 150th anniversary today. I want to say that I feel privileged to be here for it. This is where my family and I decided to make our home.&lt;/p&gt;
&lt;p&gt;Happy birthday, bonne fête, Canada! Merci de tout! 🇨🇦&lt;/p&gt;</description></item><item><title>SciTech Links (June 5, 2017)</title><link>https://rselbach.com/scitech-links-june-5-2017/</link><pubDate>Mon, 05 Jun 2017 17:53:15 +0000</pubDate><guid>https://rselbach.com/scitech-links-june-5-2017/</guid><description>&lt;ul&gt;
&lt;li&gt;The Institute for Energy Economics and Financial Analysis reports that &lt;a href="http://ieefa.org/ieefa-asia-indias-electricity-sector-transformation-happening-now/"&gt;India is cancelling plans for huge coal power stations because solar energy prices are lower&lt;/a&gt;, which, if true, represents a huge threshold for sustainable energy productions. If solar energy is cheaper than coal (and other types of energy), that means the market itself will naturally migrate over.&lt;/li&gt;
&lt;li&gt;A company is &lt;a href="https://www.newscientist.com/article/2133311-human-tests-suggest-young-blood-cuts-cancer-and-alzheimers-risk/"&gt;suggesting that young blood cuts cancer and Alzheimer’s risk&lt;/a&gt;, which seems like a huge deal. That said, a healthy dose of skepticism is warranted. For example, the company’s trial did not have a placebo test group.&lt;/li&gt;
&lt;li&gt;A new paper describes a test in which they were able &lt;a href="https://www.cell.com/neuron/fulltext/S0896-6273(15)00090-2"&gt;to restore damaged brain cells&lt;/a&gt; in mice using stem cells.&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Casting objects and boxing</title><link>https://rselbach.com/casting-objects-and-boxing/</link><pubDate>Thu, 30 Mar 2017 19:13:02 +0000</pubDate><guid>https://rselbach.com/casting-objects-and-boxing/</guid><description>&lt;p&gt;I’m back from a trip to a customer.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;How was it?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Okay. I got more snow that I expected on the way there, so the drive wasn’t much fun. Then again, a part of the trip goes through a beautiful forest that was worth everything else.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Cool!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Also, while showing the customer a new feature, the app crashed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Typical. Blame it on Murphy!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That’s what I did at first. Then I blamed it on the developer. And then I finally went looking at the C# code to find out why it happened.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What was it?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It turned out to be a rather common but not obvious mistake. See the code below and tell me what is the value of each of &lt;code&gt;doubleF&lt;/code&gt;, &lt;code&gt;doubleI&lt;/code&gt;, and &lt;code&gt;doubleO&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;float f = 0.0;
int i = (int)f;
object o = f;

var doubleF = (double) f;
var doubleI = (double) i;
var doubleO = (double) o;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;I’m sensing a catch here, but I’ll bite. They’re all cast from the same original variable &lt;code&gt;f&lt;/code&gt; so I’m guessing they’d all end up 0.0…?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You would, wouldn’t you? But you’re wrong.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Waaat?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The final line in that code will throw an &lt;code&gt;InvalidCastException&lt;/code&gt; at you — and crash your app if you don’t catch it, as was the case in our app.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Wait what? How? How come you can’t cast 0.0 to &lt;code&gt;double&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Well, you can. For instance, this works perfectly —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var d = (double) 0.0f;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this doesn’t —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object f = 0.0f;
var d = (double) d;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;It makes no sense!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Actually it does. The problem is taking &lt;code&gt;object&lt;/code&gt; to mean “anything.” Which incidentally it does, just not the way most people think. You see, &lt;code&gt;object&lt;/code&gt; is a type representing &lt;code&gt;Object&lt;/code&gt;, which is a class other types inherit from but not all. You can store anything as &lt;code&gt;object&lt;/code&gt; because &lt;code&gt;Object&lt;/code&gt; &lt;em&gt;boxes&lt;/em&gt; whatever object you put in it. It stores the value internally but the compiler doesn’t know what type is stored there.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;No no no! I know for a fact that you can too check what type is stored in an object&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You’re right, you can. For instance —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object o = /* something */
Console.WriteLine(o.GetType());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print the type of whatever you put in the variable &lt;code&gt;o&lt;/code&gt;. But this is at run time: &lt;strong&gt;the compiler doesn’t know&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That’s why we using casting. If we &lt;strong&gt;know for a fact&lt;/strong&gt; that variable &lt;code&gt;o&lt;/code&gt; will contain a, say, &lt;code&gt;int&lt;/code&gt;, we can help the compiler and tell it about it with a cast. Remember, &lt;strong&gt;when you cast something, you are telling the &lt;em&gt;compiler&lt;/em&gt; what type will be stored in the variable&lt;/strong&gt;. The compiler can’t be held responsible if you lie to it.&lt;/p&gt;
&lt;p&gt;Let’s get back at the original problem —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object o = 1.0f;
var d = (double) o;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You &lt;em&gt;told&lt;/em&gt; the compiler that &lt;code&gt;o&lt;/code&gt; will be a &lt;code&gt;double&lt;/code&gt;, but it isn’t. Remember a &lt;code&gt;double&lt;/code&gt; is a shorthand for the struct &lt;code&gt;Double&lt;/code&gt; as &lt;code&gt;float&lt;/code&gt; is for &lt;code&gt;Single&lt;/code&gt;. And guess what? A &lt;code&gt;Double&lt;/code&gt; is not a &lt;code&gt;Single&lt;/code&gt;. When you stored a &lt;code&gt;float&lt;/code&gt; in the variable &lt;code&gt;o&lt;/code&gt; of type &lt;code&gt;object&lt;/code&gt;, the &lt;code&gt;float&lt;/code&gt; value was boxed inside an object of type &lt;code&gt;Object&lt;/code&gt;. When you cast, the compiler has to unbox whatever was inside &lt;code&gt;o&lt;/code&gt; and guess what, the value stored in &lt;code&gt;o&lt;/code&gt; is of a different structure, with different methods and storage, than what you told it it was. You could &lt;em&gt;convert&lt;/em&gt; between them, but &lt;em&gt;they are not the same&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So the compiler expects an object of type &lt;code&gt;Double&lt;/code&gt; but it has a &lt;code&gt;Single&lt;/code&gt; and things fail miserably.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But you just said that we can convert between them! Why don’t the compiler does it?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It could. But think of how this would work out in real life. Remember the compiler doesn’t know what will be inside &lt;code&gt;o&lt;/code&gt; so it needs to test what the value is. It would need to test if the type is a, say, &lt;code&gt;string&lt;/code&gt;. If it is, then convert &lt;code&gt;string&lt;/code&gt; to &lt;code&gt;Double&lt;/code&gt;. If it isn’t then check if it is a &lt;code&gt;Int32&lt;/code&gt;. Then a &lt;code&gt;Int64&lt;/code&gt;. Then a &lt;code&gt;DateTime&lt;/code&gt;. The number of possibilities is enormous and the compiler would have to generate all this code &lt;em&gt;every time it needs it finds a cast&lt;/em&gt;. This would be a &lt;em&gt;lot&lt;/em&gt; of code. It would be so much code in fact that you’d be mad not to put it all in separate methods. It would also be slow so the compiler won’t do this by default.&lt;/p&gt;
&lt;p&gt;That’s why we have the &lt;code&gt;Convert&lt;/code&gt; class, which in turn depends on types implementing the &lt;code&gt;IConvertible&lt;/code&gt; interface. Whenever you want to &lt;em&gt;convert&lt;/em&gt; a value of TypeA to TypeB, you can use this conversion methods. You can do —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;object o = 1.0f;
var d = Convert.ToDouble(o);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler authors had to make a decision: either they’d generate lots of slow code to test for the type and convert the value, or they’d leave the decision for the programmer who can call &lt;code&gt;Convert.ToSomething&lt;/code&gt; when needed.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;And they chose the former.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Exactly. I believe it was reasonable. If you &lt;em&gt;know&lt;/em&gt; something will be of a given type at run time, you can still cast it. Otherwise, you should convert it.&lt;/p&gt;</description></item><item><title>Retreating back into my bubble</title><link>https://rselbach.com/retreating-back-into-my-bubble/</link><pubDate>Tue, 31 Jan 2017 18:31:32 +0000</pubDate><guid>https://rselbach.com/retreating-back-into-my-bubble/</guid><description>&lt;p&gt;A few months back I decided to try and burst out of my bubble. I then decided to follow some public figures from all sides on Facebook and Twitter. On Facebook this is particularly weird because you’re forced to &lt;em&gt;like&lt;/em&gt; the page. So it tells the world &lt;em&gt;“Roberto likes Mrs. Public-Figure”&lt;/em&gt;, which is sometimes undesirable.&lt;/p&gt;
&lt;p&gt;Still, I wanted to see what both sides of the political spectrum were saying. Also, I consider myself a centrist so I expected to agree with everyone on at least something.&lt;/p&gt;
&lt;p&gt;Anyway, my town suffered a terrorist attack a couple of days ago and as soon as the identity of the suspect became known, the media started drawing conclusions based on who he “liked” on Facebook. That got me thinking: someone will eventually go through my social media and conclude I believe in &lt;em&gt;X&lt;/em&gt; because I “like” &lt;em&gt;Y&lt;/em&gt; on FB, even though I may only “like” &lt;em&gt;Y&lt;/em&gt; because I want to be informed and not because I necessarily agree with them.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.independent.co.uk/news/world/americas/donald-trump-muslim-immigration-ban-facebook-check-iraq-sudan-syria-mana-yegani-a7551256.html"&gt;There are also reports&lt;/a&gt; that US Border agents now require people to provide social network credentials so that their political leanings can be attested. Regardless on my personal opinions about this, the fact is that someone with access to my FB account can quickly draw the conclusion that I lean this or that way because of the pages I like, even though I liked them only to be informed.&lt;/p&gt;
&lt;p&gt;I then realized that it’s time to give up on my bubble-bursting experiment. I “unliked” pretty much every public figure on Facebook.&lt;/p&gt;</description></item><item><title>Cedilha no Fedora 25</title><link>https://rselbach.com/cedilha-no-fedora-25/</link><pubDate>Tue, 06 Dec 2016 19:38:20 +0000</pubDate><guid>https://rselbach.com/cedilha-no-fedora-25/</guid><description>&lt;p&gt;Quem utiliza teclado US Internacional para escrever no Linux já deve ter dado de cara com o fato de que na maioria das distribuições, a combinação ‘+c gera um “ć” em vez de um “ç”. Resolver isso no Fedora 25 é fácil, mas não evidente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tl;dr&lt;/strong&gt; – eu criei &lt;a href="https://github.com/robteix/c-cedilla-fedora"&gt;este script&lt;/a&gt; que faz todos os passos abaixo automaticamente. Basta rodar isso:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;curl https://raw.githubusercontent.com/robteix/c-cedilla-fedora/master/c-cedilla-fedora | bash
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Se você preferir não executar o script, continue lendo.&lt;/p&gt;
&lt;p&gt;Primeiro, vamos criar um novo mapa de teclado para seu usuário. Rode o comando abaixo:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sed -e &lt;span style="color:#e6db74"&gt;&amp;#39;s,\xc4\x86,\xc3\x87,g&amp;#39;&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; -e &lt;span style="color:#e6db74"&gt;&amp;#39;s,\xc4\x87,\xc3\xa7,g&amp;#39;&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; &amp;lt; /usr/share/X11/locale/en_US.UTF-8/Compose &amp;gt; ~/.XCompose
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Isso copia o arquivo de mapeamento de teclas do Fedora para o diretório &lt;tt&gt;$HOME&lt;/tt&gt; do usuário, substituindo o “Ć” por um “Ç”.&lt;/p&gt;
&lt;p&gt;Agora vamos configurar o GNOME para que ele não controle a configuração do teclado, para que possamos usar nossa própria:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;gsettings set org.gnome.settings-daemon.plugins.keyboard active false
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Para selecionar o &lt;em&gt;input method&lt;/em&gt; apropriado, o Fedora fornece um programinha chamado &lt;tt&gt;im-chooser&lt;/tt&gt; que não é instalado por padrão. Para instalá-lo:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sudo dnf install im-chooser
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Por fim, executamos o &lt;tt&gt;im-chooser&lt;/tt&gt; e escolhemos “Use X Compose table”:&lt;/p&gt;
&lt;p&gt;Clique em “Log out” para aplicar as modificações e a partir de agora deve ser possível gerar o c-cedilha com a combinação ‘+c.&lt;/p&gt;</description></item><item><title>The Woman the Mercury Astronauts Couldn’t Do Without</title><link>https://rselbach.com/the-woman-the-mercury-astronauts-couldnt-do-without/</link><pubDate>Fri, 02 Dec 2016 13:31:17 +0000</pubDate><guid>https://rselbach.com/the-woman-the-mercury-astronauts-couldnt-do-without/</guid><description>&lt;p&gt;Must-read article on “human computer” Katherine Johnson. Not only was she a key figure in pushing NASA’s efforts forward, but she did it while fighting misogyny and racism:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Outside the gates, the caste rules were clear. Blacks and whites lived separately, ate separately, studied separately, socialized separately, worshipped separately, and, for the most part, worked separately. At Langley, the boundaries were fuzzier. Blacks were ghettoed into separate bathrooms, but they had also been given an unprecedented entrée into the professional world. Some of Goble’s colleagues were Yankees or foreigners who’d never so much as met a black person before arriving at Langley. Others were folks from the Deep South with calcified attitudes about racial mixing. It was all a part of the racial relations laboratory that was Langley, and it meant that both blacks and whites were treading new ground together. The vicious and easily identifiable demons that had haunted black Americans for three centuries were shape-shifting as segregation began to yield under pressure from social and legal forces. Sometimes the demons still presented themselves in the form of racism and blatant discrimination. Sometimes they took on the softer cast of ignorance or thoughtless prejudice. But these days, there was also a new culprit: the insecurity that plagued black people as they code-shifted through the unfamiliar language and customs of an integrated life.&lt;/p&gt;
&lt;p&gt;Katherine understood that the attitudes of the hard-line racists were beyond her control. Against ignorance, she and others like her mounted a day-in, day-out charm offensive: impeccably dressed, well-spoken, patriotic, and upright, they were racial synecdoches, keenly aware that the interactions that individual blacks had with whites could have implications for the entire black community. But the insecurities, those most insidious and stubborn of all the demons, were hers alone. They operated in the shadows of fear and suspicion, and they served at her command. They would entice her to see the engineer as an arrogant chauvinist and racist if she let them. They could taunt her into a self-doubting downward spiral, causing her to withdraw from the opportunity that Dr. Claytor had so meticulously prepared her for.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;em&gt;&lt;a href="http://nautil.us/issue/43/heroes/the-woman-the-mercury-astronauts-couldnt-do-without"&gt;The Woman the Mercury Astronauts Couldn’t Do Without&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>New stuff coming in C# 7.0</title><link>https://rselbach.com/new-stuff-coming-in-c-7-0/</link><pubDate>Tue, 30 Aug 2016 20:10:32 +0000</pubDate><guid>https://rselbach.com/new-stuff-coming-in-c-7-0/</guid><description>&lt;p&gt;Mads Torgersen wrote a blog post highlighting &lt;a href="https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/"&gt;what’s new in C# 7.0&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;C# 7.0 adds a number of new features and brings a focus on data consumption, code simplification and performance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The changes all seem to be in line with the recent trends of borrowing syntax sugar from other languages to C#. Nothing wrong with that: copy what’s good and shed what’s bad.&lt;/p&gt;
&lt;p&gt;One of the changes is related to &lt;code&gt;out&lt;/code&gt; variables. These are the C# way to deal with not being able to return multiple values (see below for good news on that front). It’s basically the same as passing by reference in, say, C. For instance:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int myOutVar;
changeMyOutVar(out myOutVar);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You could have the value of &lt;code&gt;myOutVar&lt;/code&gt; set inside &lt;code&gt;changeMyOutVar&lt;/code&gt;. Simple. What is changing in C# 7.0 is that you would no longer need to predeclare &lt;code&gt;myOutVar&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;changeMyOutVar(out int myOutVar);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scope of the new variable will be the enclosing block. I have to say it: I don’t like it. It &lt;em&gt;feels&lt;/em&gt; obfuscated to me. The variable doesn’t &lt;em&gt;look&lt;/em&gt; like it should be in that scope. Compare with this popular Go idiom:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if err := DoSomething(); err != nil {
 return err;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The variable &lt;code&gt;err&lt;/code&gt; is created &lt;em&gt;inside&lt;/em&gt; the &lt;code&gt;if&lt;/code&gt; and its scope is there as well. I know a lot of people who hate this for the same reason I don’t like the way the new &lt;code&gt;out&lt;/code&gt; variables are to be created in C#. I find it much more clear in Go though.&lt;/p&gt;
&lt;p&gt;The feature I &lt;em&gt;absolutely loved&lt;/em&gt; to read about is &lt;em&gt;tuples&lt;/em&gt;. Error handling in .NET is often done with exceptions, which I find clunky and cumbersome. With tuples, we might be able to move to something more Go-like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(User, bool) FindUser(string username) {
 var found = _userList.Find(u =&amp;gt; u.Username == username);
 if (found == null)
 return null, false;
 return found, true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So we could do something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var (user, ok) = FindUser(&amp;quot;someusername&amp;quot;);
if (!ok) {
 // user not found, deal with this
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check his post for more features.&lt;/p&gt;</description></item><item><title>Millie 0.9.6, or “installers are hard”</title><link>https://rselbach.com/millie-0-9-6-or-installers-are-hard/</link><pubDate>Sun, 28 Aug 2016 22:08:43 +0000</pubDate><guid>https://rselbach.com/millie-0-9-6-or-installers-are-hard/</guid><description>&lt;p&gt;I’ve been terribly busy with work lately and so I haven’t really had much time for my side projects. I did however managed to get a new version of Millie out of the door.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/robteix/millie/releases/tag/v0.9.6"&gt;Got get it here.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Changes are mostly infrastructural though.&lt;/p&gt;
&lt;h3 id="user-8220visible8221-changes"&gt;User “visible” changes:&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Add .deb installation files for Linux&lt;/li&gt;
&lt;li&gt;Add support for electron-builder&lt;/li&gt;
&lt;li&gt;New settings system&lt;/li&gt;
&lt;li&gt;Fix missing icon on Win64 installer&lt;/li&gt;
&lt;li&gt;Split generation of 32- and 64-bit installers on windows&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="minor-changes-and-fixes"&gt;Minor changes and fixes&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Merge branch ‘builder’ of github.com:robteix/millie&lt;/li&gt;
&lt;li&gt;Ignore backups&lt;/li&gt;
&lt;li&gt;Add missing dependency&lt;/li&gt;
&lt;li&gt;Ignore main.js.map&lt;/li&gt;
&lt;li&gt;Remove warning on unknown props&lt;/li&gt;
&lt;li&gt;Cleanup&lt;/li&gt;
&lt;li&gt;Move background declaration to MillieView&lt;/li&gt;
&lt;li&gt;Stop loading old settings&lt;/li&gt;
&lt;li&gt;Add script to generate release files&lt;/li&gt;
&lt;li&gt;Remove log file left from first commit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That said, this release reminded me of how hard installers are to get right. Actually, they’re hard to do &lt;em&gt;at all&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;MacOS installers can basically only be made on macOS due to there not seeming to be decent implementations of DMG anywhere else. You can compile and create a ZIP file with your binaries, sure, but who wants that? To create a nice DMG, you need to be on macOS.&lt;/p&gt;
&lt;p&gt;Windows is also hard to do in other systems. Not impossible, just annoying. You can get by with Mono in Linux or macOS but you need to get around a bug here and there. It works though.&lt;/p&gt;
&lt;p&gt;Linux installers (meaning .dpkg and .rpm) are easy to do in macOS but &lt;em&gt;barely doable&lt;/em&gt; on Windows.&lt;/p&gt;
&lt;p&gt;In practice you end up having to create each installer on its own environment. Linux and Windows are easy to do with VMs, but you’ll need a macOS box eventually.&lt;/p&gt;
&lt;p&gt;Windows has its own share of problems though. I hear they’re working on &lt;em&gt;finally&lt;/em&gt; fixing their dumb limitations on path length but it’s definitely not there yet. This is important when working with things like npm that create paths hundreds of levels deep. Trying to something simple like deleting a node_modules directory on Windows is an exercise in frustration.&lt;/p&gt;
&lt;p&gt;The installers for this release were all created in a Linux VM though. It was the closest thing I got to building it all in a single platform. No DMG for macOS though.&lt;/p&gt;</description></item><item><title>I’m sad today</title><link>https://rselbach.com/im-sad-today/</link><pubDate>Mon, 01 Aug 2016 13:25:21 +0000</pubDate><guid>https://rselbach.com/im-sad-today/</guid><description>&lt;p&gt;So this is me today.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/im-sad-today/640px-Sad-pug_hu_4636eb3abaf53eb1.webp 320w, https://rselbach.com/im-sad-today/640px-Sad-pug_hu_672248251be25151.webp 480w, https://rselbach.com/im-sad-today/640px-Sad-pug_hu_760c1e2e8e75362a.webp 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/im-sad-today/640px-Sad-pug.jpg"
 alt="640px-Sad-pug"
 loading="lazy"
 decoding="async" width="640" height="427" srcset="https://rselbach.com/im-sad-today/640px-Sad-pug_hu_b64054d3376160e1.jpg 320w, https://rselbach.com/im-sad-today/640px-Sad-pug_hu_43a6d8305972048.jpg 480w, https://rselbach.com/im-sad-today/640px-Sad-pug_hu_4abf7097cab4afb8.jpg 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Years ago, my wife and I had a friends couple we worked with for some years. Someday they stopped talking to us. They refused to answer our calls or answer our emails. They never accepted out friend requests on Facebook.&lt;/p&gt;
&lt;p&gt;We never knew why.&lt;/p&gt;
&lt;p&gt;This happened over 10 years ago so naturally life moved on. From time to time, it happens that I see something from them on Facebook seen as we have a lot of friends in common, but neither of us acknowledges the present of the other.&lt;/p&gt;
&lt;p&gt;I recently joined a Slack team and to my (and I’m sure his) surprise, my former-friend was there as well. Over the course of the next few weeks, we’d be talking in the same channel to other people but again, never directly to each other. I wanted to but I knew he was mad at me for something and I didn’t want to force it. What I did try doing was to engage in the channel as if nothing ever happened, just like everyone else.&lt;/p&gt;
&lt;p&gt;But it was unconfortable. There was always that elephant in the room. So last Thursday I send him a message in private. I told him I never knew what I had done but that I apologized for whatever it was. I told him that he didn’t have to forgive me but I would really appreciate knowing what it was.&lt;/p&gt;
&lt;p&gt;But he wouldn’t answer. So today I decided that there was no point in making two people uncomfortable all the time and I left the channel.&lt;/p&gt;
&lt;p&gt;In the end, it’s no big deal. As I said, this was something that happened (whatever it was) over 10 years ago. But it makes me sad that I’ll never know what I did wrong.&lt;/p&gt;
&lt;p&gt;(Image &lt;a href="https://creativecommons.org/licenses/by-sa/3.0/deed.en"&gt;CC-BY-SA&lt;/a&gt; by &lt;a href="https://commons.wikimedia.org/wiki/File:Sad-pug.jpg"&gt;Wikimedia Commons&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>Une lettre ouverte à la madame qui a pointé son doigt vers ma fille</title><link>https://rselbach.com/une-lettre-ouverte-a-la-madame-qui-a-pointe-son-doigt-vers-ma-fille/</link><pubDate>Wed, 27 Jul 2016 15:36:32 +0000</pubDate><guid>https://rselbach.com/une-lettre-ouverte-a-la-madame-qui-a-pointe-son-doigt-vers-ma-fille/</guid><description>&lt;p&gt;Conscient du fait que mon français n’est pas très bon (j’apprends), je pense cependant qu’il est important de l’écrire dans cette langue, car vous m’avez fait savoir que toute autre langue n’est pas acceptable.&lt;/p&gt;
&lt;p&gt;Il y a quelques jours, j’étais chez un restaurant Valentine avec ma fille de six ans. Nous y étions allés parce qu’elle adore leur hot-dog et puisqu’elle avait fait un super effort à l’école ce jour-là, j’avais décidé de la récompenser. Vos raisons pour y être doivent être encore plus mondaines que ça.&lt;/p&gt;
&lt;p&gt;Nous sommes arrivés au Québec depuis moins de trois mois et ma fille est toujours en train d’apprendre le français. Et bien qu’elle s’efforce de se faire comprendre par un monde qui ne la comprend pas, elle a encore des difficultés. Elle devient souvent frustrée. Et pourtant elle persévère. Sachez que je suis très fier d’elle.&lt;/p&gt;
&lt;p&gt;Elle venait d’entrer à l’école avant les vacances d’été, juste pour apprendre le français. Un jour, les élèves devraient avoir un examen de mathématique et les enseignantes avaient décidé de sortir ma fille de la salle pour continuer à étudier le français. Ma fille a commencé à pleurer et supplier. Les enseignantes ne comprenaient pas pourquoi. Ils m’ont alors appelé et je suis allé à l’école. En y arrivant, nous avons finalement compris : elle se croyait punie et elle ne savait pas ce qu’elle avait fait pour le mériter. Elle implorait le pardon. C’est de ce genre de situation à laquelle elle doit faire face tous les jours.&lt;/p&gt;
&lt;p&gt;Encore une fois, elle a six ans.&lt;/p&gt;
&lt;p&gt;Elle a perdu toutes ses amies. Elle a dû laisser presque tous ces jouets. En tant que père, ça me brise le cœur de la voir en regardant des autres enfants qui jouent, parce que je sais qu’elle veut jouer avec eux, mais elle est souvent gênée à cause du français.&lt;/p&gt;
&lt;p&gt;Je suis certain que vous serez d’accord avec moi quand je dis que ça n’est pas facile pour un adulte, et encore moins pour une petite fille. Mais elle fait un vrai effort là et je suis tellement fier d’elle. Vraiment fier.&lt;br&gt;
Et c’est pour cette raison qu’il m’a fait tellement mal quand vous avez décidé de lui réprimander de ne pas parler français avec son papa.&lt;/p&gt;
&lt;p&gt;Elle jacassait comme d’habitude quand vous l’avez interrompue pour me dire qu’il fallait parler français au Québec. Ça m’a surpris un peu, mais j’ai commencé quand même à vous expliquer qu’elle apprenait. Vous avez décidé de m’ignorer et de pointer votre doigt à une petite fille et de crier « en français ! »&lt;/p&gt;
&lt;p&gt;Je suis un nouvel arrivant et je n’avais aucune idée, à ce moment-là, comment réagir. Je ne savais pas si vous auriez l’appui du reste des gens chez le restaurant ou pas. Mon instinct était juste de protéger ma fille de vous. Ma petite fille qui se protégeait derrière moi, intimidée par une madame qui partait en colère pour quelque chose qu’elle ne comprenait pas.&lt;/p&gt;
&lt;p&gt;Et pourquoi ?&lt;/p&gt;
&lt;p&gt;Je vous demande, madame, c’est à quoi exactement que vous vous attendiez ? Je comprends que vous considérez votre langue importante. En général, j’appuie l’idée que tout le monde doit être capable de se communiquer en français au Québec. Je pense que c’est absolument juste que personne ne vous demande de parler l’anglais ou d’autre langue, quelle qu’elle soit. Vous êtes au Québec et le français devrait être suffisant pour y vivre. Je suis d’accord avec tout ça.&lt;/p&gt;
&lt;p&gt;Mais si ma fille veut parler à son papa en sa langue maternelle, qu’est-ce que vous avez à voir avec ça ?&lt;br&gt;
Essayez de vous mettre dans notre place. Essayez de vous imaginer à un autre pays. Est-ce que vous arrêtiez de parler français avec vos enfants ou votre conjoint ? Soyez honnête.&lt;/p&gt;
&lt;p&gt;Je ne sais vraiment pas ce que vous pensiez à réussir, madame, mais ce que vous avez réussi à faire c’est de me faire repenser le Québec et les québécois. Vous m’avez fait me demander si les québécois sont tous des colons. C’est ce que vous avez réussi, madame.&lt;/p&gt;
&lt;p&gt;Mais même là, vous avez échoué.&lt;/p&gt;
&lt;p&gt;Lorsque vous êtes partie, des gens chez le resto sont venus s’excuser de vous. Une femme a donné à ma fille des crayons et une feuille de papier pour qu’elle puisse dessiner afin d’arrêter de pleurer.&lt;/p&gt;
&lt;p&gt;J’ai donc conclu que non, vous ne représentez pas le Québec. La plupart des québécois avec qui j’ai eu le plaisir d’interagir m’ont traité avec respect. La plupart des québécois avec qui j’en ai parlé m’ont dit la même chose : vous, et ceux comme vous, avez perdu la guerre. Vous êtes une relique d’un passé honteux de cette belle province. Vous représentez le passé.&lt;/p&gt;
&lt;p&gt;Ma fille est l’avenir. Deal with it.&lt;/p&gt;</description></item><item><title>Philae Lander signing off</title><link>https://rselbach.com/philae-lander-signing-off/</link><pubDate>Wed, 27 Jul 2016 13:17:58 +0000</pubDate><guid>https://rselbach.com/philae-lander-signing-off/</guid><description>&lt;blockquote class="twitter-tweet" data-width="550" data-dnt="true"&gt;
 &lt;p lang="en" dir="ltr"&gt;
 It’s time for me to say goodbye. Tomorrow, the unit on &lt;a href="https://twitter.com/ESA_Rosetta?ref_src=twsrc%5Etfw"&gt;@ESA_Rosetta&lt;/a&gt; for communication with me will be switched off forever&amp;#8230;
 &lt;/p&gt;
 &lt;p&gt;
 &amp;mdash; Philae Lander (@Philae2014) &lt;a href="https://twitter.com/Philae2014/status/757938537803153408?ref_src=twsrc%5Etfw"&gt;July 26, 2016&lt;/a&gt;
 &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And so ends one of the most exciting science missions in recent years. The team at ESA managed a remarkable feat while also engaging the rest of us emotionally. Great job to all involved.&lt;/p&gt;</description></item><item><title>Best icecream I’ve ever had</title><link>https://rselbach.com/best-icecream-ever/</link><pubDate>Mon, 25 Jul 2016 16:52:58 +0000</pubDate><guid>https://rselbach.com/best-icecream-ever/</guid><description>&lt;p&gt;Years ago, I was part of a project that went completely off the rails. A little context: we were a services company and we had local offices in cities all over the country. My team provided 2nd-level support which means we often had the PMs call us from those via an annoying Nextel radio.&lt;/p&gt;
&lt;p&gt;I won’t go through the details but suffice it to say this project envolved one such branch going rogue and committing actual fraud, with criminal proceedings and all. People were on the edge, and the relationship with that branch was increasingly hostile. There was also an internal power struggle in the company between some directors at that point. In other words, a clusterfuck I’ll always cherish, if by cherish you mean hate hate hate. Anywho…&lt;/p&gt;
&lt;p&gt;One time, there was a national holiday on a Thursday and we were going to make it a long weekend. As customary, I communicated with all the PMs about contigency plans. This PM then told us that we could not take Friday off because the customer wanted us to fly over there. We were supposed to be at the customer’s site early Friday morning. That meant we would have to fly Thrusday afternoon. I wasn’t happy.&lt;/p&gt;
&lt;p&gt;It immediatly felt arbitrary too. As I said, the relationship was not good and we suspected he was just trying to cost us our days off. I knew enough of the customer to be fairly sure they would not have requested us that Friday. Why did the customer want us then? There was nothing yet on production and if it was just to show progress, surely we could move it to Monday. At worst, can’t we make it over the phone? No, no, no, he said. The customer was adamant that we be there on Friday. Sucked to be us.&lt;/p&gt;
&lt;p&gt;So we flew over Thursday afternoon and on Friday morning we headed to the customer’s offices only to find it closed. They too had made it a long weekend and wouldn’t be back until Monday.&lt;/p&gt;
&lt;p&gt;Normally I would be furious over the waste of time but to be honest, both I and my colleague smiled at that. It confirmed that the PM just tried to screw us and the customer have never asked for us. We headed back to the local office.&lt;/p&gt;
&lt;p&gt;Before coming in, we both bought ourselves some icecream. My friend stayed in the little garden in front and I went in. The PM immediatly saw me and demanded to know why I wasn’t at the customer yet. I didn’t answer. Instead I grabbed the Nextel radio and headed back out with the PM following. I then sat down on the grass and called my director. Smiling and staring at the PM, I told him about the office being closed. The PM’s face froze when my director asked to talk to him.&lt;/p&gt;
&lt;p&gt;We sat outside under the sun, enjoying our icecreams while the PM got shouted at. It was the best icecream I’ve ever had.&lt;/p&gt;</description></item><item><title>Is it really as bad as it looks?</title><link>https://rselbach.com/as-bad-as-it-looks/</link><pubDate>Mon, 18 Jul 2016 11:39:00 +0000</pubDate><guid>https://rselbach.com/as-bad-as-it-looks/</guid><description>&lt;p&gt;&lt;strong&gt;&lt;em&gt;Hi, I have a question&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Do you ever not?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;“Learning never exhausts the mind.”&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Leonardo DaVinci? I’m proud of you. Go ahead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Thank you. I’ve been reading all these horrific news stories about the Olympic games and Rio…&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Oh that. Yeah, I’ve been getting asked that question regularly lately.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Is it really that bad?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yes and no. There is a lot of sensationalism out there. But it’s bad and, at least to us Brazilians, not at all unexpected because Rio is widely known as a hellhole among us.&lt;/p&gt;
&lt;p&gt;Years ago, when the games were announced, most of us in Brazil expected the worst. Rio is widely perceived by Brazilians as a hellhole.&lt;/p&gt;
&lt;p&gt;I spent a whole month there once because of work and it left a strong mark in me. I’ve seen people being mugged, a dead body was left outside my hotel one night, I was in the middle of a shooting between rival gangs, and — crème de la crème — one day some big shot drug trafficker was killed in jail and what followed was, to me, surreal. The city is beautiful though.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;What happened?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;All thoughout that day, people kept telling me about the news of this guy who got killed. I could not care less, to be honest. But at the end of the day I finally understood when I tried leaving the office and people said I was nuts. I wasn’t allowed to go because the drug gangs issued a curfew on the city.&lt;/p&gt;
&lt;p&gt;A. Curfew. On. The. City.&lt;/p&gt;
&lt;p&gt;At times like that, commerces close their doors all over the place and the police completely disappears.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;That’s insane! How is anyone safe during the games?!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Well, here’s where I point out that it’s probably not as bad as you’d think.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Wait what&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hold on. First of all, it is not in the gangs’ interest to attract international attention. They already have full control over the city, the police, etc. It’s in their best interest to wait the games out and then continue with business as usual. I doubt there will be problems with the drug traffic.&lt;/p&gt;
&lt;p&gt;Second, there will be 50,000 security personnel there. FIFTY THOUSAND. And again, it’s in the powers that be’s interests that foreigners have a good time there. It will suck for people from Rio who will be hounded by the police and the army, though.&lt;/p&gt;
&lt;p&gt;Just a couple of weeks ago the Brazilian Congress passed a special law granting de facto immunity to members of security forces who happen to commit crimes against civilians during the games. It’s going to be fun.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Wait what? Immunity&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;De facto&lt;/em&gt;. What the law does is transfer the authority of judging these cases to militaty justice. In practice, it means immunity. And the reason for the law was exactly to allow the police and the army to trump people’s rights in order to keep the peace. It’s so very Rio.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;And terrorism?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Yeah, I don’t know. As we’ve seen these past weeks, you don’t need much to kill people. A rental truck is enough. So yes, there might be something. Then again, 50,000 security personnel. I’m guessing there might be easier targets out there. In short, I don’t know but I don’t think the risk is high enough to worry about it.&lt;/p&gt;
&lt;p&gt;What will be horrible is already happening and mostly hidden from foreigners. It’s the human rights abuses against the poor. That to is very Rio. They’re make every effort to hide the poor and prevent them from being seen, no matter the cost. Thousands have been forcebly relocated already.&lt;/p&gt;
&lt;p&gt;And in the end, what for? The city (and its people) won’t come out better after the games than before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;It’s depressing&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Indeed.&lt;/p&gt;</description></item><item><title>Ubuntu on Windows</title><link>https://rselbach.com/ubuntu-on-windows/</link><pubDate>Fri, 27 May 2016 18:04:05 +0000</pubDate><guid>https://rselbach.com/ubuntu-on-windows/</guid><description>&lt;p&gt;I’ve just installed Git on a Windows 10 box using &lt;tt&gt;apt-get&lt;/tt&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/ubuntu-on-windows/apt-get_hu_536f3a90927e32c6.webp 320w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_3d6366a218f173a1.webp 480w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_632ef03d5822bef6.webp 640w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_7869863c4bf26ecf.webp 800w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_96bd86ea9b0f2d86.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/ubuntu-on-windows/apt-get.png"
 alt="apt-get"
 loading="lazy"
 decoding="async" width="1025" height="768" srcset="https://rselbach.com/ubuntu-on-windows/apt-get_hu_317a8bece888ac6e.png 320w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_5785b3d0deeefed3.png 480w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_a0654d985ab718ef.png 640w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_c6868c82293d444d.png 800w, https://rselbach.com/ubuntu-on-windows/apt-get_hu_19ddcd4a9768dba1.png 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;What a glorious time to be alive 😉&lt;/p&gt;</description></item><item><title>How do I add an unknown attribute to an element in ReactJS?</title><link>https://rselbach.com/add-unknown-attribute-element-reactjs/</link><pubDate>Thu, 17 Mar 2016 12:34:22 +0000</pubDate><guid>https://rselbach.com/add-unknown-attribute-element-reactjs/</guid><description>&lt;p&gt;There’s this project I’ve been working on in ReactJS and Chrome and I needed to use a &lt;code&gt;&amp;lt;webview&amp;gt;&lt;/code&gt; component. In case you’re not familiar with &lt;code&gt;&amp;lt;webview&amp;gt;&lt;/code&gt;, it looks something 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;-webkit-text-size-adjust:none;"&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;webview&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://robteix.com&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;webview&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One of the coolest features of &lt;code&gt;&amp;lt;webview&amp;gt;&lt;/code&gt; is the ability to isolate scope or &lt;em&gt;partition&lt;/em&gt; it. When you use a partition, all cookies, local and session storage, etc, will be stored separately from other partitions. All you need to do is add the &lt;code&gt;partition&lt;/code&gt; attribute. So I had something similar to this inside one of my components —&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return (
 &amp;lt;webview id=&amp;quot;foo&amp;quot; 
 src=&amp;quot;https://somewebsite.com/&amp;quot;
 partition=&amp;quot;persist:foobar&amp;quot; /&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To my surprise, multiple &lt;code&gt;&amp;lt;webview&amp;gt;&lt;/code&gt;s were sharing the same session data. Not good. A quick glance in the app storage directory told me that no partition was being created. What’s wrong?&lt;/p&gt;
&lt;p&gt;We can look at the HTML output for hints —&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;-webkit-text-size-adjust:none;"&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;webview&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;id&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;foo&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://somewebsite&amp;#34;&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;MyComponent__component___3U2KV&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;data-reactid&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;.0.0.0.2:$MyComponent.0&amp;#34;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;tabindex&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;-1&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style="color:#f92672"&gt;webview&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No &lt;code&gt;partition&lt;/code&gt; to be found. The reason is React doesn’t know what to do with that attribute and as a result it is not output. There are good reasons for this, but the main one is that properties may (and often do) work differently accross browsers and React needs to be able to deal with this.&lt;/p&gt;
&lt;p&gt;But how can we add the attribute that we need then? You could add some JavaScript to get the element but that’s not very reactive. The reactive way to do this is to leverage &lt;code&gt;ref&lt;/code&gt;, which can provide a reference to an element.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-jsx" data-lang="jsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;Element&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ref&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;somevar&lt;/span&gt;} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In this code, &lt;code&gt;somevar&lt;/code&gt; will hold a reference to &lt;code&gt;&amp;lt;Element&amp;gt;&lt;/code&gt;. Better yet, just use a function instead.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-jsx" data-lang="jsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;Element&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ref&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{&lt;span style="color:#66d9ef"&gt;function&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) { &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Something&lt;/span&gt;(); }} /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or in ES6 syntax.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-jsx" data-lang="jsx"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&amp;lt;&lt;span style="color:#f92672"&gt;Element&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ref&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;{(&lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;) =&amp;gt; &lt;span style="color:#a6e22e"&gt;e&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Something&lt;/span&gt;() } /&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which finally takes us to our problem: how do we add the &lt;code&gt;partition&lt;/code&gt; attribute? Easy peasy.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return (
 &amp;lt;webview id=&amp;quot;foo&amp;quot; 
 src=&amp;quot;https://somewebsite.com/&amp;quot;
 ref={elm =&amp;gt; elm &amp;amp;&amp;amp; elm.setAttribute('partition', 'persist:foobar')} /&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And that’s it, a simple way to add unknown attributes to elements in React.&lt;/p&gt;</description></item><item><title>Clashing method names in Go interfaces</title><link>https://rselbach.com/clashing-method-names-go-interfaces/</link><pubDate>Fri, 11 Mar 2016 01:13:26 +0000</pubDate><guid>https://rselbach.com/clashing-method-names-go-interfaces/</guid><description>&lt;p&gt;I wrote about &lt;a href="https://rselbach.com/interfaces-go-c/"&gt;how the Go and C# compilers implement interfaces&lt;/a&gt; and mentioned how C# deals with clashing method names but I didn’t talk about how Go does it, so here it is.&lt;/p&gt;
&lt;p&gt;Two interfaces with the same method name that need to behave differently is very likely a sign of bad API design and we should fix it instead. But sometimes we can’t help it (e.g. the interfaces are part of a third-party package). How do we deal with it then? Let’s see.&lt;/p&gt;
&lt;p&gt;Given two interfaces&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Firster interface {
 DoSomething()
}

type Seconder interface {
 DoSomething()
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We implement them like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyStruct struct{}

func (ms MyStruct) DoSomething() {
 log.Println(&amp;quot;Doing something&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can &lt;a href="http://play.golang.org/p/peyb-wv7L8"&gt;run this little test here&lt;/a&gt; to verify that &lt;code&gt;MyStruct&lt;/code&gt; implements both interfaces.&lt;/p&gt;
&lt;p&gt;But what if we need &lt;code&gt;DoSomething()&lt;/code&gt; to do something different depending on whether &lt;code&gt;MyStruct&lt;/code&gt; is being cast as &lt;code&gt;Firster&lt;/code&gt; or &lt;code&gt;Seconder&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Go doesn’t support any kind of explicit declaration of interfaces when implementing methods. We can’t do something like, say&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyStruct struct{}

func (ms MyStruct) Firster.DoSomething() {
 log.Println(&amp;quot;Doing something&amp;quot;)
}

func (ms MyStruct) Seconder.DoSomething() {
 log.Println(&amp;quot;Doing something&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That won’t work. The solution is to wrap &lt;code&gt;MyStruct&lt;/code&gt; with a new type that reimplements &lt;code&gt;DoSomething()&lt;/code&gt;. Like this&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type SeconderWrapper struct {
 MyStruct
}

func (sw SeconderWrapper) DoSomething() {
 log.Println(&amp;quot;Doing something different&amp;quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we need to pass it to a function expecting &lt;code&gt;Seconder&lt;/code&gt;, we can wrap &lt;code&gt;MyStruct&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ms := MyStruct{}
useSeconder(SeconderWrapper{ms})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That will run the &lt;code&gt;DoSomething()&lt;/code&gt; from &lt;code&gt;SeconderWrapper&lt;/code&gt;. When passing it as &lt;code&gt;Firster&lt;/code&gt;, the original &lt;code&gt;DoSomething()&lt;/code&gt; will be called instead. You can &lt;a href="http://play.golang.org/p/iwEvW7hkx4"&gt;see this in action here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Interfaces in Go and C#</title><link>https://rselbach.com/interfaces-go-c/</link><pubDate>Wed, 09 Mar 2016 14:26:33 +0000</pubDate><guid>https://rselbach.com/interfaces-go-c/</guid><description>&lt;p&gt;I make no secret of the fact that I love Go. I think it’s a wonderfully designed language and it gave me nothing but pleasure in the years I’ve been working with it full time.&lt;/p&gt;
&lt;p&gt;Now however I am working on a project that requires the use of C#, which prompted me to realize something.&lt;/p&gt;
&lt;p&gt;When people ask about Go, most people talk about channels and concurrency but I think one of the most beautiful aspects of Go is its implementation of interfaces.&lt;/p&gt;
&lt;p&gt;To see what I mean, let’s define a simple interface in Go.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Greeter interface {
 Hello() string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now say we have a type that implements this interface&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyGreeter struct {}

func (mg MyGreeter) Hello() string {
 return &amp;quot;Hello World&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is it. &lt;code&gt;myGreeter&lt;/code&gt; &lt;em&gt;implicitly&lt;/em&gt; implements the &lt;code&gt;Greeter&lt;/code&gt; interface and we can pass it along to any function that accepts a &lt;code&gt;Greeter&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func doSomethingWithGreeter(g Greeter) {
 // do something
}

func main() {
 mg := myGreeter{}
 doSomethingWithGreeter(mg)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fact that the implementation is actually important, but we’ll get to that. Let’s first do the same thing in C#.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Greeter
{
 string Hello();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we create a class that implements the interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyGreeter
{
 public string Hello()
 {
 return &amp;quot;Hello world&amp;quot;;
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We quickly find out that the compiler doesn’t like this.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static string doSomething(Greeter g)
{
 return g.Hello ();
}

public static void Main (string[] args)
{
 MyGreeter mg = new MyGreeter ();
 doSomething (mg);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The compiler complains that it cannot convert &lt;code&gt;MyGreeter&lt;/code&gt; to type &lt;code&gt;Greeter&lt;/code&gt;. That’s because the C# compiler requires classes to &lt;em&gt;explicitly&lt;/em&gt; declare the interfaces they implement. Changing &lt;code&gt;MyGreeter&lt;/code&gt; as below solves the problem.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyGreeter : Greeter
{
 public string Hello()
 {
 return &amp;quot;Hello world&amp;quot;;
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And voilà, everything works.&lt;/p&gt;
&lt;p&gt;Now, we might argue that it is not much different. All you have to do is to declare the interface implemented by the class, right? Except it does make a difference.&lt;/p&gt;
&lt;p&gt;Imagine that you cannot change &lt;code&gt;MyGreeter&lt;/code&gt;, be it because it’s from a third-party library or it was done by another team.&lt;/p&gt;
&lt;p&gt;In Go, you could declare the &lt;code&gt;Greeter&lt;/code&gt; interface in your own code and the &lt;code&gt;MyGreeter&lt;/code&gt; that is part of somebody else’s package would “magically” implement it. It is great for mocking tests, for example.&lt;/p&gt;
&lt;p&gt;Implicit interfaces is an underrated feature of Go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; someone on Twitter pointed me to the fact that C# not only also has implicit interfaces but that this is the default state of things. That is true in a literal sense, but it’s not the same thing.&lt;/p&gt;
&lt;p&gt;Imagine in our C# above, we have a second interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface IAgreeable
{
 string Hello();
 string Bye();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(Yes, I was also told that in C# we should always name interfaces like &lt;code&gt;ISomethingable&lt;/code&gt;. I disagree but there it is.)&lt;/p&gt;
&lt;p&gt;We then implement our class thusly&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyClass : Greeter, IAgreeable
{
 public string Hello() {
 return &amp;quot;Hello world&amp;quot;;
 }
 public string Bye() {
 return &amp;quot;Bye world&amp;quot;;
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It now correctly implements both interfaces and all is well with the world. Until, that is, the &lt;code&gt;Hello()&lt;/code&gt; method needs to be different for each interface in which case you will need to do an &lt;em&gt;explicit&lt;/em&gt; implementation.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyClass : Greeter, IAgreeable
{
    string Greeter.Hello() {
 return &amp;quot;Hello world from Greeter&amp;quot;;
 }
 public string Hello() {
 return &amp;quot;Hello world from !Greeter&amp;quot;;
 }
 public string Bye() {
 return &amp;quot;Bye world&amp;quot;;
 }
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then the compiler will call the appropriate &lt;code&gt;Hello()&lt;/code&gt; depending on the cast.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static string doSomething(Greeter g)
{
 return g.Hello ();
}
public static string doSomethingElse(IAgreeable g)
{
 return g.Hello ();
}
public static void Main (string[] args)
{
 MyClass mg = new MyClass ();
 Console.Out.WriteLine(doSomething (mg));
 Console.Out.WriteLine(doSomethingElse (mg));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will print&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello world from Greeter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Hello world from !Greeter
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Personally I consider two interfaces with clashing method names that need to behave differently a design flaw in the API, but reality being what it is, sometimes we need to deal with this.&lt;/p&gt;</description></item><item><title>Github’s Swift Style guide</title><link>https://rselbach.com/githubs-swift-style-guide/</link><pubDate>Mon, 25 Jan 2016 15:27:12 +0000</pubDate><guid>https://rselbach.com/githubs-swift-style-guide/</guid><description>&lt;p&gt;Github has published a &lt;a href="https://github.com/github/swift-style-guide"&gt;Swift Style Guide&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you have to meet certain criteria to continue execution, try to exit early. So, instead of 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; n.isNumber {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// Use n here&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; &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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/blockquote&gt;
&lt;p&gt;use 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;guard&lt;/span&gt; n.isNumber &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; &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;// Use n here&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Feels very Go-like.&lt;/p&gt;</description></item><item><title>Invincible breaks my heart</title><link>https://rselbach.com/invincible-breaks-my-heart/</link><pubDate>Wed, 16 Dec 2015 22:38:26 +0000</pubDate><guid>https://rselbach.com/invincible-breaks-my-heart/</guid><description>&lt;p&gt;Spoilers abound. You’ve been warned.&lt;/p&gt;
&lt;p&gt;From the very beginning, Robert Kirkman maintained that &lt;strong&gt;Invincible&lt;/strong&gt; was his attempt at doing the whole superhero thing right. There is a running gag in this book that on every cover (at least for a lot time if not since the beginning), they take a little jab at the superhero genre as done by other houses.&lt;/p&gt;
&lt;p&gt;As news appeared that there would be a reboot, Kirkman was quick to declare that he was not going to be a dick to his readers and would not erase the whole timeline. By calling the arc “&lt;strong&gt;REBOOT?&lt;/strong&gt;” with a question mark, he could not have made this more clearer. Also, the cover tagline:&lt;/p&gt;
&lt;p&gt;&lt;a href="./img2015-12-16_04.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_15749038391b1ae2.webp 320w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_230b50d9e8d66384.webp 480w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_fb792bfd7fd89e77.webp 640w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_6f80963bee49aeb0.webp 800w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_30f003d5bb7cd2d5.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04.jpg"
 alt="img2015-12-16_04"
 loading="lazy"
 decoding="async" width="1024" height="107" srcset="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_206e96c901207e47.jpg 320w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_eff6cef48be89403.jpg 480w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_fd9efb13eb94aa33.jpg 640w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_75c4e0d0160ad6f.jpg 800w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_04_hu_7ba516a0b894188b.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And so we reach the end of the arc and Kirkman did keep his promise: the timeline was not erased. But maybe it should? That is the question that he leaves us with.&lt;/p&gt;
&lt;p&gt;Mark is taken back to just before he gets his powers. He then deals the best he can with what he knows is coming: Nolan’s attempt at conquering the world for the Viltrumite Empire and the consequent deaths of so many in the process. That’s the “hero stuff” and although it’s well done, none of it will matter.&lt;/p&gt;
&lt;p&gt;Mark has also to deal with being young again, living with his parents. His wife of many years now barely knows and definitely doesn’t care for him. And his daughter wasn’t born yet. How do you deal with this?&lt;/p&gt;
&lt;p&gt;During the first two issues of the arc, Mark’s personal life was mostly a B plot. But in this final issue, it becomes central.&lt;/p&gt;
&lt;p&gt;&lt;a href="./img2015-12-16_45.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_604b5b44c4438447.webp 320w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_ec98b122ea94e26.webp 480w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_ae0db18218296122.webp 640w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_ee3fc0aefdd7743b.webp 800w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_5e2ada57c4e98fc8.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45.jpg"
 alt="img2015-12-16_45"
 loading="lazy"
 decoding="async" width="1024" height="630" srcset="https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_be8d9d75d639659e.jpg 320w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_ded38f896eaaaf56.jpg 480w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_c317493a68d1293.jpg 640w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_cdce0a7795999349.jpg 800w, https://rselbach.com/invincible-breaks-my-heart/img2015-12-16_45_hu_55b83dc2ab933f3f.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mark gets a chance to go back to his original timeline, to his wife, to his daughter. But he’s asked for a sacrife. A hero’s sacrifice.&lt;/p&gt;
&lt;p&gt;The beings that brought Mark here explain that by setting himself in another path, he can fix the future, he can fix all the things that happened to Earth in the original timeline. All he has to do is make the choice.&lt;/p&gt;
&lt;p&gt;&lt;a href="./img18.21.24.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_bfb96a67e79f2934.webp 320w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_c386dd95380b497.webp 480w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_dd342944ef6e608.webp 640w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_500be8a2d7364a74.webp 800w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_96f91b37ec872fa9.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/invincible-breaks-my-heart/img18.21.24.jpg"
 alt="img18.21.24"
 loading="lazy"
 decoding="async" width="1024" height="518" srcset="https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_7b24a74c8b4a31ec.jpg 320w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_3e512475d15121f3.jpg 480w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_2a1a035f2f255d0d.jpg 640w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_3046cf89038b7fa7.jpg 800w, https://rselbach.com/invincible-breaks-my-heart/img18.21.24_hu_d8ec6968c23f36ad.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But he says no. Mark wants to go back to his wife and daughter. There is nothing that can make him change his mind.&lt;/p&gt;
&lt;p&gt;He doomed his planet and he is a disgrace, they say. I can live with that, he responds and goes back only to find desolation where the battle against Thragg is apparently finished – and apparently lost — I suppose we’ll learn more about that soon.&lt;/p&gt;
&lt;p&gt;And of course, Kirkman had to throw one extra punch at the guy. As a father, the reveal at then end of what Mark has lost is heartbreaking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Invincible&lt;/strong&gt; remains the only book that has &lt;em&gt;never&lt;/em&gt; once disappointed me in any of its arcs. This is amazing for a story ongoing for over 10 years and 126 issues.&lt;/p&gt;
&lt;p&gt;Now excuse me while I go hug my daughter.&lt;/p&gt;</description></item><item><title>Simplicity is Complicated – The dot Post</title><link>https://rselbach.com/simplicity-is-complicated-the-dot-post/</link><pubDate>Thu, 03 Dec 2015 16:32:13 +0000</pubDate><guid>https://rselbach.com/simplicity-is-complicated-the-dot-post/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Go is often described as a simple language. It is not, it just seems that way. Rob explains how Go’s simplicity hides a great deal of complexity, and that both the simplicity and complexity are part of the design.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;em&gt;&lt;a href="http://www.thedotpost.com/2015/11/rob-pike-simplicity-is-complicated"&gt;Simplicity is Complicated – The dot Post&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>The Evolutionary Psychology Nonsense</title><link>https://rselbach.com/the-evolutionary-psychology-nonsense/</link><pubDate>Mon, 23 Mar 2015 22:58:14 +0000</pubDate><guid>https://rselbach.com/the-evolutionary-psychology-nonsense/</guid><description>&lt;p&gt;&lt;a href="https://evolution-institute.org/images/posts/2015/03/mind-1272x400.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_9d3efb6807961cfd.webp 320w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_7f6c4e9cebd2a658.webp 480w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_967f2e7662ce292a.webp 640w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_c5e39c9c0e0ef497.webp 800w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_180a2660d66e4c8b.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400.jpg"
 alt=""
 loading="lazy"
 decoding="async" width="1024" height="322" srcset="https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_9bed260ad3f45d73.jpg 320w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_af34301f9f38e5f1.jpg 480w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_3a8e3eb2344e95dc.jpg 640w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_e2592009b2f843ba.jpg 800w, https://rselbach.com/the-evolutionary-psychology-nonsense/mind-1272x400_hu_a7f148330a224d82.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I have a friend who is all into the whole evolutionary psychology fad. It’s useful when you want to rationalize bad behaviour. I once told him that evolutionary psychology was pseudoscience to which he replied saying that I was just trying to negate the world as-is.&lt;/p&gt;
&lt;p&gt;But apparently I’m not alone, as &lt;a href="https://evolution-institute.org/article/evolutionary-psychology-is-neither/"&gt;Jonathan Marks writes&lt;/a&gt; (emphasis is mine) —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I can’t shake the feeling that &lt;strong&gt;the methodologies I have encountered in evolutionary psychology would not meet the standards of any other science&lt;/strong&gt;.  For a notable example, it is apparently a revelation to evolutionary psychology that one cannot readily generalize about the human condition from a sample of humans that is Western, Educated, Industrialized, Rich, and Democratic. Perhaps this was news in psychology – creationist, evolutionary, or otherwise – but, sad to say, everybody else who works with cultural diversity knew that a really long time ago.&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Avoiding Avoidance Behaviour</title><link>https://rselbach.com/avoiding-avoidance-behaviour/</link><pubDate>Fri, 13 Mar 2015 13:06:36 +0000</pubDate><guid>https://rselbach.com/avoiding-avoidance-behaviour/</guid><description>&lt;p&gt;I am currently trying a new approach to deal with my social phobia. Basically it involves avoiding avoidance behaviours. You see, people with social anxiety tend to avoid problematic situations and run to metaphorical safe places. After a while, &lt;strong&gt;the avoidance behaviours become almost automatic, almost imperceptible&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Almost but not quite.&lt;/p&gt;
&lt;p&gt;We behave so without thinking but we recognize the behaviour while we’re doing it. &lt;strong&gt;And that’s exactly when we have to force ourselves to avoid avoidance.&lt;/strong&gt; It is very difficult and the temptation of avoidance is almost irresistible. The problem, of course, is the more we avoid things, the harder it is to stop doing it later in life.&lt;/p&gt;
&lt;p&gt;Last night I posted something to Facebook —&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.facebook.com/robteix/posts/10153208453849048"&gt;https://www.facebook.com/robteix/posts/10153208453849048&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The voices of social anxiety immediately started working in my head. “FriendA will mock at you,” they shout. “FriendB will think you’re pathetic.” And so they went. I came this close to deleting the post before stopping and forcing myself to ignore the voices.&lt;/p&gt;
&lt;p&gt;It is not easy. &lt;strong&gt;It is so not easy that I am writing a blog post about it&lt;/strong&gt;. Social phobia makes my mind work against me: it constantly attacks me, my self-esteem, and my confidence. It would be so much easier to just delete the post.&lt;/p&gt;
&lt;p&gt;Dealing with social anxiety involves many counter-intuitive measures: it forces socially anxious people to go against what our own brains tell us is not the safest route.&lt;/p&gt;
&lt;p&gt;I need to keep working on it. I’ll need to continue to force myself into doing more of what I desperately want to avoid. Let’s see how it goes.&lt;/p&gt;</description></item><item><title>Why do Salespeople Believe in Magic?</title><link>https://rselbach.com/why-do-salespeople-believe-in-magic/</link><pubDate>Wed, 11 Mar 2015 20:29:31 +0000</pubDate><guid>https://rselbach.com/why-do-salespeople-believe-in-magic/</guid><description>&lt;p&gt;File this one under techies complaining about non-techies. Over the years, I have noticed a pattern with salespeople, &lt;strong&gt;they have a firm belief in wishful thinking&lt;/strong&gt;. They &lt;em&gt;honestly&lt;/em&gt; believe that wishing something to be true will magically make it so.&lt;/p&gt;
&lt;p&gt;&lt;a href="./Wishful_Thinking.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/why-do-salespeople-believe-in-magic/Wishful_Thinking_hu_7689a164a786158e.webp 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/why-do-salespeople-believe-in-magic/Wishful_Thinking.jpg"
 alt="Wishful_Thinking"
 loading="lazy"
 decoding="async" width="447" height="398" srcset="https://rselbach.com/why-do-salespeople-believe-in-magic/Wishful_Thinking_hu_6e8b4005b034c6c7.jpg 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The specific pattern I noticed many a time goes something like this.&lt;/p&gt;
&lt;p&gt;The customer wants something done and goes through their account manager to request that. The account manager — a fancy name for salesperson — commits to a date without talking to the developer first. They then go to the developer and tell her something to the effect of “yeah, I’m going to need that by Friday morning.” The exasperated developer explains that this is not feasible and the account manager responds by simply repeating that they will need it by Friday. They usually leave at this point satisfied that everything is fine.&lt;/p&gt;
&lt;p&gt;Come Friday, the account manager is then horrified to discover that the feature is not ready. “But we made a commitment with the customer,” they’ll say, emphasizing the “we” that never was.&lt;/p&gt;
&lt;p&gt;This has happened some many times in my career that I should no longer be surprise and yet I still do. Every time.&lt;/p&gt;
&lt;p&gt;Of course, what they are really doing is &lt;strong&gt;trying to put pressure on the developers&lt;/strong&gt; so that they will hurry up and deliver on the desired timeframe. And to be fair, it can sometimes work, but if the developer tells them &lt;strong&gt;in no uncertain terms&lt;/strong&gt; that the deadline is not feasible, then the salesperson is taking the risk by herself.&lt;/p&gt;
&lt;p&gt;By committing to a date with the customer before talking to the developer, the salesperson has already taken a big risk. &lt;strong&gt;They will then try to spread that risk by sharing it with the developer&lt;/strong&gt;. Since the developer never had a chance to agree with that in the first place, it is only fair that she should be able to refuse to take the risk if she doesn’t believe it is worth it. Why should she?&lt;/p&gt;
&lt;p&gt;And yet, &lt;strong&gt;we developers often accept the risk by being passive and simply accepting it&lt;/strong&gt;. This problem can be exacerbated by managers who are also passive. Many years ago I worked at a company where the engineering power that be were submissive to the sales department, which led to some of the worst experiences in my life as a developer, including the Project From Hell.&lt;/p&gt;
&lt;p&gt;At the time, I led the engineering services group, so whenever a new project came about that required software development, it had to go through me for analysis. Another group leader analysed the infrastructure projects. Then one day this large project showed up on my desk. I looked over it along with the infrastructure guy and we agreed that it was a monster of a project, including developing a huge distributed nationwide (Brazil) infrastructure, a huge system developed from scratch, and a lot of technology transfer and end-user training.&lt;/p&gt;
&lt;p&gt;The project had some timeframes attached to it and although they were tight, they were not what immediately caught out eyes. We saw the pricing they were offering the customer and it was clear to us that it was low. We raised the issue but were told not to worry about it. &lt;strong&gt;There are valid business reasons to do projects at a loss sometimes&lt;/strong&gt;, so it was okay, except the exact wording to us was, “please limit yourselves to your &lt;em&gt;little&lt;/em&gt; expertise sphere.” Ok then.&lt;/p&gt;
&lt;p&gt;We talked to our teams and we committed to the dates defined in the project documentation. Again, it was a little tight but feasible: we would have many months to get things ready for initial deployment.&lt;/p&gt;
&lt;p&gt;About a week after we approved the statement of work, I received a call from the customer. At this point, I had not yet engaged the customer at all, so they had gotten my phone number from their sales rep. The customer was possessed. &lt;strong&gt;This person I had never talked to before was shouting at me on the phone&lt;/strong&gt; that we were late. It took me a while to call him down and understand what the issue was.&lt;/p&gt;
&lt;p&gt;As it turns out, the sales team, in an effort to get the customer’s signatures before the end of the quarter, changed the statement of work after we had gone through it, moving the dates to &lt;em&gt;right that very moment.&lt;/em&gt; We had not even started working on the project yet and the customer was expecting it to be ready right then. &lt;strong&gt;We were late before we even started&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Many stressful meetings later we managed to agree on some new dates, but they were much tighter than the ones originally in the statement of work and would required us to outsource parts of the project to a contractor to help speed up things. Incidentally, what we paid the contractor for &lt;em&gt;only a part of the project&lt;/em&gt; was more than what our company made on the project. &lt;strong&gt;All because the sales team wanted to make sure they got their commission in that quarter&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The people responsible would eventually be let go of the company, in great part due to this, but that did not prevent the company from losing at least an order of magnitude more than what it made from that project.&lt;/p&gt;
&lt;p&gt;And still, I continue to see salespeople ignoring the developers and then trying to share the fallout. &lt;strong&gt;Developers need to stand firmly by their professional evaluations of deadlines and technical feasibilities&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Of course, if you turn out to be wrong, all of this is moot.&lt;/p&gt;</description></item><item><title>Goodbye, Mr. Spock.</title><link>https://rselbach.com/goodbye-mr-spock/</link><pubDate>Fri, 27 Feb 2015 19:31:53 +0000</pubDate><guid>https://rselbach.com/goodbye-mr-spock/</guid><description>&lt;p&gt;&lt;a href="./leonard_nimoy_02.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_6c6f0decf5e4195e.webp 320w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_a5796f7ae96a06af.webp 480w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_e3dbc833f9cf4b34.webp 640w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_19b937628b8d1d0a.webp 800w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_c27485715b38d9af.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768.jpg"
 alt="Leonard Nimoy"
 loading="lazy"
 decoding="async" width="1024" height="768" srcset="https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_1c603e5d227574e3.jpg 320w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_14a65417df2a587b.jpg 480w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_c1550139bcf5f4f.jpg 640w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_3795dcaaf83a42a7.jpg 800w, https://rselbach.com/goodbye-mr-spock/leonard_nimoy_02-1024x768_hu_c41862b1f07f2547.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I am sad. &lt;a title="The New York Times" href="http://www.nytimes.com/2015/02/27/arts/television/leonard-nimoy-spock-of-star-trek-dies-at-83.html?module=Notification&amp;version=BreakingNews&amp;region=FixedTop&amp;action=Click&amp;contentCollection=BreakingNews&amp;contentID=30440287&amp;pgtype=article" target="_blank"&gt;The New York Times&lt;/a&gt; —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Leonard Nimoy, the sonorous, gaunt-faced actor who won a worshipful global following as Mr. Spock, the resolutely logical human-alien first officer of the Starship Enterprise in the television and movie juggernaut “Star Trek,” died on Friday morning at his home in the Bel Air section of Los Angeles. He was 83.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am actually sad. As much as &lt;strong&gt;Star Trek&lt;/strong&gt; was a part of my life, I had not felt this way when other cast members passed away in the past.&lt;/p&gt;
&lt;p&gt;I suppose Nimoy was different somehow. My theory is that as Spock, he would (almost) never smile and that made his rare smile that much more important. When I think of Nimoy/Spock, it is his smile that I picture in my mind. When out of character, he was &lt;em&gt;always&lt;/em&gt; smiling. This contrast forces an emotional connection or something. I don’t know.&lt;/p&gt;
&lt;p&gt;What I do know is I am sad. For whatever’s worth, Nimoy was a part of my life.&lt;/p&gt;
&lt;p&gt;You’ll be missed, Mr. Nimoy. I have been, and always shall be, your fan.&lt;/p&gt;</description></item><item><title>My social anxiety screwed me royally this week</title><link>https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/</link><pubDate>Sat, 18 Oct 2014 11:48:19 +0000</pubDate><guid>https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/</guid><description>&lt;p&gt;&lt;a href="./1024px-Sadness_4.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_33ee231265395f65.webp 320w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_72feabd47b89ade1.webp 480w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_a2fb02df0eab4609.webp 640w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_561332c27302fdf7.webp 800w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_161389565aa8c168.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678.jpg"
 alt="By Christopher Walker (Sadness) CCBY−SA2.0(http://creativecommons.org/licenses/by−sa/2.0)CC BY-SA 2.0 (http://creativecommons.org/licenses/by-sa/2.0), via Wikimedia Commons"
 loading="lazy"
 decoding="async" width="1024" height="678" srcset="https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_55bf45ec843e39d4.jpg 320w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_4adc2eab52a3e49a.jpg 480w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_c8d036771b3550ff.jpg 640w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_cca4686209ccd1bf.jpg 800w, https://rselbach.com/my-social-anxiety-screwed-me-royally-this-week/1024px-Sadness_4-1024x678_hu_40052fa7f3de9e2b.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A few years ago I &lt;a href="https://rselbach.com/2011/08/04/my-need-for-anonymity/" title="My need for anonymity"&gt;wrote&lt;/a&gt; about how social anxiety makes me use fake accounts on the web.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I love coding. I have done it since I was a kid and it’s the best thing I know how to do. And then there is open source. Open source projects should be the perfect venue for me to have f un. Except I am scared stiff by the idea that someone might laugh at the code. It came to a point where it is impossible for me to contribute. Then I’ve come up with a solution: an alias. For the past several years I’ve lived two different lives online: one as myself and another as an alias. I keep them strictly separate.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Actually I today use more than one single separate life. Looking at my Chrome identities menu I count four (including the real me), but I actually have more around that I have abandoned.&lt;/p&gt;
&lt;p&gt;It has allowed me to do what I like to do. I don’t have to be afraid because I know all I have to do is abandon one account and start over with another. It’s a good solution but it has some issues.&lt;/p&gt;
&lt;p&gt;I have been offered this &lt;em&gt;great&lt;/em&gt; job in the past by the manager of someone I’ve worked together. It was one of the Big Tech Companies, a place I &lt;em&gt;really&lt;/em&gt; would love to work. All great, right? Except the offer was not addresses to Roberto Teixeira, but to one of my aliases. Tough luck. I’ve soon abandoned that alias for good.&lt;/p&gt;
&lt;p&gt;So yes, it sucks. But not as much as it has sucked this week.&lt;/p&gt;
&lt;p&gt;I—under an alias—have been working with a developer of a big open source project out there to try to solve a problem we were having at work. And I found a solution that was pretty clever. That developer checked it out and thought it was great and then we both wrote a proposal and submitted it. And it was accepted and our change will be part of their next major release.&lt;/p&gt;
&lt;p&gt;I’m not saying it was something revolutionary or anything. Still, it was something I am very proud of. And there will be a name there in the changelog/release notes/whatever but it will not be my name.&lt;/p&gt;
&lt;p&gt;This happened the same week I learned that someone I–the real me; real name and everything–interviewed with a few months ago had dismissed me for not having open source contributions.&lt;/p&gt;
&lt;p&gt;In short, I am sad and angry. Fuck social anxiety. 🙁&lt;/p&gt;
&lt;p&gt;(&lt;em&gt;photo: Christopher Walker (Sadness) / &lt;a href="http://creativecommons.org/licenses/by-sa/2.0"&gt;CC BY-SA 2.0&lt;/a&gt; / via Wikimedia Commons&lt;/em&gt;)&lt;/p&gt;</description></item><item><title>From Kerbin to Laythe and Back</title><link>https://rselbach.com/from-kerbin-to-laythe-and-back/</link><pubDate>Thu, 25 Sep 2014 19:29:21 +0000</pubDate><guid>https://rselbach.com/from-kerbin-to-laythe-and-back/</guid><description>&lt;p&gt;Even though I’ve been playing Kerbal Space Program for over a year, I have only barely explored the outer kerbolar system. I have been around Jool before and I have even landed on Laythe but never managed to make a round trip until this week.&lt;/p&gt;
&lt;p&gt;Traveling all the way to Jool’s moon Laythe and back is not easy&lt;sup&gt;[&lt;i&gt;&lt;strong&gt;citation needed&lt;/strong&gt;&lt;/i&gt;]&lt;/sup&gt;. It’s not so much getting there—although that’s not trivial, either—but we want to &lt;em&gt;land&lt;/em&gt; on Laythe &lt;em&gt;and&lt;/em&gt; we want to get back to orbit later: that takes a lot of fuel.&lt;/p&gt;
&lt;p&gt;This is my adventure.&lt;/p&gt;
&lt;h2 id="mission-overview"&gt;Mission Overview&lt;/h2&gt;
&lt;p&gt;The Kerbal Air and Space Agency (KASA) will be sending two separate missions: the &lt;strong&gt;Laythe Recovery System (LARS)&lt;/strong&gt; and the &lt;strong&gt;Laythe Orbital Landing System (LOLS)&lt;/strong&gt;. The LARS stack is controlled by an automated probe and its function is to carry fuel for the travel back.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot31.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_3f4b96bc64bec8ec.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_d30fe1caecdc6b8e.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_495d7addc31b5e1d.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_e6093f521dc0ae6b.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_abf5db97b0f36e7f.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31.jpg"
 alt="screenshot31.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_a320d42201779c62.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_d056bf5b3c151353.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_9e16ee53c462f497.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_e4b43be18bf68bf6.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot31_hu_f525f37f9a0665d9.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although huge, it is only as heavy as it needs to be to deliver fuel, lacking scientific instrumentation and communication systems other than the Clamp-O-Tron’s acquisition signal.&lt;/p&gt;
&lt;p&gt;The second stack is LOLS, which will carry a 3-kerbal crew, the lander itself, and all the scientific instruments: a gravioli detector, a termometer, a barometer, and a Mystery Goo™.&lt;/p&gt;
&lt;p&gt;The two stacks will launch a couple of days between them and will meet in orbit around Jool over two years later. LOLS will then rendezvous with LARS, dock, and transfer any remaining fuel from its tanks and then drop its large tank before the LARS will then make the Hoffman transfer to Laythe&lt;sup id="fnref-1"&gt;&lt;a href="#fn-1" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="lars-launch-and-trans-joolian-insertion-burn"&gt;LARS Launch and Trans-joolian Insertion Burn&lt;/h2&gt;
&lt;p&gt;The first of the two missions to launch was LARS, the rationale being that if something were to go wrong later that prevented the LOLS launch, LARS could wait in Jool orbit for the several years until a new interplanetary window opened to allow LOLS—or its successor—to go rendezvous with it. It would be harder to keep a Kerbal crew waiting there&lt;sup id="fnref-2"&gt;&lt;a href="#fn-2" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Also, to minimze the risks, LARS was launched as soon as the window opened, allowing for more time to deal with any issues with LOLS later. This resulted in a night launch.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot32.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_11591f81349fb758.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_2111a878d7fc7e04.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_444bda4d0daa940d.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_1269c5fc556483b7.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_3529215ff986c9bf.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32.jpg"
 alt="screenshot32.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_a3d232049cfd5bd0.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_ac15e1ea72a5670.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_101cc5229f06ca9a.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_ab40b2ad8e5ab04.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot32_hu_fb34239cd78069f4.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Showing that LARS was not too heavy, it quickly started accelerating towards terminal velocity and we lower the thrust to about 80% so we don’t waste fuel fighting drag.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot34.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_4a30015e5b37249f.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_f4983873f68ce4d5.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_3f8365584ce44f3d.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_3e240cb58b864a74.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_89f28f6317199519.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34.jpg"
 alt="screenshot34.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_5a3af5c07c951d4.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_a996c6e4df7b0ed8.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_991dbc9cb50343bd.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_1fdc59666f9848fe.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot34_hu_8e128dfdde01b6d3.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At 7300m we start the roll program to begin gaining horizontal speed. A little later, just passing the 10,000m mark our two first boosters run dry and we have a beautiful separation.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot35.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_ceab067356cc93ab.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_97275314c68c9d44.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_702f5c71036644ac.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_af2fc48e79e2b419.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_588466fb75af530.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35.jpg"
 alt="screenshot35.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_8a2aadd04f33c5c3.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_c04eb7fc7cc7cea6.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_aa7f3132dd955527.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_9286a84601b62cd0.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot35_hu_6b2db0093c914774.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;At its parking altitude of 240Km, the probe circularizes its orbit and, as Robert Heinlein would have said it, we are halfway to anywhere&lt;sup id="fnref-3"&gt;&lt;a href="#fn-3" rel="footnote"&gt;3&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot37.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_3d89f2ae612cf9ec.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_ae9f4780dafc62ab.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_f4267ffe88e6fdc1.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_f560ef44d6058689.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_f62467048d8fdb.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37.jpg"
 alt="screenshot37.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_a44b3fd33d788927.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_2004577b91e86e4d.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_796c58948e8f696.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_e19b0c1723a38c92.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot37_hu_789534e262ac0759.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;About 2 hours after settling in its parking orbit, LARS is GO for TJI. This burn adds the 1,907m/s delta-V needed for LARS to reach Jool.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot38.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_cb980ee9ff40f7a0.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_91911c3568e850d1.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_294f20400368e31e.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_b384f7b3d3e4c7ff.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_6a57af0f73d485dd.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38.jpg"
 alt="screenshot38.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_e8f7db63174ca825.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_4bfb94d45dd50dcb.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_37624f3eb6838a2e.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_42efd003a525cb8.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot38_hu_ea95765a3f57d138.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After the burn is done, LARS is on its way. Time to remember to extend our four Gigantor solar panels to ensure the probe will have power in its journey&lt;sup id="fnref-4"&gt;&lt;a href="#fn-4" rel="footnote"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot40.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_aa5b7346beaccfb5.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_7980f51bb6829e2f.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_33c7b9e3140c03e.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_850688e25c168c12.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_e70274ca3391a634.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40.jpg"
 alt="screenshot40.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_fc2466cfffc11825.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_f20b0021407f63d5.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_505adcc26958dcb6.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_b005cc74e51906d4.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot40_hu_75909a2ca1190845.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;With LARS now on the way, the KSP staff turns its attention to their next mission: LOLS.&lt;/p&gt;
&lt;h2 id="lols-launch-and-trans-joolian-insertion-burn"&gt;LOLS Launch and Trans-joolian Insertion Burn&lt;/h2&gt;
&lt;p&gt;LOLS presents its own set of challenges. It is considerably heavier than LARS because not only it carries a similar amount of fuel—which it will spend much more quickly due to the extra mass—but it also adds the crew, large capsule, and scientific instrumentation.&lt;/p&gt;
&lt;p&gt;With the greater weight, the engines struggle to lift off the launch pad. It takes almost 20 seconds to clear the flag pole by the pad.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot41.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_d5f294a2c066b692.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_83beb5943a5d968f.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_7184519388d9bae6.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_176a7c8440babfb4.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_82703d48c3995e2f.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41.jpg"
 alt="screenshot41.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_dd2caee64d782b35.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_76f3ed5428b66ac.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_a2612e3762cb6777.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_d7b55268b79e16d8.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot41_hu_d55d0a19f40aefec.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It never comes close to terminal velocity while in the atmosphere and the throttle remains at 100% all the way. It begins roll program at 7300m and has barely accelerated past the 200m/s mark at this point.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot42.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_57dbc2c5e770b91a.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_63f9342dd1bfa132.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_289b1c4ac0efdc59.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_2c392d0ad57000f9.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_128a93cda0babd2a.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42.jpg"
 alt="screenshot42.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_a37ac1499dc2bf0.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_71cf457bb018c4ec.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_a39816b3643b6846.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_9d19e6a38cc2739d.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot42_hu_31c50bc08cdfbd43.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After another beautiful separation, the speed is mounting a bit faster now…&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot43.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_af4233517f403d89.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_4d490ffa9a8e75e9.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_79074674baa442c4.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_1e9a3cfd382d7a26.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_9253f89912c8a5b8.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43.jpg"
 alt="screenshot43.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_c89829e132bcf638.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_4adbe963de27d8c5.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_911c58caef8fa5db.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_e6c7eed49e93099.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot43_hu_61b2084496ad3ec0.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;… but it still takes a while. As well, although LARS managed to reach orbit on its second stage alone–albeit running on fumes at the end—, LOLS had to rely on the third stage to reach orbit, spending half its fuel tank.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot46.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_c5ebddbb874259d.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_c8b853ab6f44276c.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_3354a3eeaa5f077a.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_7fc3f1be5858dcdb.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_a448cd32610cc7c6.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46.jpg"
 alt="screenshot46.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_656962ab9922fa78.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_6426574e50811899.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_4417f6a4e926bbff.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_a222758f58792347.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot46_hu_dba571f1400d7deb.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;About 90 minutes after reaching the parking orbit, LOLS is GO for TJI. Delta-V needed is 1,904m/s.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot47.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_1c6f417a223a3650.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_a917cc3db0d6a1a.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_86eb4646b6e11f62.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_3b465392d11bc61f.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_644be26a6d5dc885.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47.jpg"
 alt="screenshot47.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_b66631e0a95ff7a6.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_831fb25cd982e27a.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_ef973087f106b047.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_6dfa2c8a80502011.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot47_hu_1bdbc52415866007.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Over a million kilometers behind LARS, LOLS is now on its way for the long, nearly-two-year-long journey to Jool.&lt;/p&gt;
&lt;h2 id="deep-space-transfer"&gt;Deep space Transfer&lt;/h2&gt;
&lt;p&gt;During the long transfer period spend after escaping Kerbin’s sphere of influence and before the encouter with Jool, the crew does some science.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot50.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_82164f18811f1e43.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_a1653e49f1ad8fc4.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_aeefebf6ae402ff3.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_2127e8743b9d597d.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_b88da7051952f1bd.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50.jpg"
 alt="screenshot50.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_6ef176c7808409a7.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_cfd84310c393eaf6.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_2305a8f249208fab.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_e9a3c540178c9a94.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot50_hu_6251bae2418a18f9.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tonmen Kerman goes EVA for a while and reports back to Kerbin. They also do a crew report and use the gravioli detector for some extra science production.&lt;/p&gt;
&lt;p&gt;The original plan was to have LARS arriving first and waiting for LOLS to arrive. That would make sense as LARS is an automated probe while LOLS is carrying actual kerbals. That is why mission control&lt;sup id="fnref-5"&gt;&lt;a href="#fn-5" rel="footnote"&gt;5&lt;/a&gt;&lt;/sup&gt; naïvely launched LARS first. As it turns out, it was a mistake.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot49.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_e952c21755f27ddf.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_3303234b29b774d8.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_11efae439cfd85d7.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_5183258aee48d1cc.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_d4b577b700308d1f.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49.jpg"
 alt="screenshot49.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_180c58d2c0094d2a.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_6a2308c11e9d5278.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_d0677313e1f1177c.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_583f2e1301593374.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot49_hu_c01931522ced7648.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As you can see in this picture, LARS &lt;em&gt;is&lt;/em&gt; indeed ahead of LOLS. However, due to their positions in relation to Jool itself, LOLS ends up getting caught by the Jool sphere of influence &lt;em&gt;before&lt;/em&gt; LARS. This caught mission control by surprise and almost ended the mission as all its attention was on the LARS approach and only by sheer dumb luck they noticed LOLS encountering Jool SOI.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot51.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_6852670c5c84bfcd.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_37474c7648881cd9.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_7824e96c0b4de981.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_9efd388c3f75061f.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_d45979e6c4168eb7.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51.jpg"
 alt="screenshot51.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_b6444cfe9410b832.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_f7cf0b38e97d00c9.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_9c979bc4a6c1df94.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_900a1f14eea668b2.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot51_hu_e0f17fa7f92eba6f.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In retrospect, this makes perfect sense. In the 1-2 days following the launch of LARS, Kerbin and Jool were moving in relation with each other. Kerbin, having a much lower orbit than Jool’s, was moving faster and since it was “behind” Jool, this meant both planets were actually getting closer to each other so LOLS had a slightly shorter trip than LARS. That’s orbital mechanics for you.&lt;/p&gt;
&lt;h2 id="lols-aerobreaking-and-joolian-orbit-insertion-burn"&gt;LOLS Aerobreaking and Joolian Orbit Insertion Burn&lt;/h2&gt;
&lt;p&gt;One year, 270 days into the mission, LOLS, already deep inside the joolian SOI, maneuvered to adjust its periapsis to 110Km above Jool. Jool’s atmosphere begins at around 138Km of its sea level, which is not that much considering how big the planet is. But that atmosphere is &lt;em&gt;very&lt;/em&gt; dense, which is good for aerobreaking, saving us some precious fuel.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot52.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_51204ba89503145e.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_f965420b2d556ad3.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_f36cb495d1e32d35.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_760f4225ad78a39d.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_27ca036823e3f0e6.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52.jpg"
 alt="screenshot52.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_8961447cd0a33dc1.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_d30f5e6362b10bf4.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_58d2e4915887886c.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_858aef8f6d73db6.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot52_hu_78bc4452f7b7fe82.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Two days later, LOLS reaches Jool…&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot53.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_c3214cc45c9b3d26.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_a974ab0a944a599f.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_6c06988bdbd69069.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_f37fafec2182164c.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_6a661da2b7a8b5cd.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53.jpg"
 alt="screenshot53.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_62dc25faa6a24b4e.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_c113ebfb700a500.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_89f4da681f04630a.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_f8ddf1f3596d4078.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot53_hu_7a06f0fbb97ef92c.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;… and falls 30Km into its dense atmosphere for some heartstopping aerobreaking.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot54.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_ff86db5bc8c31a3f.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_5666cffcb6cddc48.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_5a870a0b8fd130df.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_76caef9dba61065a.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_fb451ce2786fb0bd.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54.jpg"
 alt="screenshot54.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_5299ca59045455b5.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_b7be1f7f0b7a6c86.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_8e7714b7a02f7287.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_a4c7af4b51fb52df.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot54_hu_e8616f53f9768a0f.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mission control found 105Km to be a great aerobreaking altitude as LOLS re-emerged from the atmosphere with an apoapsis of a tad over 500Km, which required &lt;em&gt;very&lt;/em&gt; little delta-V during the Joolian Orbit Insertion burn to reach the desired parking orbit of 400Km. The crew adjusted their apoapsis and periapsis and waited for the LARS to arrive. This was the early hours of &lt;strong&gt;1y 273d MET&lt;/strong&gt;&lt;sup id="fnref-6"&gt;&lt;a href="#fn-6" rel="footnote"&gt;6&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id="lars-aerobreaking-and-joolian-orbit-insertion-burn"&gt;LARS Aerobreaking and Joolian Orbit Insertion Burn&lt;/h2&gt;
&lt;p&gt;Twenty seven days after the arrival of LOLS in Jool, LARS enters Jool’s SOI. LARS will try for an approach with a periapsis a little lower: 105Km. The 5Km difference doesn’t sound much but considering the density of Jool’s atmosphere, it makes a difference.&lt;/p&gt;
&lt;p&gt;Despite mission control’s best efforts to point it retrograde, LARS insisted in going nose first.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot55.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_7a4bc317b2e81303.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_6d91ebc685f2d1f7.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_16dc5c4191afade2.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_60adfa74a4ee3e23.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_8c8461b55b4a081b.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55.jpg"
 alt="screenshot55.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_5379803f29a3d0ee.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_fe090234109ec8ce.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_5da7212e49fae0c8.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_d80dbc00c39658a.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot55_hu_4cda095e198ef142.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LARS leaves the atmosphere with a apoapsis of a little under 400Km, again leaving little delta-V for the JOI burn. It parks at 280Km. This is 1y 386d MET, meaning the intrepid kerbal crew waited in orbit for 113 days for LARS, even though their total mission time so far was a little under 2 days shorter than LARS’.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot57.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_da63ad1d5722b4fc.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_3c6188017a9df223.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_56d200fd607530.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_7c0e27a680338606.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_5911ca57f8df291d.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57.jpg"
 alt="screenshot57.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_63f15904af51dd3c.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_d335d5187a792ac5.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_b292081314dec8ed.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_57522dfcc4e08313.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot57_hu_2ef12918db65a7b2.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="rendezvous-and-docking"&gt;Rendezvous and Docking&lt;/h2&gt;
&lt;p&gt;As soon as LARS settled in its parking orbit, the LOLS crew starts working on the rendezvous maneuver. A good encounter happens after 5 orbits around Jool.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot58.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_fb9e8b6ae2d30ceb.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_709a5161904a649.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_7218f3e6a050d71e.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_56d608ed62931ec6.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_be65c83a7496db74.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58.jpg"
 alt="screenshot58.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_ad45df91b4c74159.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_7a316f63d0d9801c.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_3cbbf699e847d250.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_a5ac2ba5338532e5.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot58_hu_6b7622b2e9184281.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="./screenshot59.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_eedab0b7fe1bab2c.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_422e444ec3131a22.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_7347eb4607e94546.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_4804e0f4833722c4.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_733ac862db474b7d.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59.jpg"
 alt="screenshot59.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_ad0a0d935ff6c088.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_d778350cfeb8bc06.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_604bcd7451a6fd67.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_98d9c6a5711d94ba.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot59_hu_bd86a3e91d3903c0.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="./screenshot61.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_3dfc97bfbf4b5528.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_6931768377e48a16.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_271e95aeb63a8565.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_daa7943f21009f89.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_4e4dae5ad302c2b1.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61.jpg"
 alt="screenshot61.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_d8e095a5f9fabf76.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_afef58fe3aa65da7.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_2cb1178367b13e75.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_77463c0d382276a0.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot61_hu_958093d13ad14422.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Once docked, the crew transfers all the fuel remaining on the LOLS main tank to LARS and ejects it. From now on, LARS will be doing the heavy lifting of the Trans-laythean Injection.&lt;/p&gt;
&lt;h2 id="trans-laythean-injection-and-laythean-orbit-injection"&gt;Trans-laythean Injection and Laythean Orbit Injection&lt;/h2&gt;
&lt;p&gt;(I mentioned early on that rendezvous in Jool orbit proved to be a mistake. The why might become more clear from this picture.)&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot62.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_63832e069a4e305.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_4a1a53d6355e0ef2.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_a36bfbdc095e3343.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_d2d3121208d571c4.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_5ad8cb29150aad6a.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62.jpg"
 alt="screenshot62.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_ca911c60fe201983.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_93ef4785e2cb05fd.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_4efecc59cd6892fb.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_5a968c8ed6241aef.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot62_hu_71cc727444aa612d.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The two small extra boosters added to LOLS made it a bit too much for the Clamp-O-Tron docking port to hold. When LARS ignited its engine for TLI, the centre of mass shifted slightly but enough to make LOLS wobble and put on too much stress on the docking port, which then broke. (Thankfully I had quicksaved just before the burn and was able to recover.)&lt;/p&gt;
&lt;p&gt;The crew slowly pushes on the throttle to ease on the clamp-o-tron stress. Same thing during LOI: easy on the throttle. The crew and mission control also agreed to skip the aerobreaking, the rationale being that Laythe’s atmosphere is low (55Km) and not very dense so it would probably not help them all that much and they did not want to put on more stress on the docking port.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot63.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_55cbd95555cfba94.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_d06ada2911bd38c1.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_cbc9bac17af0e2cf.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_56923ff90faf4515.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_b27a24603d984eed.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63.jpg"
 alt="screenshot63.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_58aa8cbe5491c20b.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_2862258ad489c27.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_a3c9b71f8dfdee72.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_2a6fddf39de75094.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot63_hu_f25623cb85359424.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="undocking-lols-and-landing"&gt;Undocking LOLS and Landing&lt;/h2&gt;
&lt;p&gt;At this point, the crew transfers fuel from the LARS main tank to the three LOLS tanks and undocked. Time to land.&lt;/p&gt;
&lt;p&gt;Laythe is touch to land for two basic reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It’s almost entirely made of oceans, with some relatively small islands and continents; and also&lt;/li&gt;
&lt;li&gt;The islands and continents are rough! Lots of mountains and steep slopes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The crew ultimately picked the continent that looks sort of like a large ‘C’ with an internal sea.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot66.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_64033c83419f8164.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_fa10c98765a83076.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_c653adca49db9fe9.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_c488a244d7459723.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_36e529ea5c846e6e.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66.jpg"
 alt="screenshot66.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_4e32b3815a0f2485.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_fb4a8824b4778b12.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_7ae59354ceececaa.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_36740859ce9102bd.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot66_hu_79478b6990a1845e.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;(It took me a few tries and one big scare as just before landing I noticed a deep valley just ahead.)&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot67.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_b01371b13c4db757.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_2bb1f5907b1b85ae.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_c8cd47cd718ceffc.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_a18d970da5f8e7d.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_aa68c72fd20e1169.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67.jpg"
 alt="screenshot67.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_d32e026fb86765b6.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_64b768dd6e433ae9.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_5f3518b3bd2488b1.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_b6a8ae552942a2b8.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot67_hu_33048eb30a26fa6b.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LMP Donbro Kerman uses the retro rockets plus the parachutes to slow the LOLS down and slowly descend.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot68.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_c3fb9edf0831bb8f.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_1b7113a751a73d56.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_caa0ef0ed9deca94.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_624c3d9bdd477681.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_fcf7b57ceb8d0b92.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68.jpg"
 alt="screenshot68.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_7398ba1fa0b3f649.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_33c1a932ae1fd1d8.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_334359de885efb11.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_7d79fc38f9007ece.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot68_hu_50ef757b4ed2478f.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;He also has to rotate LOLS around to make sure it lands in a way to prevent it from tumbling over the slope.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot69.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_13c3c712a6c138f6.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_953fd6d48d36acde.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_e3f2ed0ef73cbee3.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_ac9f99d87673a233.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_2152902e39fa5cae.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69.jpg"
 alt="screenshot69.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_a5b2bafab20343ff.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_f36fe4ed0f212185.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_8fae458c6c01efad.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_bdf6a54c15fce268.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot69_hu_7f7c785450ec325a.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It all ends well though, as Commander Roley Kerman steps out of LOLS and steps on Laythe.&lt;/p&gt;
&lt;h2 id="laythe-base"&gt;Laythe Base&lt;/h2&gt;
&lt;p&gt;The crew has to remain on the surface until the next interplanetary window before they could go back to Kerbin, which means: time for some science! With some gravioli detection, temperature and barometric measurements, EVA reports, crew reports, Mystery Goo™ observation, and some surface samples, the LOLS crew gets well over a thousand science points.&lt;/p&gt;
&lt;p&gt;And they had some fun too, like this little night rave or whatever. This is Donbro standing by the lander legs while Tomner jumps by the flag.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot71.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_582ccadc85733f5e.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_42b2bc31ee0cd5e7.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_19204296ff8df0ad.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_16f367ccf2a2bdfe.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_60f0bcfd0cefbf58.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71.jpg"
 alt="screenshot71.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_7c9241ff6b757254.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_f9581e5f8bf95fcc.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_6296e21f0652b4d3.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_f5ebbe5be0cd8457.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot71_hu_9719d6a711086b0c.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Commander Roley was asleep and missed the shenanigans. Probably for the best.&lt;/p&gt;
&lt;h2 id="takeoff-and-rendezvous"&gt;Takeoff and rendezvous&lt;/h2&gt;
&lt;p&gt;After a couple of years on Laythe, the crew was eager to go back home.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot73.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_2bf1fdd4f7754a3d.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_57c8bb61663d72f.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_f9437db1426dbeb2.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_adcc1b92865738e1.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_5b597d6ab7e0984a.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73.jpg"
 alt="screenshot73.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_e5b05887a3e170b6.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_3c12ffae3a468518.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_e2ecaf1f07e5ecb.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_1ab7464e962b65cf.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot73_hu_a87278401cd432c6.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;LOLS rendezvous with LARS, which patiently waited all these years in orbit.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot74.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_b9395f24e7bad88b.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_a7db7d42962a6b1.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_2c8a82a19e50caca.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_c450b6808c8838e6.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_d635f3633d82f578.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74.jpg"
 alt="screenshot74.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_8fd1f160a376b52d.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_e5b65bc51e07253c.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_c6ec67f0c8f4a2fd.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_973d04dd6c157c1b.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot74_hu_e9971d7ba47203ac.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="./screenshot75.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_39d580b04a2c6013.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_747953eec06443e6.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_3cd471e5b9b97c5e.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_cdf51204d3f3bed.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_b0ffd79a844b28d7.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75.jpg"
 alt="screenshot75.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_622be8af09dcc750.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_d65d2559ed22a525.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_f498bd307d38a830.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_bb63476201a24274.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot75_hu_53bc90b9cb6f5f86.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
&lt;a href="./screenshot77.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_6b207e58b83b316e.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_63b4de355f845c8a.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_7e9c2c89b5ec2075.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_ed6a8c78a920e87c.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_bf2e7eefe3019322.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77.jpg"
 alt="screenshot77.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_b3d824d3c7d91317.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_5ff98d3995bb4faa.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_5c18a8e35fc5af51.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_cc7b25d2df977596.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot77_hu_6b8a6cac3c4fd98b.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The crew now transfers the remaining fuel from the LOLS tank to LARS’ and jettisons it. Time to go home.&lt;/p&gt;
&lt;h2 id="the-jorney-home"&gt;The jorney home&lt;/h2&gt;
&lt;p&gt;The jorney back is largely uneventful, starting with the transfer from Laythe back to Jool. Now that they got rid of the LOLS tanks, the Clamp-O-Tron docking port is more than capable of holding the capsule in place.&lt;/p&gt;
&lt;p&gt;Back in orbit around Jool, LARS is GO for Trans-Kerbal Injection.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot78.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_1ee6c4aef2124c70.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_4b6404ac37b846c3.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_63aeb59e10ad8562.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_f35788319224ef47.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_8d797154c2c063dd.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78.jpg"
 alt="screenshot78.jpg"
 loading="lazy"
 decoding="async" width="1024" height="626" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_37641369228c31f8.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_49930f11a90c618c.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_7115ede729faae28.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_de3c592d79d663b5.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot78_hu_5c0814459a60db36.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And the long journey back to Kerbin is now underway.&lt;/p&gt;
&lt;p&gt;Two years later, the crew maneuvers to reach Kerbin with a periapsis of 30Km for some heavy aerobreaking.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot79.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_de972b692f7534fc.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_7fd066931d2a0c14.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_29ae07f13e566f18.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_684bad8da8d87a55.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_23ead3d19b9eeeb4.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79.jpg"
 alt="screenshot79.jpg"
 loading="lazy"
 decoding="async" width="1024" height="626" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_d69facf0809f5bd7.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_d80fe13facaf13a2.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_7ab6490c7d48c292.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_49dfa08add8a7a1c.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot79_hu_8cb29ee9d1ed616c.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;They reemerge from Kerbal atmosphere with an apoapsis of almost 2,000Km and running of fumes. The remaining fuel is then used to lower the apoapsis a little bit without changing the periapsis too much. This made sure that on the second periapsis pass, the stack will not make it out of the atmosphere again.&lt;/p&gt;
&lt;p&gt;At this point, mission control tries for a bonus mission objective. They plan to land both the LOLS capsule &lt;em&gt;and&lt;/em&gt; the LARS rockets at the same time and try to recover the LARS hardware costs. After they stop burning in the reentry, the LOLS capsule undocks from LARS and mission control constantly switch between the two to try and land both.&lt;/p&gt;
&lt;p&gt;Unfortunately, saving the LARS was a failure. As soon as the parachutes opened, LARS disintegrated, leaving only the RCS tank and docking port to slowly land.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot80.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_888567b5743604a8.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_ff228601518594c1.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_c264ec7602ca9f53.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_2647ec3ad28a70e8.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_46f0190b7bfc9d9c.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80.jpg"
 alt="screenshot80.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_7609c7aae547d168.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_94d60886f7f1233e.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_2679541a0efd189b.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_930fad68a8e13670.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot80_hu_27d9ceae294086.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The LOLS capsule on the other hand had a beautiful descent.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot82.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_d31a3e23415bb14d.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_f362a67df314e64a.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_4a25a80ec3543ae4.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_21d87daedfe45d99.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_6e2afbdce5b026e.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82.jpg"
 alt="screenshot82.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_cd3b0dfda8f34e63.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_5f609e5086715cf3.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_47000a7a66d62927.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_cd46339b77bc838.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot82_hu_80d56951bb315707.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After LOLS splashed down, the capsule, its science payload, and its crew were successfuly recovered. Mission accomplished!&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot83.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_4fc45dba71713b78.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_b2f9fa224c546878.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_c747f2dd1b74df8.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_84880c325286c521.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_a7feff0d8d7079fc.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83.jpg"
 alt="screenshot83.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_33da35ba2a910a61.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_b5f06975efbd16fc.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_415d08ff15cf319e.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_40acfa0477b102bb.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot83_hu_44111c47d82a8078.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The remaining LARS parts were found to not be recoverable.&lt;/p&gt;
&lt;p&gt;&lt;a href="./screenshot84.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_689489dce4de60e1.webp 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_5e9998e6c2c372e3.webp 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_cc1ca77d8e5e9c45.webp 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_9d6dcc80c397662d.webp 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_aa41899de168d4e6.webp 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84.jpg"
 alt="screenshot84.jpg"
 loading="lazy"
 decoding="async" width="1024" height="640" srcset="https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_cdb206129ddb3c16.jpg 320w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_6bc8d40982fef6a.jpg 480w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_86fb4c9d13baa09e.jpg 640w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_165b006838658b04.jpg 800w, https://rselbach.com/from-kerbin-to-laythe-and-back/screenshot84_hu_f2aed908df75565e.jpg 960w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="a-few-closing-remarks"&gt;A few closing remarks&lt;/h2&gt;
&lt;p&gt;I was pretty pleased to have completed my longest mission yet. Still, a few things I would have changed.&lt;/p&gt;
&lt;p&gt;I wasted fuel by not aerobreaking in Laythe. Also, I am sure there is an optimal altitude that I could have reached Jool for a better aerobreaking. I did try several but going even a bit under 105Km seemed to be too much as the ship never came back out of the atmosphere. A little over 110Km and the apoapsis would end up in the millions of kilometers.&lt;/p&gt;
&lt;p&gt;Another thing is that I made the hop in Jool orbit between Kerbin and Laythe. I am not sure it is possible but I think I will try in the future to go directly to Laythe. It must be terribly difficult but a scenario would be to arrive in such an angle that you could aerobreak in Jool and come out with a path leading to Laythe directly, hopefully with some extra aerobreaking. This would be optimal but would require a lot of precision. I don’t think I could make it but it sure sounds like it would be fun.&lt;/p&gt;
&lt;p&gt;I should also have scheduled the LOLS-LARS rendezvous to happen in orbit around Laythe instead of Jool to avoid the wobbly lander problem. The LOLS was low on fuel by that time though, so I am not really sure I could have pulled it off anyway.&lt;/p&gt;
&lt;p&gt;And I could have taken at least one more Mystery Goo™ as I could have observed it in orbit aroud Laythe for some extra points.&lt;/p&gt;
&lt;p&gt;And finally, I &lt;em&gt;love&lt;/em&gt; this game so much. You learn more every time you play it. This whole thing, including designing both stacks and doing some testing, was done over the course of a few days, mostly last weekend with the return from Laythe to Jool and then Kerbin done over a couple of nights this week.&lt;/p&gt;
&lt;p&gt;I thank Squad for this great game.&lt;/p&gt;
&lt;p&gt;May your solar panels be always unobstructed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; this whole story was created way before KSP 1.0 came out. The aerobreaking maneuvers here would not have worked due to the lack of heat shields. As well, a scientist kerbal in the crew would solve my issue above with only taking a single Mystery Goo™ as kerbal scientists can reset experiments in flight.&lt;/p&gt;
&lt;li id="fn-1"&gt;
 That would turn out to be a mistake, as we’ll see.&lt;a href="#fnref-1" rev="footnote"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-2"&gt;
 Not really, actually, as Kerbals don’t seem to care…&lt;a href="#fnref-2" rev="footnote"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-3"&gt;
 Heinlein’s actual quote was “Once you get to earth orbit, you’re halfway to anywhere in the solar system.” I suppose it can be used for Kerbin and the kerbolar system.&lt;a href="#fnref-3" rev="footnote"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-4"&gt;
 You learn that the hard way in KSP. How many a time I have arrived somewhere after warping time just to find my ship completely dead because there was no electrical power left.&lt;a href="#fnref-4" rev="footnote"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-5"&gt;
 That’s me.&lt;a href="#fnref-5" rev="footnote"&gt;↩&lt;/a&gt;
&lt;/li&gt;
&lt;li id="fn-6"&gt;
 Mission Elapsed Time&lt;a href="#fnref-6" rev="footnote"&gt;↩&lt;/a&gt;&lt;/fn&gt;&lt;/footnotes&gt;</description></item><item><title>Things I wish I knew one year ago</title><link>https://rselbach.com/things-i-wish-i-knew-one-year-ago/</link><pubDate>Fri, 29 Aug 2014 00:47:23 +0000</pubDate><guid>https://rselbach.com/things-i-wish-i-knew-one-year-ago/</guid><description>&lt;p&gt;About an year ago, I started a new project in a stealth startup. Aside from the fact that it would provide currency that could then be exchanged for goods and services, including food for me and my family, one of the biggest reasons I took the job was that I was going to get to learn a lot of new things.&lt;/p&gt;
&lt;p&gt;Now, over a year later I can say that learn I did. And I caught myself thinking of what I wish I knew back in the beginning. The following list is probably not exaustive but it’s a good example of what I’ve learned. These are mostly things I’ve dealt with on rewrites, redesigns, or are things that I still struggle with.&lt;/p&gt;
&lt;h2 id="javascript-is-magical-and-thats-not-good"&gt;JavaScript is magical and that’s not good&lt;/h2&gt;
&lt;p&gt;I already had a general idea of the issues with JavaScript, but I really failed to grasp how &lt;em&gt;magical&lt;/em&gt; JavaScript is. And when I say magical, I don’t mean the good kind that conjures fluffy rabbits inside hats. I mean evil dark magic that would make Voldermort cry. I mean magic in the sense of a language that never seems to behave just the way you think it should.&lt;/p&gt;
&lt;p&gt;I wish I knew back then how much work I would need to spend to do what seemed at first to be basic, simple stuff. I often caught myself fighting JavaScript instead of working with it.&lt;/p&gt;
&lt;h2 id="angularjs-is-awesome-but-the-web-works-on-jquery"&gt;AngularJS is &lt;em&gt;awesome&lt;/em&gt; but the web works on jQuery&lt;/h2&gt;
&lt;p&gt;I think &lt;a href="https://angularjs.org/"&gt;AngularJS&lt;/a&gt; is fantastic. Really I do. I am not at all sorry for having picked it for the project. I think it’s well-designed and logical and yes, it is magical sometimes, but often in a good way.&lt;/p&gt;
&lt;p&gt;But…&lt;/p&gt;
&lt;p&gt;I wish I knew back then how much work I would have to do making AngularJS and &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt; work together, and you &lt;em&gt;have&lt;/em&gt; to make them work together because honestly, unless you want to reinvent the wheel for every little thing, you will end up using jQuery components. We will revisit that later.&lt;/p&gt;
&lt;p&gt;So yeah, AngularJS is great but for it to work properly, you &lt;em&gt;have&lt;/em&gt; to do things the Angular way and that means turning a lot of jQuery components into AngularJS directives. Once you get the hang of it, it gets much easier and faster but there’s a bit of a learning curve there.&lt;/p&gt;
&lt;h2 id="asynchronously-have-you-think-undefined"&gt;asynchronously have You think undefined&lt;/h2&gt;
&lt;p&gt;You have to think asynchronously to work with JavaScript. For a while, it was painful and it surely added to my perception of JavaScript being magical. JavaScript is entirely asynchronous and works like a turn-based game. This can be annoying at first and I caught myself working around this in all kinds of wrong ways. I wish I knew back then what I know now so I’d embrace the async’ness from the get-go. Things would be so much easier.&lt;/p&gt;
&lt;h2 id="large-single-page-web-applications-rock-but"&gt;Large single-page web applications rock but…&lt;/h2&gt;
&lt;p&gt;Picking AngularJS as my framework of choice, I designed the web client for the project to be almost purely implemented inside the browser in JavaScript as a single-page app. This has some very good consequences, chief among them responsiveness. The application feels &lt;em&gt;fast&lt;/em&gt;. Sure it has to download a lot of JavaScript up front, but (1) this is not nearly as bad as I expected it to be and (2) with caching this is done once and that’s it. Switching from a “page” to the other is essentially instantaneous as it’s all pre-loaded AJAX parts of the page. The server actually has to do very little.&lt;/p&gt;
&lt;p&gt;But now, if I could start all over again, I would not do it like one big single-page app. It’s just too much JavaScript code and I’ve already established how I feel about that. I never have the feel that things are stable, even though there are integration tests passing all the time. I can’t explain it but it all feels like a big house of cards, like it’s all going to come tumbling down all of a sudden. I simply don’t trust the code. I would much rather use JavaScript sparingly, to do &lt;em&gt;some&lt;/em&gt; things but surely not all. I have been fixing this slowly by isolating some parts of the application as separate, but to do it for everything would require a rewrite that I don’t have the time for right now.&lt;/p&gt;
&lt;h2 id="trust-the-go-standard-lib"&gt;Trust the Go standard lib&lt;/h2&gt;
&lt;p&gt;As an alternative title, “don’t go too fancy with &lt;a href="https://golang.org"&gt;Go&lt;/a&gt;.” It’s not that I didn’t trust it, but I sure did try things that honestly didn’t make sense. I am looking right at you, &lt;a href="https://github.com/go-martini/martini"&gt;Martini&lt;/a&gt;, I had to do a lot of work to get rid of you.&lt;/p&gt;
&lt;p&gt;As it turns out, the standard library is really good. And for the things that are not in the standard lib, there are packages that are really well done and work &lt;em&gt;with&lt;/em&gt; the standard lib, not against it. I love you, &lt;a href="http://www.gorillatoolkit.org/"&gt;Gorilla Toolkit&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="never-assume-jquery-components-will-do-what-you-need"&gt;Never assume jQuery components will do what you need&lt;/h2&gt;
&lt;p&gt;The current state of JavaScript is such that there is literally—in the figurative sense—an infinite number of components out there ready for you to use. As far as I can tell, &lt;em&gt;anything&lt;/em&gt; I could ever possibly conceive of has already done and hosted on Github. There are time pickers, calendars, and even a business hours widget display ready for you.&lt;/p&gt;
&lt;p&gt;And yet.&lt;/p&gt;
&lt;p&gt;I am yet to find one single component that is flexible enough to work for my needs without extensive modifications. And it’s not like I have especially unique needs. I am talking about things that are to the best of my knowledge pretty basic. But no, they never do that one tiny thing you need them to and I spend a lot of time implementing features I need.&lt;/p&gt;
&lt;h2 id="bootstrap-responsiveness"&gt;Bootstrap responsiveness&lt;/h2&gt;
&lt;p&gt;For a long time I misunderstood how Bootstrap deals with responsive design. Trusting Bootstrap’s system is a must. It works very well. I wish I understood it better back then. It would have saved me a lot of time. And related:&lt;/p&gt;
&lt;h2 id="dont-use-an-existing-bootstrap-theme"&gt;Don’t use an existing Bootstrap theme&lt;/h2&gt;
&lt;p&gt;It’s just not worth it. Back then, we didn’t have a designer and we wanted to have our minimum viable product as quickly as possible so we went after something ready. We chose a premium theme that is very popular. It was good looking and seemed really nice. Boy do I wish I had a time machine now.&lt;/p&gt;
&lt;p&gt;We are now stuck with it. Switching to something else is something we will have to do eventually and let me tell you: it will be painful. These themes change &lt;em&gt;everything&lt;/em&gt; and not in a way that we could simply switch themes painlessly. It permeates everything.&lt;/p&gt;
&lt;h2 id="think-of-touchscreens-from-the-beginning"&gt;Think of touchscreens from the beginning&lt;/h2&gt;
&lt;p&gt;Oh boy. You do a lot of nice stuff and then one day you try it on a tablet and &lt;em&gt;nothing works&lt;/em&gt; just as your co-founder tells you that your first customer will use the app &lt;em&gt;exclusively&lt;/em&gt; on tablets. Then you start panicking and sobbing and crying and running for help. You then find out about the hack that is &lt;a href="http://touchpunch.furf.com/"&gt;jQueryUI Touch Punch&lt;/a&gt; and although it fixes some of the problems it creates new ones and then there’s &lt;a href="http://jquerymobile.com/"&gt;jQuery Mobile&lt;/a&gt; which is OMG fantastic except lo and behold it doesn’t play well together with AngularJS. And then you find some projects on Github trying to get jQuery Mobile to work alongside with AngularJS but they’re no longer maintained and won’t really work that well and…&lt;/p&gt;
&lt;p&gt;Well, it would be best to think about touch support from the get go.&lt;/p&gt;
&lt;h2 id="stop-worrying-about-the-size-of-your-javascript"&gt;Stop worrying about the size of your JavaScript&lt;/h2&gt;
&lt;p&gt;Or actually, do worry, but for the right reasons. One of the things I learned is that the web is full of outdated and/or otherwise uninformed advice.&lt;/p&gt;
&lt;p&gt;Stop laughing.&lt;/p&gt;
&lt;p&gt;Yes, it’s obvious, but still. With little experience with web frontend programming, I used to worry about the size of my JavaScript code. And the web doesn’t really help. Search a bit and you will find people telling you how horrendous it would be to download 100Kb of JavaScript and how the only way to save your customers is to access files from popular CDNs.&lt;/p&gt;
&lt;p&gt;It turns out that’s kind of like cargo cult programming. It’s just a bunch of people repeating something they heard somewhere. Things change. We measured them.&lt;/p&gt;
&lt;p&gt;Concatenating and minifying our files and serving them gzip’d and with aggressive caching is actually faster. Even concatenation is about to become irrelevant now that &lt;a href="http://www.chromium.org/spdy"&gt;SPDY&lt;/a&gt; is becoming more common (IE11 now supports SPDY3 and the upcoming versions of Safari for OSX and iOS will as well.)&lt;/p&gt;
&lt;p&gt;The first access to our app downloads almost 1MB of JavaScript code and CSS files. It sounds horrible but it’s really fast, even on bad 3G with with &lt;code&gt;defer&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt;. Speaking of which.&lt;/p&gt;
&lt;p&gt;Most of the advice repeated as a mantra on the web is from before browser support for &lt;code&gt;&amp;lt;script defer&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;script async&amp;gt;&lt;/code&gt; was widespread.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I think that’s about it. Now all I need is a time machine.&lt;/p&gt;</description></item><item><title>More on validating with Go</title><link>https://rselbach.com/more-on-validating-with-go/</link><pubDate>Sat, 03 May 2014 04:29:55 +0000</pubDate><guid>https://rselbach.com/more-on-validating-with-go/</guid><description>&lt;p&gt;A few days ago &lt;a href="http://robteix.com/blog/2014/04/29/package-validate/"&gt;I posted&lt;/a&gt; about a new Go package called &lt;a href="https://github.com/go-validator/validator"&gt;validator&lt;/a&gt; that we initially developed for our own internal use at project7.io but what fun is internal stuff, right? So we opensourced it.&lt;/p&gt;
&lt;p&gt;Then &lt;a href="https://twitter.com/mholt6"&gt;Matthew Holt&lt;/a&gt; pointed me to &lt;a href="https://github.com/martini-contrib/binding/issues/9"&gt;an ongoing discussion&lt;/a&gt;&lt;br&gt;
on validation happening on &lt;a href="https://github.com/martini-contrib"&gt;martini-contrib’s github&lt;/a&gt;. By the way, if you don’t know martini yet, &lt;a href="https://github.com/go-martini/martini"&gt;go rectify that right now&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And today I learned about &lt;a href="https://github.com/pengux/check"&gt;check&lt;/a&gt;, which takes a completely different&lt;br&gt;
approach to data validation than the one we took, which by the way is totally fine and you should check it out.&lt;/p&gt;
&lt;p&gt;Good to know we were not the only ones with the problem. Although I’m happy with how our package turned out, I kind of wish&lt;br&gt;
I had found these other guys earlier. Could have shared some code or just ideas.&lt;/p&gt;</description></item><item><title>[ANN] Package validator</title><link>https://rselbach.com/ann-package-validator/</link><pubDate>Tue, 29 Apr 2014 19:22:35 +0000</pubDate><guid>https://rselbach.com/ann-package-validator/</guid><description>&lt;p&gt;The thing about working in a startup under stealth mode is you can’t often talk about what you’re doing. Thankfully, from time to time, an opportunity appears that lets us at least share something tangential.&lt;/p&gt;
&lt;p&gt;A large part of our project at project7.io involves receiving data from a client (generally in JSON) for processing. This involves unmarshaling the JSON into a &lt;code&gt;struct&lt;/code&gt;, something that Go does very well. But then comes the boring part: checking that the information sent from the client is correct and complete before doing anything with it.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The boring life of validating stuff&lt;/em&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Username&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Age&lt;/span&gt; &amp;gt; &lt;span style="color:#ae81ff"&gt;18&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Foo&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; len(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Bar&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Baz&lt;/span&gt;) &amp;gt; &lt;span style="color:#ae81ff"&gt;8&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We had to do this often and for a large number of different structs and sometimes a &lt;code&gt;struct&lt;/code&gt; gained a new field and we had to go back and see that it was being properly validated everywhere. It was so boring that we ended up writing something to make it easier for us. We’ve been using this for a while and now we decided to open source it in the hopes that it might be useful for others.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/go-validator/validator"&gt;Package validator&lt;/a&gt; implements validation of variables. Initially we had implemented &lt;a href="http://json-schema.org/"&gt;JSONschema&lt;/a&gt; but we don’t always deal with JSON, we also get data as XML and sometimes as &lt;code&gt;form-encoded&lt;/code&gt;. So we changed our approach and went right to the &lt;code&gt;struct&lt;/code&gt; definitions.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A struct definition with some validation rules attached&lt;/em&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;T&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;A&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`validate:&amp;#34;nonzero&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;B&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`validate:&amp;#34;nonzero,max=10&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;C&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;Ca&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`validate:&amp;#34;min=5,max=10&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;Cb&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`validate:&amp;#34;min=8, regexp:^[a-zA-Z]+&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This allowed us to attach validation rules right to the data structure definitions. Then instead of large, boring list of &lt;code&gt;if&lt;/code&gt; statements, we were able to validate in single function call.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Validating an instance of a struct&lt;/em&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;valid&lt;/span&gt;, &lt;span style="color:#a6e22e"&gt;_&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;validator&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Validate&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;); !&lt;span style="color:#a6e22e"&gt;valid&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// not valid, so return an http.StatusBadRequest of something&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="multiple-rules-for-different-situations"&gt;Multiple rules for different situations&lt;/h2&gt;
&lt;p&gt;We often use the same &lt;code&gt;struct&lt;/code&gt; to deal with different data coming from the client. Sometimes we only care about one or two of the fields in one scenario, so we also supported multiple rules like, say&lt;/p&gt;
&lt;p&gt;&lt;em&gt;A struct with two different sets of rules&lt;/em&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;type&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;T&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;A&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`foo:&amp;#34;nonzero&amp;#34; bar:&amp;#34;min=5,max=10&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;B&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;string&lt;/span&gt; &lt;span style="color:#e6db74"&gt;`bar:&amp;#34;nonzero&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;In scenario &lt;code&gt;foo&lt;/code&gt;, we need &lt;code&gt;A&lt;/code&gt; to be non-zero, but we don’t care for what is in &lt;code&gt;B&lt;/code&gt;. In the &lt;code&gt;bar&lt;/code&gt; scenario, however, we need &lt;code&gt;B&lt;/code&gt; to be non-zero and the value of &lt;code&gt;A&lt;/code&gt; to be between 5 and 10, inclusive. To validate, we then make &lt;code&gt;validator&lt;/code&gt; use a different tag name for each case&lt;/p&gt;
&lt;p&gt;&lt;em&gt;WithTag() FTW&lt;/em&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;T&lt;/span&gt;{&lt;span style="color:#a6e22e"&gt;A&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;validator&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;WithTag&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;Validate&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// valid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;validator&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;WithTag&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;).&lt;span style="color:#a6e22e"&gt;Validate&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;t&lt;/span&gt;) &lt;span style="color:#75715e"&gt;// invalid&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can also change the tag by using &lt;code&gt;SetTag&lt;/code&gt; with will then make the tag name persistent until changed again by another call to &lt;code&gt;SetTag&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Please refer to &lt;a href="http://godoc.org/gopkg.in/validator.v1"&gt;http://godoc.org/gopkg.in/validator.v1&lt;/a&gt; for a lot more documentation, use cases, and how to access the individual validation errors found for each field.&lt;/p&gt;</description></item><item><title>Easter Eggs in the Apollo Program</title><link>https://rselbach.com/easter-eggs-in-the-apollo-program/</link><pubDate>Sun, 26 Jan 2014 02:57:28 +0000</pubDate><guid>https://rselbach.com/easter-eggs-in-the-apollo-program/</guid><description>&lt;p&gt;On the way to the Moon, the Apollo crew and ground control needed precise information about the position, speed, and acceleration of the stack. Getting that information was a feat of engineering involving very clever solutions that deserve a post of their own, but the easiest piece of the puzzle was the position.&lt;/p&gt;
&lt;p&gt;To figure out where exactly the Apollo was at any given moment, the crew used a &lt;a href="http://history.nasa.gov/computers/Ch2-5.html"&gt;guidance computer&lt;/a&gt; designed by the MIT along with a &lt;a href="http://timeandnavigation.si.edu/multimedia-asset/apollo-sextant-and-eye-piece"&gt;sextant&lt;/a&gt;, not much unlike the ones used for centuries by sea captains.&lt;/p&gt;
&lt;p&gt;The CM pilot would pick a specific star (or planet sometimes) that could be placed within view of the sextant optics, he’d then align the sextant viewport with the chosen star, and then tell the guidance computer to calculate the position by telling it which star he had just pointed the sextant to.&lt;/p&gt;
&lt;p&gt;The astronauts had a table of stars they could use, each accompanied by an octal code that could be fed to the computer. In this list, along with well-known stars—such as Sirius (15), Rigel (12), and the Sun (46)—were three oddly named stars.&lt;/p&gt;
&lt;p&gt;&lt;a href="./apollo1_crew.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_795e8a9059f99d8a.webp 320w, https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_8f997fa814e75adc.webp 480w, https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_9736bf3a50d57989.webp 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew.jpg"
 alt="apollo1_crew"
 loading="lazy"
 decoding="async" width="640" height="480" srcset="https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_b2fb558f2e242967.jpg 320w, https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_aa6ebf886680f81b.jpg 480w, https://rselbach.com/easter-eggs-in-the-apollo-program/apollo1_crew_hu_9eaba33ebbee470c.jpg 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;(Credit: Nasa)&lt;/p&gt;
&lt;p&gt;While helping with the development of this system, the crew of Apollo I decided to have a little fun and added references to themselves by changing the names of some stars to parts of their own names spelled backwards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Navi&lt;/em&gt; (03) for Gus Grisson’s middle name (Ivan)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Regor&lt;/em&gt; (17) for Roger Chaffee&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Dnoces&lt;/em&gt; (20) for Edward White II.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In reality, &lt;em&gt;Navi&lt;/em&gt; was &lt;a href="http://en.wikipedia.org/wiki/Gamma_Cassiopeiae"&gt;&lt;em&gt;γ Cassiopeiae&lt;/em&gt;&lt;/a&gt;, &lt;em&gt;Regor&lt;/em&gt; was &lt;a href="http://en.wikipedia.org/wiki/Gamma_Velorum"&gt;&lt;em&gt;γ Velorum&lt;/em&gt;&lt;/a&gt;, and &lt;em&gt;Dnoces&lt;/em&gt; was &lt;a href="http://en.wikipedia.org/wiki/Talitha_Borealis"&gt;&lt;em&gt;ι Ursae Majoris&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After the crew lost their lives in a tragic fire—the first fallen in the American space manned program—the people at NASA kept those names in all their documentation and literature.&lt;/p&gt;</description></item><item><title>Exemplo de texto de MBA</title><link>https://rselbach.com/exemplo-de-texto-de-mba/</link><pubDate>Wed, 07 Aug 2013 18:31:05 +0000</pubDate><guid>https://rselbach.com/exemplo-de-texto-de-mba/</guid><description>&lt;p&gt;Um amigo publicou um texto no Facebook hoje que me lembrou do MBA. Vou deixar o texto aqui, como exemplo do tipo de texto comum em escolas de negócio e deixar o julgamento ao leitor:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ASSALTO MUITO INTERESSANTE.&lt;/p&gt;
&lt;p&gt;Durante um assalto, em Guangzhou, China, o assaltante de bancos gritou para todos no banco: “Não se mova O dinheiro pertence ao Estado Sua vida pertence a você…”&lt;/p&gt;
&lt;p&gt;Todo mundo no banco deitou-se calmamente no chão. Isso é chamado de “Mudando o conceito mental” Mudar a forma convencional de pensar.&lt;/p&gt;
&lt;p&gt;Quando uma senhora apresentou-se sobre a mesa provocativamente, em que o ladrão gritou para ela: “Por favor, seja civilizada isto é um assalto e não um estupro!”&lt;/p&gt;
&lt;p&gt;Isso é chamado de “ser profissional” Concentre-se apenas no que você está treinado para fazer!&lt;/p&gt;
&lt;p&gt;Quando os assaltantes voltaram para casa, o ladrão mais jovem (MBA treinee), disse ao ladrão mais velho (que só completou seis anos na escola primária): “Big brother, vamos contar o quanto nós temos.”&lt;/p&gt;
&lt;p&gt;O assaltante mais velho rebateu e disse: “Você é muito estúpido. Há tanto dinheiro que vai nos levar muito tempo para contar. Hoje à noite, o noticiário da TV vai nos dizer o quanto nós roubamos do banco…!”&lt;/p&gt;
&lt;p&gt;Isso é chamado de “experiência”. Hoje em dia, a experiência é mais importante do que as qualificações do papel!&lt;/p&gt;
&lt;p&gt;Depois que os ladrões saíram, o gerente do banco disse ao supervisor bancário para chamar a polícia rapidamente. Mas o supervisor lhe disse: “Espere, vamos retirar U$ 10 milhões do banco para nós mesmos e adicioná-lo aos 70 milhões dólares que já foram desviados do banco”.&lt;/p&gt;
&lt;p&gt;Isso é chamado de “nadar à favor da maré.” Convertendo uma situação desfavorável para a sua vantagem!&lt;/p&gt;
&lt;p&gt;O supervisor diz: “Vai ser bom para nós se houver um assalto a cada mês.”&lt;/p&gt;
&lt;p&gt;Isso é chamado de “morte do tédio”. Felicidade pessoal é mais importante do que o seu trabalho.&lt;/p&gt;
&lt;p&gt;No dia seguinte, o noticiário da TV informou que U$ 100 milhões, foram retirados do banco. Os ladrões contaram e contaram e contaram, mas eles só podiam contar o montante de U$20 milhões. Os ladrões estavam muito irritados e reclamaram: “Nós arriscamos nossas vidas e só levamos 20 milhões de dólares. O gerente do banco levou 80 milhões de dólares com apenas um estalar de seus dedos. Parece que é melhor ser educado do que ser um ladrão..!”&lt;/p&gt;
&lt;p&gt;Isso é chamado de “Conhecimento que vale tanto quanto ouro!”&lt;/p&gt;
&lt;p&gt;O gerente do banco estava sorrindo feliz porque suas perdas no mercado de ações foram agora cobertas por este roubo.&lt;/p&gt;
&lt;p&gt;Isso é chamado de “Aproveitando a oportunidade.” Ousadia para assumir riscos!&lt;/p&gt;
&lt;p&gt;Então, quem são os ladrões reais aqui?&lt;/p&gt;
&lt;p&gt;Conde de Kakflour&lt;/p&gt;
&lt;/blockquote&gt;</description></item><item><title>Join us on App.Net</title><link>https://rselbach.com/join-us-on-app-net/</link><pubDate>Sat, 18 May 2013 18:08:49 +0000</pubDate><guid>https://rselbach.com/join-us-on-app-net/</guid><description>&lt;p&gt;I liked the idea behind App.Net (or ADN for the initiated) from the start; I’ve happily signed up during the initial funding effort and before it even existed. It is quite like Twitter, although it does have some pretty interesting API advantages that allow clients to do things that are not possible in Twitter such as creating private chat rooms (with &lt;a href="http://patter-app.net"&gt;Patter&lt;/a&gt;.) I found a text by Matt Gemmell, &lt;a href="http://mattgemmell.com/2013/04/05/app-net-for-conversations/"&gt;App.Net for conversations&lt;/a&gt;, that sums it up nicely:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The interesting part, though, is what you &lt;em&gt;won’t&lt;/em&gt; be used to from Twitter. There are no ads, anywhere. Because it’s a paid service, there’s no spam at all; I’ve certainly never seen any. There’s an active _and happy _developer community, which ADN actually &lt;a href="http://thenextweb.com/insider/2013/03/01/app-net-developer-payouts/"&gt;financially rewards&lt;/a&gt;. There’s a rich, modern, &lt;a href="http://developers.app.net/"&gt;relentlessly improved API&lt;/a&gt;. And again because it’s a paid service, there’s a commensurately (and vanishingly) low number of Bieber fans, teenagers, illiterates, and sociopaths.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But the real difference I notice is in the conversations. On Twitter, the back-and-forth tends to be relatively brief, not only in terms of the 140-character limit, but also the number of replies. There’s a certain fire-and-forget sensibility to Twitter; it’s a noticeboard rather than a chatroom. Then there’s the keyword-spam (woe betide the person who mentions iPads, or MacBooks, or broadband, or &lt;a href="https://twitter.com/ddfreyne/status/319185269549051904"&gt;just about &lt;em&gt;anything&lt;/em&gt;&lt;/a&gt;). Oh, and let’s not forget the fact that any malcontent with internet access can create an account (or two, or ten) in seconds. Not a happy mixture.&lt;/p&gt;
&lt;p&gt;I’d add that there seems to be less of a popular clique on ADN. Popular users seem to be much more engaging with “regular people” than on Twitter. And there’s the developers… although most of the rush is now behind us, it was fun to follow the developers working on ADN clients. It was a very collaborative effort, with alpha builds floating around and discussions about whether this or that should be done in a certain way.&lt;/p&gt;
&lt;p&gt;As for the developers of ADN proper, well, you can try asking ADN CEO and Founder &lt;a href="https://alpha.app.net/dalton"&gt;Dalton&lt;/a&gt; something to see if he’ll answer you in about 30 seconds. He actually does. 🙂&lt;/p&gt;
&lt;p&gt;It all feels like a big community where everyone feels a bit like they own the place as well and want it to thrive. Again I think Matt is on the money on why this is so:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We value what we pay for. We not only pay for things which we deem to be of value, but we &lt;em&gt;also&lt;/em&gt; retrospectively &lt;em&gt;assign and justify value&lt;/em&gt; based on what we’ve paid. Any consumer is familiar with the simple psychology of cost equating as much to value &lt;em&gt;after&lt;/em&gt; the transaction as value does to cost beforehand (likely &lt;em&gt;moreso&lt;/em&gt;, from my own experience). At its core, I don’t think that the reason for the noticeably different, warmer, more discursive “feel” of ADN is any more complicated than that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I personally love the service and I think you should consider it too. There is a free tier account that allows you to follow up to 40 people for free, as long as you’re invited by a current user. If you’re interested, I have a few invites.&lt;/p&gt;
&lt;p&gt;Feel free to comment on this post by using &lt;a href="https://plus.google.com/113816409376221973603/posts/PUCrdNaNHsj"&gt;this Google+ thread&lt;/a&gt; or also by talking to me on, where else, ADN, where I’m &lt;a href="https://alpha.app.net/robteix"&gt;@robteix&lt;/a&gt;. And of course Twitter isn’t going anywhere and &lt;a href="http://twitter.com/robteix"&gt;I’m there too&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Why is Change So Difficult?</title><link>https://rselbach.com/why-is-change-so-difficult/</link><pubDate>Tue, 30 Apr 2013 22:37:30 +0000</pubDate><guid>https://rselbach.com/why-is-change-so-difficult/</guid><description>&lt;p&gt;&lt;a title="About" href="https://rselbach.com/about/" target="_blank"&gt;I have long been a bit of a nomad&lt;/a&gt;. What is interesting to me is that I don’t really like change too much. &lt;strong&gt;I am a creature of habit&lt;/strong&gt; and yet, here I am.&lt;/p&gt;
&lt;p&gt;&lt;a href="./Brazil_argentina_allegory.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/why-is-change-so-difficult/Brazil_argentina_allegory_hu_1cf88eee909fa282.webp 320w, https://rselbach.com/why-is-change-so-difficult/Brazil_argentina_allegory_hu_7fbe2e10abf4ef4e.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/why-is-change-so-difficult/Brazil_argentina_allegory.jpg"
 alt="Brazil_argentina_allegory"
 loading="lazy"
 decoding="async" width="550" height="514" srcset="https://rselbach.com/why-is-change-so-difficult/Brazil_argentina_allegory_hu_f51c4cc65ffbdf4f.jpg 320w, https://rselbach.com/why-is-change-so-difficult/Brazil_argentina_allegory_hu_c43fcec5d9219fe8.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Four years ago, I moved to Argentina due to professional reasons. At the time, I had my mind set that I was going to give it a try and come back if it didn’t work out. &lt;strong&gt;It was a hard decision too, since my wife and I were pregnant of our daughter.&lt;/strong&gt; But we took the plunge anyway. We knew we could always come back and as Terry Pratchett wrote in one of his &lt;strong&gt;Discworld&lt;/strong&gt; novels —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Why do you go away? So that you can come back. So that you can see the place you came from with new eyes and extra colors. And the people there see you differently, too. Coming back to where you started is not the same as never leaving.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I am now leaving, returning to my old country. It is mostly due to the economic situation in Argentina. I was able to mostly protect myself by keeping my savings in Brazil as well as transferring as much money as I could from Argentina. But still, I need to make a move. Our daughter is growing up and should start school soon so if we’re going, we should go now.&lt;/p&gt;
&lt;p&gt;I officially &lt;a title="Goodbye Intel" href="https://rselbach.com/2013/04/22/goodbye-intel/" target="_blank"&gt;left my job&lt;/a&gt; about a week ago. My wife and daughter left soon after as I stayed to put our affairs in order — selling stuff, closing accounts, the whole thing — and now it’s my time. I am leaving in the morning.&lt;/p&gt;
&lt;p&gt;Being here alone made me think about — and fear — the future. &lt;strong&gt;Why is change always so difficult?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It isn’t that I have doubts about the decision itself. &lt;strong&gt;I know it is the best move for us.&lt;/strong&gt; But I still fear it.&lt;/p&gt;
&lt;p&gt;Will I ever find another job again? Am I too old to do these things? Will my daughter learn to speak the language? Rationally and logically I know the answers to these questions. But the fear of change is there.&lt;/p&gt;
&lt;p&gt;What I learned — or at least what I tell myself — is that it’s fine to be afraid. &lt;strong&gt;Being afraid just means you are about to do something brave&lt;/strong&gt;. Or stupid. Starting tomorrow, I find out which.&lt;/p&gt;
&lt;p&gt;_Adiós, Argentina.** **_See you all on the other side.&lt;/p&gt;</description></item><item><title>Goodbye Intel</title><link>https://rselbach.com/goodbye-intel/</link><pubDate>Mon, 22 Apr 2013 16:06:20 +0000</pubDate><guid>https://rselbach.com/goodbye-intel/</guid><description>&lt;p&gt;Eight years ago I joined a great company in &lt;a href="http://intel.com"&gt;Intel&lt;/a&gt;. It has been a great ride but as of a few minutes ago, I have informed my manager that I quitting my job for personal reasons.&lt;/p&gt;
&lt;p&gt;A couple of years ago, I relocated to the software development centre in Cordoba, Argentina. It was always meant to be a temporary assignment but it ended up being longer than my family and I ever thought. And as our daughter grows up, it becomes ever more difficult to engage in international relocations, so my wife—who also works at Intel, by the way—and I decided that we needed to act. We set a hard deadline for the move and stuck to it. This is it.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/goodbye-intel/DSC02120-580x435_hu_6e7879116979176a.webp 320w, https://rselbach.com/goodbye-intel/DSC02120-580x435_hu_6581624d6b556416.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/goodbye-intel/DSC02120-580x435.jpg"
 alt="My wife, daugher, and I at the Intel Minigolf side during the last Kids@work Day"
 loading="lazy"
 decoding="async" width="580" height="435" srcset="https://rselbach.com/goodbye-intel/DSC02120-580x435_hu_6525eeb17d7f583e.jpg 320w, https://rselbach.com/goodbye-intel/DSC02120-580x435_hu_8bc965ec9db3efd2.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;My wife and daughter will be flying to Brazil in two weeks and I should follow some short time later, once I’m done closing everything behind. We’ll be relocating to Curitiba, where my wife and I first met 13 years ago, so it’s fitting.&lt;/p&gt;
&lt;p&gt;So this is it. Good bye, Intel, it’s been fun and I wish you all the best.&lt;/p&gt;</description></item><item><title>Linux Kernel Linked List Explained</title><link>https://rselbach.com/linux-kernel-linked-list-explained/</link><pubDate>Fri, 04 Jan 2013 20:13:22 +0000</pubDate><guid>https://rselbach.com/linux-kernel-linked-list-explained/</guid><description>&lt;p&gt;I appreciate beautiful, readable code. And if someone were to ask me for an example of beautiful code, I’ve always had the answer ready: the linked list implementation in the Linux kernel.&lt;/p&gt;
&lt;p&gt;The code is &lt;em&gt;gorgeous&lt;/em&gt; in its simplicity, clarity, and amazing flexibility. If there’s ever a museum for code, this belongs there. It is a masterpiece of the craft.&lt;/p&gt;
&lt;p&gt;I was just telling a friend about it while we talked about beautiful code and he found this piece that I share here: &lt;a href="http://isis.poly.edu/kulesh/stuff/src/klist/"&gt;Linux Kernel Linked List Explained&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Container changes in C++11</title><link>https://rselbach.com/container-changes-in-c11/</link><pubDate>Sat, 15 Dec 2012 14:41:36 +0000</pubDate><guid>https://rselbach.com/container-changes-in-c11/</guid><description>&lt;p&gt;The recently approved C++11 standard brings a lot of welcome changes to C++ that modernize the language a little bit. Among the many changes, we find that containers have received some special love.&lt;/p&gt;
&lt;h1 id="initialization"&gt;Initialization&lt;/h1&gt;
&lt;p&gt;C++ was long behind modern languages when it came to initializing containers. While you could do&lt;/p&gt;
&lt;p&gt;[cpp]int a[] = {1, 2, 3};[/cpp]&lt;/p&gt;
&lt;p&gt;for simple arrays, things tended to get more verbose for more complex containers:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
vector&lt;string&gt; v;&lt;br&gt;
v.push_back(“One”);&lt;br&gt;
v.push_back(“Two”);&lt;br&gt;
v.push_back(“Three”);&lt;br&gt;
[/cpp]&lt;/p&gt;
&lt;p&gt;C++11 has introduced an easier, simpler way to initialize this:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
vector&lt;string&gt; v = {“One”, “Two”, “Three”};&lt;br&gt;
[/cpp]&lt;/p&gt;
&lt;p&gt;The effects of the changes are even better for things like &lt;strong&gt;map&lt;/strong&gt;s, which could get cumbersome quickly:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
map&amp;lt;string, vector&lt;string&gt; &amp;gt; m;&lt;br&gt;
vector&lt;string&gt; v1;&lt;br&gt;
v1.push_back(“A”);&lt;br&gt;
v1.push_back(“B”);&lt;br&gt;
v1.push_back(“C”);&lt;/p&gt;
&lt;p&gt;vector&lt;string&gt; v2;&lt;br&gt;
v2.push_back(“A”);&lt;br&gt;
v2.push_back(“B”);&lt;br&gt;
v2.push_back(“C”);&lt;/p&gt;
&lt;p&gt;m[“One”] = v1;&lt;br&gt;
m[“Two”] = v2;&lt;br&gt;
[/cpp]&lt;/p&gt;
&lt;p&gt;This can now be expressed as:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
map&amp;lt;string, vector&lt;string&gt;&amp;gt; m = One,&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {&amp;quot;Two&amp;quot;, {&amp;quot;Z&amp;quot;, &amp;quot;Y&amp;quot;, &amp;quot;X&amp;quot;}}};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[/cpp]&lt;/p&gt;
&lt;p&gt;Much simpler and in line with most modern languages. As an aside, there’s another change in C++11 that would be easy to miss in the code above. The declaration&lt;/p&gt;
&lt;p&gt;[cpp]map&amp;lt;string, vector&lt;string&gt;&amp;gt; m;[/cpp]&lt;/p&gt;
&lt;p&gt;was illegal until now due to &amp;raquo; always being evaluated to the right-shift operator; a space would always be required, like&lt;/p&gt;
&lt;p&gt;[cpp]map&amp;lt;string, vector&lt;string&gt; &amp;gt; m[/cpp]&lt;/p&gt;
&lt;p&gt;No longer the case.&lt;/p&gt;
&lt;h1 id="iterating"&gt;Iterating&lt;/h1&gt;
&lt;p&gt;Iterating through containers was also inconvenient. Iterating the simple &lt;em&gt;vector v&lt;/em&gt; above:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (vector&lt;string&gt;::iterator i = v.begin();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; i != v.end(); i++)
cout &amp;lt;&amp;lt; i &amp;lt;&amp;lt; endl;[/cpp]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Modern languages have long had some &lt;em&gt;foreach&lt;/em&gt; equivalent that allowed us easier ways to iterate through these structures without having to explicitly worry about iterators types. C++11 is finally catching up:&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (string s : v)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cout &amp;lt;&amp;lt; s &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[/cpp]&lt;/p&gt;
&lt;p&gt;As well, C++11 brings in a new keyword, &lt;em&gt;auto&lt;/em&gt;, that will evaluate to a type in compile-type. So instead of&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (map&amp;lt;string, vector&lt;string&gt; &amp;gt;::iterator i = m.begin();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; i != m.end(); i++) {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[/cpp]&lt;/p&gt;
&lt;p&gt;we can now write&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (auto i = m.begin(); i != m.end(); i++) {&lt;br&gt;
[/cpp]&lt;/p&gt;
&lt;p&gt;and &lt;em&gt;auto&lt;/em&gt; will evaluate to &lt;em&gt;map&amp;lt;string, vector&lt;string&gt;&amp;gt;::iterator&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Combining these changes, we move from the horrendous&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (map&amp;lt;string, vector&lt;string&gt; &amp;gt;::iterator i = m.begin();&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; i != m.end(); i++)
for (vector&amp;lt;string&amp;gt;::iterator j = i-&amp;gt;second.begin();
 j != i-&amp;gt;second.end(); j++)
 cout &amp;lt;&amp;lt; i-&amp;gt;first &amp;lt;&amp;lt; ': ' &amp;lt;&amp;lt; *j &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[/cpp]&lt;/p&gt;
&lt;p&gt;to the much simpler&lt;/p&gt;
&lt;p&gt;[cpp]&lt;br&gt;
for (auto i : m)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (auto j : i.second)
 cout &amp;lt;&amp;lt; i.first &amp;lt;&amp;lt; ': ' &amp;lt;&amp;lt; j &amp;lt;&amp;lt; endl;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[/cpp]&lt;/p&gt;
&lt;p&gt;Not bad.&lt;/p&gt;
&lt;p&gt;C++11 support varies a lot from compiler to compiler, but all of the changes above are already supported in the latest versions of GCC, LLVM, and MSVC compilers.&lt;/p&gt;</description></item><item><title>FOR is evil or something</title><link>https://rselbach.com/for-is-evil-or-something/</link><pubDate>Mon, 19 Nov 2012 13:31:16 +0000</pubDate><guid>https://rselbach.com/for-is-evil-or-something/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Have you ever wondered how FORs impact your code? How they are limiting your design and more important how they are transforming your code into an amount of lines without any human meaning?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How can you not want to read an article that starts like that? I had to steal the intro from the original. Seriously, I have used FOR since I learned BASIC back in the day. I never thought about how it was limiting my design. I. Must. Learn. How.&lt;/p&gt;
&lt;p&gt;The article I am referring to is, &lt;a href="http://www.lordofthejars.com/2012/10/avoiding-fors-anti-if-campaign.html"&gt;Avoiding FORs – Anti-If Campaign&lt;/a&gt;. Eager learner I, I could not not read it.&lt;/p&gt;
&lt;p&gt;After the resplendent intro, &lt;em&gt;Avoiding FOR&lt;/em&gt; goes on to show “how to transform a simple example of a for […], to something more readable and well designed.”&lt;/p&gt;
&lt;p&gt;It takes this unreadable piece of code — that I now recognize as unreadable:&lt;/p&gt;
&lt;p&gt;[java]&lt;br&gt;
public class Department {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private List resources = new ArrayList();

public void addResource(Resource resource) {
 this.resources.add(resource);
}

public void printSlips() {

 for (Resource resource : resources) {
 if(resource.lastContract().deadline().after(new Date())) {

 System.out.println(resource.name());
 System.out.println(resource.salary());
 }
 }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}[/java]&lt;/p&gt;
&lt;p&gt;My eyes hurt already. Thankfully the author transforms the aberration above into this clean, much more readable snippet:&lt;/p&gt;
&lt;p&gt;[java]public class ResourceOrderedCollection {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; private Collection&amp;lt;Resource&amp;gt; resources = new ArrayList&amp;lt;Resource&amp;gt;();



public ResourceOrderedCollection() {
 super();
}

public ResourceOrderedCollection(Collection&amp;lt;Resource&amp;gt; resources) {
 this.resources = resources;
}

public void add(Resource resource) {
 this.resources.add(resource);
}

public void forEachDo(Block block) {
 Iterator&amp;lt;Resource&amp;gt; iterator = resources.iterator();

 while(iterator.hasNext()) {
 block.evaluate(iterator.next());
 }

}

public ResourceOrderedCollection select(Predicate predicate) {

 ResourceOrderedCollection resourceOrderedCollection = new ResourceOrderedCollection();

 Iterator&amp;lt;Resource&amp;gt; iterator = resources.iterator();

 while(iterator.hasNext()) {
 Resource resource = iterator.next();
 if(predicate.is(resource)) {
 resourceOrderedCollection.add(resource);
 }
 }

 return resourceOrderedCollection;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;public class Department {&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private List&amp;lt;Resource&amp;gt; resources = new ArrayList&amp;lt;Resource&amp;gt;();

public void addResource(Resource resource) {
 this.resources.add(resource);
}

public void printSlips() {
 new ResourceOrderedCollection(this.resources).select(new InForcePredicate()).forEachDo(new PrintSlip());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}[/java]&lt;/p&gt;
&lt;p&gt;Wait, what? Is this an Onion article?&lt;/p&gt;
&lt;p&gt;Snarky Mode Off.&lt;/p&gt;
&lt;p&gt;I understand what the author wanted to do, but really, the example used is so off the left field that it’s not even funny.&lt;/p&gt;</description></item><item><title>Rolling out your own Fusion Drive with the recovery partition</title><link>https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/</link><pubDate>Fri, 09 Nov 2012 11:18:34 +0000</pubDate><guid>https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/</guid><description>&lt;p&gt;&lt;a href="./Screen-Shot-2012-11-09-at-9.32.21-AM.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/Screen-Shot-2012-11-09-at-9.32.21-AM-580x493_hu_ba3f32ea2a9fbc3e.webp 320w, https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/Screen-Shot-2012-11-09-at-9.32.21-AM-580x493_hu_c5c6b4672cb76a4b.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/Screen-Shot-2012-11-09-at-9.32.21-AM-580x493.jpg"
 alt="disk utility showing Fusion Drive"
 loading="lazy"
 decoding="async" width="580" height="493" srcset="https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/Screen-Shot-2012-11-09-at-9.32.21-AM-580x493_hu_c2010ef35fd9cb07.jpg 320w, https://rselbach.com/rolling-out-your-own-fusion-drive-with-the-recovery-partition/Screen-Shot-2012-11-09-at-9.32.21-AM-580x493_hu_c7043bb5aef60ad2.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My Macbook Pro has two disks, an HDD and an SSD, each of 240GB or so. With the details of Apple’s Fusion Drive coming out I decided to do what any reasonable geek would do to their production computer: I’ve decided to implement my own untested, highly experimental and barely understood Fusion Drive.&lt;/p&gt;
&lt;p&gt;One of the things that initially put me off doing this was that according to the 3,471,918 tutorials that have popped up in the last 10 minutes would cause me to lose my Mountain Lion recovery partition because these partitions are not supported in a Fusion drive. Turns out this is not exactly true.&lt;/p&gt;
&lt;p&gt;Fusion Drive is just a marketing term for a what essentially is a CoreStorage logical volume spanning an SSD &lt;em&gt;and&lt;/em&gt; an HDD. And although you cannot have the recovery partition inside a CS logical volume, it doesn’t mean you can’t have &lt;em&gt;both&lt;/em&gt; a recovery partition &lt;em&gt;and&lt;/em&gt; a Fusion Drive at the same time. It’s all in the &lt;a href="http://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man8/diskutil.8"&gt;diskutil man page&lt;/a&gt;, by the way:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Create a CoreStorage logical volume group. The disks specified will become the (initial) set of physical volumes; more than one may be specified. &lt;strong&gt;You can specify partitions&lt;/strong&gt; (which will be re-typed to be Apple_CoreStorage) or whole-disks (which will be partitioned as GPT and will contain an Apple_CoreStorage partition). The resulting LVG UUID can then be used with createVolume below. All existing data on the drive(s) will be lost. Ownership of the affected disk is required.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What matters is what’s in bold above: we’re not limited to using whole disks. So here’s what I did.&lt;/p&gt;
&lt;p&gt;I rebooted my system and held the &lt;strong&gt;option&lt;/strong&gt; key so I could select my recovery partition as the start up disk. Once the OSX recovery started up, I launched a terminal to do the dirty work.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diskutil list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this I noted two things: (a) the main SSD partition (the one holding my OSX and that sited by my recovery partition) and (b) the disk name of my HDD. They were respectively &lt;em&gt;disk0s2&lt;/em&gt; and &lt;em&gt;disk1&lt;/em&gt; in my case, but they’ll very likely be different for you. Then the magic begins.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diskutil cs create &amp;quot;Fusion Drive&amp;quot; disk0s2 disk1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(For crying out loud, you need to change disk0s2 and disk1 for whatever makes sense on your system!)&lt;/p&gt;
&lt;p&gt;That created the coreStorage logical volume. Then I listed it all again to note what the new logical volume UUID was.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diskutil list
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The UUID is a long number identifier like F47AC10B-58CC-4372-A567-0E02B2C3D479. You’ll need that one next to actually create the volume where you’ll be installing your system.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diskutil coreStorage createVolume F47AC10B-58CC-4372-A567-0E02B2C3D479 jhfs+ &amp;quot;Macbook FD&amp;quot; 100%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command above will create a volume named “Macbook FD” using 100% of the logical volume we had created earlier.&lt;/p&gt;
&lt;p&gt;I then restored my Time Machine backup and that’s it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Note that after this process, the Recovery partition will still be present and things that require it (such as Find My Mac) will work fine. Some people correctly pointed out, however, that you can no longer boot from the recovery partition by using the menu from holding ⌥ (&lt;strong&gt;option)&lt;/strong&gt; during boot. I’m not sure why that is, but fear not, it will still boot normally from pressing &lt;strong&gt;⌘R (command + R)&lt;/strong&gt;.&lt;/p&gt;</description></item><item><title>Not a-ok</title><link>https://rselbach.com/not-a-ok/</link><pubDate>Thu, 08 Nov 2012 17:48:39 +0000</pubDate><guid>https://rselbach.com/not-a-ok/</guid><description>&lt;p&gt;I have a little confession to make: our family is going through a rough patch. Both my wife and I are having problems. No, not between us. And both are experiencing very different sorts of problems. But the happening-at-the-same-time complicates the one-supports-the-other thing.&lt;/p&gt;
&lt;p&gt;And to compound to that, I have been sick.&lt;/p&gt;
&lt;p&gt;As I sat in the waiting room at the hospital the other day, waiting for some exams, I felt like this is one of the worst times of my life. I feel tired, unmotivated, unappreciated, and generally unhappy. And fucking hopeless. That’s the worst part, I suppose.&lt;/p&gt;
&lt;p&gt;I also have been distancing myself from friends lately. Ostensibly to avoid distractions in a time where I am having to give all and a bit more to a project I do not believe in. Truth be told, I just don’t feel like small talk but I also don’t want bring others down to my Dark Hole of Misery. And so I’m trying to keep some distance.&lt;/p&gt;
&lt;p&gt;I feel lost. I look at my friends and how they all seem to have it all figured out already. I’m still trying to figure out who the fuck I am. I was so sure when I was younger. I was so fucking &lt;em&gt;good&lt;/em&gt; at what I did. Now? I just don’t know anymore. I feel unhappy.&lt;/p&gt;
&lt;p&gt;I know some people who will be So. Fucking. Happy. reading the paragraph above. This one’s for free for you guys.&lt;/p&gt;
&lt;p&gt;But not all is bad news. I actually got some good news last night. I can’t tell the details although I know some of you know exactly what this is about. Anyway, I now have a set date for it and it’s in under six months from now.&lt;/p&gt;
&lt;p&gt;I’m actually confident that this will make it all better somehow. Just have to wait.&lt;/p&gt;
&lt;p&gt;Sorry for the downer, but I felt like writing something.&lt;/p&gt;</description></item><item><title>How I accidentally because a domain squatter</title><link>https://rselbach.com/how-i-accidentally-because-a-domain-squatter/</link><pubDate>Fri, 02 Nov 2012 00:39:23 +0000</pubDate><guid>https://rselbach.com/how-i-accidentally-because-a-domain-squatter/</guid><description>&lt;p&gt;A couple of days ago, I was listening to one of my favourite podcasts, &lt;a href="http://5by5.tv/frequency"&gt;The Frequency&lt;/a&gt;, when one of the hosts, Dan Benjamin, thought of a cool domain name, &lt;a href="http://ohitson.com"&gt;ohitson.com&lt;/a&gt; and checked to see if it was available. Turns out it was and he said he was registering right there and then. Now, two things: (a) I was listening to a recorded podcast, not live; and (b) I thought to myself, damn it! it &lt;em&gt;is&lt;/em&gt; a cool domain name.&lt;/p&gt;
&lt;p&gt;The next day, I launched &lt;a href="http://hover.com"&gt;Hover&lt;/a&gt; and checked the domain name and to my surprise it was available. I simply thought either Dan had given up on it or, most likely, I had misunderstood the domain name he was talking about and fortunately that had made me thing of &lt;em&gt;this&lt;/em&gt; cool domain name. I even checked Google to make sure “Oh, it’s on” was really written like that 😛&lt;/p&gt;
&lt;p&gt;Obviously I went ahead and registered the name. After that, I listed to that day’s The Frequency and heard Dan tell Haddie something to the effect of “oh, I forgot to register that domain yesterday!” That’s when I thought, oh-oh, maybe I &lt;em&gt;had&lt;/em&gt; heard the correct domain name.&lt;/p&gt;
&lt;p&gt;As it turns out, I was just listening to today’s episode and guess what? Dan mentions that someone registered it due to his mention on the show (which is technically true.)&lt;/p&gt;
&lt;p&gt;But I am a nice guy. I offered to transfer the domain to Dan for free just a few minutes ago. Not sure he’ll see my posts to app.net or Twitter. If not, I’ll try again a few times. I really don’t have any intention to keep this domain name as long as he still wants it. Sounds unfair.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 7 Nov 2012:&lt;/strong&gt; can you believe he actually accepted my offer? What a douche! Just kidding, it was the Fair Thing to Do™ and I’m happy to say the domain has been transferred to His Benjaminship already.&lt;/p&gt;</description></item><item><title>The day a videogame changed my life</title><link>https://rselbach.com/the-day-a-videogame-changed-my-life/</link><pubDate>Thu, 04 Oct 2012 15:32:42 +0000</pubDate><guid>https://rselbach.com/the-day-a-videogame-changed-my-life/</guid><description>&lt;p&gt;Last night, as I tried getting my three year-old to sleep so I could play my brand-new copy of &lt;a href="http://www.easports.com/nhl"&gt;NHL 13&lt;/a&gt;, I had an epiphany of sorts. It dawned on me how much videogames influenced my real-life pleasures. And the story actually started a long time ago.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;img src="http://cd.textfiles.com/gifgalaxy/PROGRAMS/VGACOPY.GIF"
 alt="http://cd.textfiles.com/gifgalaxy/PROGRAMS/VGACOPY.GIF"
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
 The story begins in a rather pleasant Saturday afternoon in late 1993. We went into a shoddy building, walked up the stairs to the first floor and found this rather unassuming office at the end of the hall. We skimmed over a thick catalog of game titles and started picking a few we wanted to try. They were cheap, pirated games. After selecting the titles, the guy there noted them down and asked us to wait. In the back, two guys got some floppy disks and started copying the games for us with &lt;a href="http://www.moenk.de/pages/vgacopy.html"&gt;VGAcopy&lt;/a&gt;. Nice!&lt;/p&gt;
&lt;p&gt;Of the games we bought, only one I can still remember: &lt;a href="http://www.nhl94.com/"&gt;NHL Hockey&lt;/a&gt;. To that day, I had only a general idea of what hockey was: Soccer on the ice. We picked it simply because the clerk there told us it was good.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/the-day-a-videogame-changed-my-life/538253-nhl-hockey-dos-screenshot-title-screens_hu_5e9dc46518a9b1b3.webp 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/the-day-a-videogame-changed-my-life/538253-nhl-hockey-dos-screenshot-title-screens.jpg"
 alt=""
 loading="lazy"
 decoding="async" width="320" height="200" srcset="https://rselbach.com/the-day-a-videogame-changed-my-life/538253-nhl-hockey-dos-screenshot-title-screens_hu_807d0d3d60c289b5.jpg 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
 We went home that day and probably tried some of the other, now-forgotten games, but then we installed NHL Hockey from the floppies to MS-DOS. We were both hooked instantly! The game had an arena atmosphere that later version never quite managed to reproduce. We did not know the many rules of hockey, we learned as we played. I remember vividly as my friend pumped up the volume on the PC when the game would play an 8-bit version of “We will rock you” while two cartoon hands clapped on the virtual jumbotron.&lt;/p&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/the-day-a-videogame-changed-my-life/nhl96_hu_a692f8cf56862e5e.webp 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/the-day-a-videogame-changed-my-life/nhl96.jpg"
 alt="http://www.igcent.com/images/stories/nhl96.jpg"
 loading="lazy"
 decoding="async" width="325" height="425" srcset="https://rselbach.com/the-day-a-videogame-changed-my-life/nhl96_hu_284eeb936c7fe723.jpg 320w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
 We played that game throughout that night and into Sunday. It was fast. It was fun. We were hooked. NHL 96 was the first game I ever legally bought. And I’ve bought almost every version since then.&lt;/p&gt;
&lt;p&gt;Back to how it influenced my real-life, playing the game got me into Hockey as a sport. A Brazilian hooked on Hockey? Not supposed to happen, but one likes what one likes. It also indirectly started my love affair with Canada, but that’s a different story. Following Hockey was not easy until I found a radio that streamed games and later the League started offering live vide streaming for which I have been gladly paying an exorbitant amount of money for half a decade or so. I’ve experienced a similar effect thanks to the &lt;a href="http://www.easports.com/madden-nfl"&gt;Madden&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/NBA_Live_series"&gt;NBA Live&lt;/a&gt; series, but NHL Hockey will always be a special case for me.&lt;/p&gt;
&lt;p&gt;I realized that thru hockey I’ve made friends, some of whom are &lt;em&gt;real&lt;/em&gt; good friends. And all of this started because a couple of decades ago we got some pirated games in a Saturday afternoon.&lt;/p&gt;
&lt;p&gt;Want to make a comment or suggestion? Do you feel like you need to correct me for not fitting the Standard Brazilian Specification? Feel free to talk to (or scream at) me, I’m &lt;a href="https://alpha.app.net/robteix"&gt;@robteix on App.net&lt;/a&gt; and &lt;a href="http://twitter.com/robteix"&gt;also on Twitter&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>In which Rob gets app.net</title><link>https://rselbach.com/in-which-rob-gets-app-net/</link><pubDate>Sun, 19 Aug 2012 23:48:24 +0000</pubDate><guid>https://rselbach.com/in-which-rob-gets-app-net/</guid><description>&lt;p&gt;I got myself an app.net account (&lt;a href="https://alpha.app.net/robteix"&gt;@robteix&lt;/a&gt;) a couple of days ago. [Cue the &lt;a href="http://ihave50dollars.com/"&gt;ihave50dollars.com&lt;/a&gt; jokes.] Still haven’t really used it a lot but am starting to enjoy the discussions about the API development.&lt;/p&gt;
&lt;p&gt;I joined mostly to support the idea—which I must admit is pretty insane. But what would the world be without insane ideas? ADN will &lt;strong&gt;never&lt;/strong&gt; be Twitter or Facebook, but maybe it doesn’t need to be. Maybe it really only needs to be a same environment for people like me. I like it.&lt;/p&gt;
&lt;p&gt;They do need a better name though.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Posted using BlogPress from my iPhone&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Could something like “Apollo 18” really have existed?</title><link>https://rselbach.com/could-something-like-apollo-18-really-have-existed/</link><pubDate>Sat, 17 Sep 2011 11:01:45 +0000</pubDate><guid>https://rselbach.com/could-something-like-apollo-18-really-have-existed/</guid><description>&lt;p&gt;Conspiracy theories and hoaxes abound when it comes to mankind’s arrival at our moon (or the non-arrival.) One such hoaxes is the &lt;strong&gt;Apollo 20&lt;/strong&gt;, a purported top secret mission that back to the moon back in 1976 to fetch some aliens living there. The secret would have been revealed by no other than William Rutledge, who claims to have been an astronaut in that mission and who nowadays lives and writes out of Rwanda.&lt;/p&gt;
&lt;p&gt;Arguably this theory was never as popular as its always popular theory that NASA spent billions of dollars to fabricate the moon landings but somehow forgot to paint stars in the ceiling.&lt;/p&gt;
&lt;p&gt;Now however a film promises to inspire the masses who believe NASA has been sending secret missions to Luna. The movie is &lt;a href="http://apollo18movie.net/"&gt;Apollo 18&lt;/a&gt;.&lt;br&gt;
&lt;a href="./apollo-18-movie1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/apollo-18-movie_thumb1_hu_fff8483717afdb28.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/apollo-18-movie_thumb1_hu_cfcbd06f41d0e061.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/apollo-18-movie_thumb1.jpg"
 alt="apollo-18-movie"
 loading="lazy"
 decoding="async" width="544" height="304" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/apollo-18-movie_thumb1_hu_82d6a349e8556ed3.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/apollo-18-movie_thumb1_hu_13cee43e7aab9751.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Promotional picture)&lt;/p&gt;
&lt;p&gt;The movie’s premise is that after the Apollo program was cancelled, NASA would have flown another mission and found, of course, aliens and that’s why we’ve never been back to the Moon. It’s one more movie in the style of &lt;a href="http://www.cloverfieldmovie.com/"&gt;Cloverfield&lt;/a&gt;, where the audience is expected to pretend to be watching to real top-secret leaked footage.&lt;/p&gt;
&lt;p&gt;Domension Films’ big cahuna, Bob Weinstein, stated,&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We didn’t shoot anything, we found it. Found, baby!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The movie is of course a work of fiction. That’s not hard to figure out, even if you could not look up the actors who were in the movie. For instance, the astronauts of Apollo 18 in the movie are supposed to be Nathan Walker, John Grey e Benjamin Anderson, but the astronaut corps roster was very well known and none of these gentlemen were part of it. Although none of the crews for the cancelled flights (Apollos 18, 19 and 20) were never officially named – what would be the point? –, one can infer from the assignment rotation system NASA used that &lt;a href="http://nssdc.gsfc.nasa.gov/planetary/lunar/apollo_18_20.html"&gt;the Apollo 18 crew would likely be&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Richard Gordon&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vance Brand&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Harrison Schmitt&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Except Schmitt was activated to the Apollo 17 main crew when it became clear that it would be the last chance for a scientist to step on the moon. Somebody – Joe Engle, perhaps – would have to replace him on Apollo 18.&lt;/p&gt;
&lt;p&gt;But the hoax is not really about the movie itself, but about the general idea that NASA ran more missions than we know about. So, could something like Apollo 18 really have existed? Could NASA have performed this secretly?&lt;/p&gt;
&lt;p&gt;It is unfortunately impossible to prove a negative, but at least we can think of how likely would that be. I can’t really see how such a thing could have been done. To begin with, there’s this:&lt;br&gt;
&lt;a href="./flickr-3743794970-original1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flickr-3743794970-original_thumb1_hu_ff52c6be73fbc97b.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/flickr-3743794970-original_thumb1_hu_3bf46401c489c0ef.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flickr-3743794970-original_thumb1.jpg"
 alt="flickr-3743794970-original"
 loading="lazy"
 decoding="async" width="615" height="478" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flickr-3743794970-original_thumb1_hu_9d6930aacc207657.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/flickr-3743794970-original_thumb1_hu_3e5184e805e9134f.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Image credit: &lt;strong&gt;&lt;a href="http://www.flickr.com/photos/vanderkroew/"&gt;Euclid vanderKroew&lt;/a&gt;)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You see, the &lt;strong&gt;Saturn V&lt;/strong&gt; was big. &lt;em&gt;Really big&lt;/em&gt;. Not easy to hide, then. It seems highly unlikely that NASA could have launched a Saturn V out of Cape Canaveral without it being seen.&lt;/p&gt;
&lt;p&gt;I also saw this argument on some forum that NASA would have prefferred a night launch to improve the chances to keep it a secret, but the thing is, that big dumb rocket is not very subtle either.&lt;br&gt;
&lt;a href="./3743667306_c2b398ee5b_z1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/3743667306_c2b398ee5b_z_thumb1_hu_10aa58099a01a5c5.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/3743667306_c2b398ee5b_z_thumb1_hu_d32ad5559a58a9f6.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/3743667306_c2b398ee5b_z_thumb1.jpg"
 alt="3743667306_c2b398ee5b_z"
 loading="lazy"
 decoding="async" width="584" height="440" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/3743667306_c2b398ee5b_z_thumb1_hu_59868e971073ceb6.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/3743667306_c2b398ee5b_z_thumb1_hu_8f07c68e50740718.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Image credit: &lt;a href="http://www.flickr.com/photos/vanderkroew/"&gt;Euclid vanderKroew&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;And then if we discard a launch in the continental US, it would have to be from either a platform at sea or from somewhere in North Africa. Problem here is that the logistics of accomplishing such an feat – let alone in absolute secrecy – are just fenomenal.&lt;/p&gt;
&lt;p&gt;As well, the Saturn V was a very public project. All its parts were very well tracked and it’s possible to know where most parts are even today. And some of those parts are &lt;em&gt;huge&lt;/em&gt;, not the kind that you can stow in the back of a black unmarked van.&lt;/p&gt;
&lt;p&gt;And then there are other factors, of course. We’re used to the image of three astronauts sitting on top of a rocket and a mission control room with, say, 20 or 30 people.&lt;br&gt;
&lt;a href="./2985911950_0f3a207a10_z1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/2985911950_0f3a207a10_z_thumb1_hu_b78641ed20d64cb7.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/2985911950_0f3a207a10_z_thumb1_hu_4ebee8944b5a002a.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/2985911950_0f3a207a10_z_thumb1.jpg"
 alt="2985911950_0f3a207a10_z"
 loading="lazy"
 decoding="async" width="584" height="329" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/2985911950_0f3a207a10_z_thumb1_hu_f5e0b27b62cd9f85.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/2985911950_0f3a207a10_z_thumb1_hu_b454ab42f143a13.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Image credit: &lt;a href="http://www.flickr.com/photos/doctorow/"&gt;Cory Doctorow&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;But an Apollo flight involved a lot more people than that. In &lt;a href="http://www.amazon.com/gp/product/0670814466/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=itsuptoyoma-20&amp;amp;linkCode=as2&amp;amp;camp=217145&amp;amp;creative=399369&amp;amp;creativeASIN=0670814466"&gt;A Man on the Moon: The Voyages of the Apollo Astronauts&lt;/a&gt; (Awesome gift! Thank you Stulzer!), Andrew Chalkin estimates the figure at 500,000 workers altogether. &lt;a href="http://www.amazon.com/gp/product/0618507574/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=itsuptoyoma-20&amp;amp;linkCode=as2&amp;amp;camp=217145&amp;amp;creative=399369&amp;amp;creativeASIN=0618507574"&gt;Others state the number is more like 400,000 people&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Regardless of the actual figure, it should be clear that such a mission would require the collaboration of &lt;strong&gt;hundreds of thousands of people&lt;/strong&gt; all around the world (more on that below.)&lt;/p&gt;
&lt;p&gt;And contrary to what some might want to believe, the Apollo program was not something entirely done behind closely guarded doors at some Air Force base. It involved a &lt;em&gt;lot&lt;/em&gt; of private contractors. GE, IBM, Boeing, GM… the list of contractors can occupy several pages. To assume that all the employees involved who have since likely changed jobs multiple times and retired would be able to keep this a secret for four decades really stretches one’s imagination.&lt;br&gt;
&lt;a href="./figc-31.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/figc-3_thumb1_hu_dc937cdafa18ea09.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/figc-3_thumb1_hu_97d71ce34b1f4ad3.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/figc-3_thumb1.jpg"
 alt="figc-3"
 loading="lazy"
 decoding="async" width="584" height="444" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/figc-3_thumb1_hu_ed923066baaf4e5.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/figc-3_thumb1_hu_25104e9d3a047a27.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Image credit: &lt;a href="./figc-32.jpg"&gt;history.nasa.gov&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;As big as the contractors’, the list of academic institutions involved in the program is amazing. Virtually every major US university and institute was included, but the list also included institutions form Australia, Belgium, Canada, England, Finland, Germany, Japan, Scotland and Switzerland. And that takes me to what is, to me, the most important thing to consider.&lt;/p&gt;
&lt;p&gt;In order to fly to the Moon, you don’t just point the rocket, turn on the ignition and wait for it to reach its destination. It’s a complex voyage with huge preparations, calculations and adjustments. Orbital mechanics was one of the most interesting things I’ve even studied. But I digress.&lt;/p&gt;
&lt;p&gt;An interesting challenge for NASA was to be able to communicate and track the ship all the way to the Moon and back.&lt;br&gt;
&lt;a href="./flight-profile-p171.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flight-profile-p17_thumb1_hu_e79b9473e10c3ee3.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/flight-profile-p17_thumb1_hu_63cd4231938db0cd.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flight-profile-p17_thumb1.jpg"
 alt="flight-profile-p17"
 loading="lazy"
 decoding="async" width="584" height="396" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/flight-profile-p17_thumb1_hu_d4d2422bcdd8483.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/flight-profile-p17_thumb1_hu_f443d779fe8c14f0.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Escaneado pelo autor. NASA Apollo 11 Press Kit Pg 17)&lt;/p&gt;
&lt;p&gt;Nowadays NASA added a whole network of satellites to assist the tast, but back when Mercury and Apollo were underway, the tracking depended on a network of tracking stations, vessels and aircraft all around the world.&lt;/p&gt;
&lt;p&gt;That network was the &lt;strong&gt;Manned Space Flight (Tracking) Network&lt;/strong&gt;. Starting from Apollo 10, the &lt;strong&gt;Deep Space Network&lt;/strong&gt; was added to assist.&lt;br&gt;
&lt;a href="./MSFN1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/MSFN_thumb1_hu_32292ec6dec9b909.webp 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/MSFN_thumb1_hu_395ced17d8198e18.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/could-something-like-apollo-18-really-have-existed/MSFN_thumb1.jpg"
 alt="MSFN"
 loading="lazy"
 decoding="async" width="584" height="391" srcset="https://rselbach.com/could-something-like-apollo-18-really-have-existed/MSFN_thumb1_hu_5186c56a338298f5.jpg 320w, https://rselbach.com/could-something-like-apollo-18-really-have-existed/MSFN_thumb1_hu_6604e06c963f814f.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;br&gt;
(Scanned by the author from NASA Apollo 11 Press Kit, Pg 172)&lt;/p&gt;
&lt;p&gt;It’s interesting to know that over the course of its evolution, NASA’s networks included a station in Havana, Cuba. That station was dismantled after years of service due to the Cuban revolution. As Brazilian, I also find special interest in that there was even one station along with the Brasilia International Airport, even though it was quickly disassembled and shipped to Madagascar.)&lt;/p&gt;
&lt;p&gt;Although those were NASA installations, they employed locals. These stations all around the world had been performing duties for years under heavy media scrutiny and to expect that all of a sudden they would be able to do it in complete secrecy is beyond reasonable belief. That and the local workers who also had to keep secrets for four decades make the most absurd hole in the hoax.&lt;/p&gt;
&lt;p&gt;Now remember that the Apollo missions were tracked and monitored by several governments including, of course, the Soviet Union. In &lt;a href="http://www.amazon.com/gp/product/0312308655/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=itsuptoyoma-20&amp;amp;linkCode=as2&amp;amp;camp=217145&amp;amp;creative=399369&amp;amp;creativeASIN=0312308655"&gt;Two Sides of the Moon&lt;/a&gt;, Russian cosmonaut Alexei Leonov – trained to be the first man on the moon – states that all the missions were followed by the Russians in a very well equipped Space Transmissions Corps, in Moscow. Now you need the secret to be kept by the Soviets and then, after the USSR fell, by the several nations that sprouted from it.&lt;/p&gt;
&lt;p&gt;And the Apollo missions were tracked by amateur astronomers and radio operators all over.&lt;/p&gt;
&lt;p&gt;Of course that nothing here proves beyond any doubt that such secret missions were &lt;em&gt;impossible&lt;/em&gt;, just unreasonable unlikely. But since you can’t prove a negative, no matter how improbale, there will still be plenty of people who believe.&lt;/p&gt;</description></item><item><title>My need for anonymity</title><link>https://rselbach.com/my-need-for-anonymity/</link><pubDate>Thu, 04 Aug 2011 19:54:38 +0000</pubDate><guid>https://rselbach.com/my-need-for-anonymity/</guid><description>&lt;p&gt;Much has been said about the pros and cons of anonymity lately, prompted by Google+ TOS which require the use of one’s real name. No pseudonyms allowed, except apparently if you call yourself Lady Gaga or 50 Cent.&lt;/p&gt;
&lt;p&gt;I have seen many kinds of arguments both for and against the use of aliases and I will not repeat them here. There is however one use of aliases that I haven’t seen stated anywhere and that coincidentally affects me personally. Perhaps this is so because the problem I am about to present is not so common after all. Or perhaps it is common but people decide not to talk about it. I have no way of knowing.&lt;/p&gt;
&lt;p&gt;Anonymity is a vital necessity to people with a certain kind of disability, a mental disorder. I am such a person. As some of my friends know and others mock, I suffer from a mental condition called social phobia, also known as social anxiety. I take medications that help me overcome some of the most serious effects and that allow me to do things like write about it on this very blog.&lt;/p&gt;
&lt;p&gt;Social anxiety manifests itself in varying degrees in all kinds of social interactions. And the levels of manifestations are not what you might expect. I regularly make presentations without a second thought. I’ve given talks to hundreds of people. And yet, ordering a pizza over the phone is terrifying experience to me. No matter how many times I’ve done it, I still have to “prepare” myself every time. I rehearse, play several unlikely scenarios in my head until I finally get the courage to dial the number and talk to the person on the other side. One characteristic of this anxiety disorder is that rationally I &lt;em&gt;know&lt;/em&gt; that there is nothing wrong; there is no risk in calling the pizza place. But the brain acts as if there were. But I digress.&lt;/p&gt;
&lt;p&gt;I love coding. I have been doing it since I was a kid and it’s the best thing I know how to do. And then there is open source. Open source projects should be the perfect venue for me to have fun. Except I am scared stiff by the idea that someone might laugh at the code. It came to a point where it is impossible for me to contribute. Then I’ve come up with a solution: an alias. For the past several years I’ve lived two different lives online: one as myself and another as an alias. I keep them strictly separate.&lt;/p&gt;
&lt;p&gt;Using the alias, I actively contribute to several different projects. And I enjoy it all. And it would be impossible for me to do that using my own name. My pseudonym allows me to work around my condition. It allows me to &lt;em&gt;live&lt;/em&gt; my life.&lt;/p&gt;
&lt;p&gt;I understand the rationale behind the requirement for real names on Google+. But I also know that the requirement makes it impossible for people like me to be really free on the Internet. So far, Google hasn’t figured out my alias. Hopefully it never will.&lt;/p&gt;
&lt;p&gt;(Photo by &lt;a href="http://www.fotopedia.com/users/abhishekb"&gt;Abhishek Singh&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>Chrome and the new Lion full-screen behaviour</title><link>https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/</link><pubDate>Tue, 02 Aug 2011 02:19:42 +0000</pubDate><guid>https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/</guid><description>&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/2687444500_e8beddd5e7_b1-580x375_hu_1bf9e00037cd176c.webp 320w, https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/2687444500_e8beddd5e7_b1-580x375_hu_d928c3ce85f16a15.webp 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/2687444500_e8beddd5e7_b1-580x375.jpg"
 alt="Three wise monkeys by Anderson Mancini"
 loading="lazy"
 decoding="async" width="580" height="375" srcset="https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/2687444500_e8beddd5e7_b1-580x375_hu_9ccb7eb42cba4ff3.jpg 320w, https://rselbach.com/chrome-and-the-new-lion-full-screen-behaviour/2687444500_e8beddd5e7_b1-580x375_hu_b27f8b9bce018a6b.jpg 480w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;While I was ranting about the annoyances I found in OS X Lion today, a friend commented he had no issues at all, except for the full-screen mode. I got curious because the new Lion full-screen mode is probably the only new feature I found interesting. What did Apple do so wrong?&lt;/p&gt;
&lt;p&gt;The answer: &lt;em&gt;Google Chrome&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Wait, what? My friend was complaining that in Chrome you needed to use a keyboard shortcut in order to leave full-screen mode. That, he continued, was because Apple had given programmers too much freedom to implement the new feature any way they wanted. And he knew that, he assured me, because he had searched Google and confirmed it.&lt;/p&gt;
&lt;p&gt;The new full-screen mode in Lion is implemented as a window behaviour not enabled by default, but adding it to a window is remarkably easy*:&lt;/p&gt;
&lt;p&gt;[go]&lt;br&gt;
NSWindowCollectionBehavior behavior = [window collectionBehavior];&lt;br&gt;
behavior |= NSWindowCollectionBehaviorFullScreenPrimary;&lt;br&gt;
[window setCollectionBehavior:behavior];&lt;br&gt;
[/go]&lt;/p&gt;
&lt;p&gt;Adding the NSWindowCollectionBehaviorFullScreenPrimary behaviour to a window will enable a small button to its top right corner. When enabled, the mode will allocate a new Desktop for the full-screen app and will also add an icon to the system menu to allow you to quickly leave full-screen mode.&lt;/p&gt;
&lt;p&gt;And that’s the little icon my friend complained Chrome wasn’t showing, forcing him to use a keyboard shortcut. Imagine the trepidity! And all because of Apple.&lt;/p&gt;
&lt;p&gt;I tried to explain that the problem was that Chrome’s windows still didn’t support the new full-screen behaviour but my friend’s version of reality was unswayable: Chrome supported the new behaviour and the problem was Apple. He had searched Google to confirm it. What could I possibly do?&lt;/p&gt;
&lt;p&gt;Search it myself is what. And search I did. And I found &lt;a href="http://code.google.com/p/chromium/issues/detail?id=74065#c39"&gt;this comment&lt;/a&gt; on Chromium’s bug tracker:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Comment &lt;a href="http://code.google.com/p/chromium/issues/detail?id=74065#c39"&gt;39&lt;/a&gt; by &lt;a href="http://code.google.com/u/rsesek@chromium.org/"&gt;rsesek@chromium.org&lt;/a&gt;, Jul 15, 2011&lt;/p&gt;
&lt;p&gt;We had a conversation with one of our designers, and what we’re going to do right now is remove the fullscreen button so we don’t advertise a behavior that we don’t really implement. That change just landed and will hit Canary/Dev channels soon.&lt;/p&gt;
&lt;p&gt;Long-term, we’re going to implement a proper fullscreen interface for Lion. In this interface, we’ll also experiment with having a collapsable toolbar. Until then, fullscreen will operate as it does on Leopard/Snow Leopard.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So there it is, straight from the source. Chromium — same as Chrome — still doesn’t support the new behaviour. Done. Nothing like reality to end a discussion.&lt;/p&gt;
&lt;p&gt;Except my friend would not yield. Apple still needs to fix Chrome and anyone who disagrees — presumably that Chromium developer himself — is to be disregarded as an Apple fanboy.&lt;/p&gt;
&lt;p&gt;If however you don’t want to wait for Google (or is it Apple?0 to release Chrome with the new full-screen behaviour, you can get by using &lt;a href="http://chpwn.com/apps/maximizer.html"&gt;Maximizer&lt;/a&gt;, which adds the behaviour to any application window.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Yes, I do realize that if you’re supporting multiple OSX versions and, even worse, multiple platforms, things get slightly more complex, but still.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(&lt;a href="http://www.flickr.com/photos/ektogamat/2687444500/sizes/l/in/photostream/"&gt;Image by Anderson Mancini&lt;/a&gt;)&lt;/p&gt;</description></item><item><title>Jeff Greason on the near future of space exploration</title><link>https://rselbach.com/jeff-greason-on-the-near-future-of-space-exploration/</link><pubDate>Sun, 24 Jul 2011 15:50:32 +0000</pubDate><guid>https://rselbach.com/jeff-greason-on-the-near-future-of-space-exploration/</guid><description>&lt;p&gt;Fascinating TEDx talk about the near future of space exploration. &lt;a href="http://youtu.be/m8PlzDgFQMM"&gt;Jeff Greason – Rocket Scientist: Making Space Pay and Having Fun Doing It&lt;/a&gt;:&lt;/p&gt;</description></item><item><title>“¿Por qué están todos hablando inglés?”</title><link>https://rselbach.com/por-que-estan-todos-hablando-ingles/</link><pubDate>Wed, 13 Jul 2011 19:32:22 +0000</pubDate><guid>https://rselbach.com/por-que-estan-todos-hablando-ingles/</guid><description>&lt;p&gt;I’ve been spending an insalubrious amount of time on &lt;a href="https://plus.google.com/"&gt;Google +&lt;/a&gt;. I managed to plump for a pretty good group to follow and as a consequence I’ve been getting consistently interesting content. As for myself, I mostly I post in English. A couple of days ago, I posted something most inconsequential and it would have been an unreservedly unremarkable post wasn’t it for the fact that it was geolocated in Cordoba, Argentina, which caused it to attract the attention of people nearby. In the middle of the comments, someone asked, in Spanish: “&lt;em&gt;why is everyone speaking English?&lt;/em&gt;” That was a fair question and it touched something that has bugged me in the past.&lt;/p&gt;
&lt;p&gt;Some part of my brain must miss the good old times of hunting and gathering as I ended up as a nomadic man—albeit one who doesn’t hunt and at most gathers food from grocery stores, so there might be a flaw in my theory. My rootless nature made me move a lot. This and the nature of my work caused me to make friends in several places other than my native Brazil&lt;a href="https://plus.google.com/"&gt;1&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We’ve already established that I communicate in Portuguese. Working at a US-based multinational as well as with open source, I also use English quite a bit—and my aforesaid rootlessness has taken me to inhabit the beautiful state of Oregon in the past. Oh and did I mention I am currently putting in a tour of duty in Argentina?&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.flickr.com/photos/thomasthomas/274884308/"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/por-que-estan-todos-hablando-ingles/274884308_8a9b319c87_z-300x226.jpg"
 alt="The Myth of the Tower of Babel"
 loading="lazy"
 decoding="async" width="300" height="226"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The result of all of this—&lt;a href="http://boards.straightdope.com/sdmb/archive/index.php/t-330951.html"&gt;in blatant contrast with the pre-Tower of Babel times when everybody obviously spoke English&lt;/a&gt;—is that I communicate daily in multiple languages. There isn’t a day—well, weekday—I don’t have to speak Spanish, Portuguese, English and French with someone or another. And that is fine and actually quite nice: you do lose languages if you don’t use them. Not like riding a bike, I suppose. Regardless, it is not a problem to communicate 1:1 in those languages. But what about 1:many conversations?&lt;/p&gt;
&lt;p&gt;Blogging is a clear example. Should I blog in Portuguese as some keep telling me I should? I cannot reasonably expect my non-Brazilian readers to understand Portuguese. If I write in French, my Quebecer friends will be happy but what about the rest? Nationalistic rants aside, English &lt;em&gt;is&lt;/em&gt; an international language nowadays, just as Latin and French once were. Aside from &lt;em&gt;very&lt;/em&gt; few people, the vast majority of the people I know can understand English and that’s why I use it most often than any other.&lt;/p&gt;
&lt;p&gt;Ideally, one would use their own native language and computer translation would do the rest. Unfortunately we are not there yet. I will admit that computer translation is getting better and better and it is fairly good if you write clear, short sentences. And it will get better. But I am skeptic that we will ever get to the point where the algorithms will be able to deal with subtleties, innuendos and all the puns that make up human interactions.&lt;/p&gt;
&lt;p&gt;The way I see, I currently have a few options –&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I write in Portuguese, as the ever vigilant Brazilian crowd demands it.&lt;/strong&gt; That would soothe the wrathful nationalists who believe me a Traitor Of The Fatherland™. But it also limits my already limited audience; not good.**&lt;br&gt;
** &lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I write multiple versions of the same thing in each language.&lt;/strong&gt; That would begin to feel like actual work, not fun. Also, having tried that in the past, I’ve learned that after I was done with the first version, the others never came out naturally.**&lt;br&gt;
**&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I write in English.&lt;/strong&gt; Sure it makes me a bootlicking lackey of the imperialist Yankees in the eyes of the insecure, but it also helps me reach pretty much everyone I know.**&lt;br&gt;
**&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But using English is not a perfect solution either. For once, I am obviously not a native speaker and thus have an imperfect and narrow vocabulary. As well, I don’t share the cultural experiences that help define the subtleties of English. And finally, there are heaps of topics that just &lt;em&gt;feel wrong&lt;/em&gt; in English: such as local—as in Brazilian—topics. It just feels weird.&lt;/p&gt;
&lt;p&gt;Back to Google+, I have created Circles for Portuguese and French, but these are not much valuable until Google comes up with some system of set arithmetic that would allow me to say something to the effect of “Post this to members of Circle X who also happen to be members of Circle French.” Until then, English is my best bet.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://plus.google.com/"&gt;1&lt;/a&gt; Experience tells me that I am required to point out that we, cheerful Brazilians, speak Portuguese, not Spanish.&lt;/p&gt;
&lt;p&gt;(Image Credit: &lt;a href="http://www.flickr.com/photos/thomasthomas/"&gt;ThomasThomas&lt;/a&gt;, Creative Commons)&lt;/p&gt;</description></item><item><title>Godwin’s Law is anything but</title><link>https://rselbach.com/godwins-law-is-anything-but/</link><pubDate>Mon, 11 Jul 2011 14:43:57 +0000</pubDate><guid>https://rselbach.com/godwins-law-is-anything-but/</guid><description>&lt;p&gt;I’ve got many gripes with the internet, I’ll admit. One of those is the overuse of Godwin’s Law. Actually, it’s a double offence because not only people overuse it, but this Law is anything but.&lt;/p&gt;
&lt;p&gt;But let’s start with the misuse. I posted a while ago to Facebook that I had finished reading &lt;a href="http://www.amazon.com/gp/product/1451651686/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1451651686&amp;amp;linkCode=as2&amp;amp;tag=itsuptoyoma-20"&gt;The Rise and Fall of the Third Reich&lt;/a&gt; and I mentioned how scary it was that a place right in the heart of “civilized” Europe could have fallen to that madness so quickly. A small discussion formed with some people until one of my geek friends commented something to the effect of—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Let’s stop this because Roberto already Godwinned in the original post!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;You see, us geeks are good at that: repeating some meme without thinking much about them.&lt;/p&gt;
&lt;p&gt;But that’s how I see “Godwin” being used all over the place. A discussion killer. And the discussion did get killed, unfortunately. It’s like Godwin’s Law says “if someone mentions Nazis, the discussion is over” but that’s not what the “law” says—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As an online discussion continues, the probability of a reference or comparison to Hitler or to Nazis approaches 1.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which is actually true. Just like—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As an online discussion continues, the probability of a reference or comparison to Snow White and the Seven Dwarves approaches 1.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Obviously, as a discussion grows, the probability of referencing &lt;em&gt;anything&lt;/em&gt; will approach 1! Here’s Godwin himself back in 2008 about his “law”—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although deliberately framed as if it were a law of nature or of mathematics, its purpose has always been rhetorical and pedagogical: I wanted folks who glibly compared someone else to Hitler or to Nazis to think a bit harder about the Holocaust.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I find it ironic that his little experiment for making people think harder about the Holocaust is now used as a tool to &lt;em&gt;avoid&lt;/em&gt; mentions to it.&lt;/p&gt;
&lt;p&gt;You see, I get where Godwin was trying to go. You see glib comparisons with the Nazi all the time and yes, they suck. But going from that to making one of the most defining moments of the last Century completely off-limits is preposterous! Nazi comparisons are often valid and should &lt;em&gt;not&lt;/em&gt; be avoided, especially by misusing an old usenet meme.&lt;/p&gt;
&lt;p&gt;Again, His Godwinness—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Still, I sometimes have some ambivalence about the Law, which is far beyond my control these days. Like most parents, I’m frequently startled by the unexpected turn my 18-year-old offspring takes. […] When I saw the photographs from Abu Ghraib, for example, I understood instantly the connection between the humiliations inflicted there and the ones the Nazis imposed upon death camp inmates—but I am the one person in the world least able to draw attention to that valid comparison.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Avoiding comparing things to something as defining as Nazi Germany is an arbitrary limitation that makes no sense.&lt;/p&gt;
&lt;p&gt;That’s not to say that all Nazi comparisons are valid. There really are plenty of dishonest Nazi comparisons out there, such as &lt;a href="http://www.pressherald.com/news/Governor-says-IRS-new-gestapo-in-radio-address.html"&gt;this one&lt;/a&gt;, by an American governor—&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We the people have been told there is no choice. You must buy health insurance or pay the new Gestapo — the IRS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This because the Supreme Court of the United States had upheld a law that represented the first steps of that country in &lt;strong&gt;following&lt;/strong&gt; the rest of the civilized world in providing its citizens with basic healthcare. &lt;em&gt;Healthcare! Oh the evils of that Gestapo!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But it’s unreasonable to expect people to &lt;em&gt;completely&lt;/em&gt; ignore a huge part of our history in hope that dishonest governors won’t make silly comparisons, which they’ll do anyway.&lt;/p&gt;</description></item><item><title>Anagramizer, a simple anagram solver in Go</title><link>https://rselbach.com/anagramizer-a-simple-anagram-solver-in-go/</link><pubDate>Tue, 21 Jun 2011 00:18:27 +0000</pubDate><guid>https://rselbach.com/anagramizer-a-simple-anagram-solver-in-go/</guid><description>&lt;p&gt;This weekend I took the family to celebrate Father’s Day away from town. We went around getting to know parts of the province we live in and never been to.&lt;/p&gt;
&lt;p&gt;&lt;a href="./IMG_0402.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/anagramizer-a-simple-anagram-solver-in-go/IMG_0402-300x225.jpg"
 alt=""
 loading="lazy"
 decoding="async" width="300" height="225"&gt;
&lt;/figure&gt;
&lt;/a&gt;We came back yesterday and the plan today was for a nice, calm day at home (it’s a holiday of some sort here.) Then I got engaged in a game called &lt;strong&gt;Hanging with Friends&lt;/strong&gt;, a mix of the traditional hangman with a bit of Scrabble.&lt;/p&gt;
&lt;p&gt;Since English isn’t my first language, I have a limited vocabulary, which leaves me at a disadvantage against my English-speaking friends. I can handle the “hangman” part of the game where I have to guess the word my friends come up with; but when it becomes “Scrabble” and I’ve got to form words using only a given set of letters and still make them difficult enough that a native English speaker will have problems figuring them out, then it’s tough.&lt;/p&gt;
&lt;p&gt;An itch that needed some scratching. Enter &lt;a href="http://robteix.com/w/Anagramizer"&gt;Anagramizer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When I woke up this morning, I decided to write a little program to help me. You call it cheating, I call it having a bit of nerd fun.&lt;/p&gt;
&lt;p&gt;Being that I’m currently in love with &lt;a href="http://golang.org"&gt;Go&lt;/a&gt;, I decided to write in that language and it was really easy and quick to do it. It took me about half an hour to write the program that did what I needed. But then…&lt;/p&gt;
&lt;p&gt;I succumbed to the temptation and started adding bells and whistles. Admittedly it was mostly for my own amusement and trying stuff in Go, but by the time we were leaving for lunch, the program had more options than the KDE audio volume utility (see what I did just there?)&lt;/p&gt;
&lt;p&gt;I decided to &lt;a href="http://robteix.com/w/Anagramizer"&gt;make it available to anyone&lt;/a&gt; who wants to play with it. It served its purpose of entertaining me for about half a day 🙂&lt;/p&gt;
&lt;p&gt;It’s now available on &lt;a href="https://github.com/robteix/anagramizer"&gt;Github&lt;/a&gt; and released under a BSD licence.&lt;/p&gt;</description></item><item><title>Euler 9 in Go</title><link>https://rselbach.com/euler-9-in-go/</link><pubDate>Tue, 14 Jun 2011 15:30:06 +0000</pubDate><guid>https://rselbach.com/euler-9-in-go/</guid><description>&lt;p&gt;For fun I picked one of the Euler algorithms I had played with before and
rewrote it in &lt;a href="http://golang.org"&gt;Go&lt;/a&gt;. Instead of carrying over the nested-loop search, this
version first eliminates &lt;code&gt;c&lt;/code&gt; using the fixed sum and solves the Pythagorean
equation for &lt;code&gt;b&lt;/code&gt;. Only &lt;code&gt;a&lt;/code&gt; remains to 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;package&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&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;import&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;fmt&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;&amp;#34;os&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:#66d9ef"&gt;func&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&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;sum&lt;/span&gt; = &lt;span style="color:#ae81ff"&gt;1000&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; &lt;span style="color:#a6e22e"&gt;a&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:#a6e22e"&gt;a&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;sum&lt;/span&gt;&lt;span style="color:#f92672"&gt;/&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;; &lt;span style="color:#a6e22e"&gt;a&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;numerator&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;sum&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:#f92672"&gt;*&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;denominator&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:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;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 style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;numerator&lt;/span&gt;&lt;span style="color:#f92672"&gt;%&lt;/span&gt;&lt;span style="color:#a6e22e"&gt;denominator&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&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 style="color:#66d9ef"&gt;continue&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;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;numerator&lt;/span&gt; &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;denominator&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;		&lt;span style="color:#a6e22e"&gt;c&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;sum&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&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;a&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &amp;lt; &lt;span style="color:#a6e22e"&gt;c&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;			&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;a&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;b&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;c&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&gt;&lt;/span&gt;&lt;span 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;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Fprintln&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Stderr&lt;/span&gt;, &lt;span style="color:#e6db74"&gt;&amp;#34;no solution&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;os&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Exit&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The divisibility check rejects values of &lt;code&gt;a&lt;/code&gt; that would produce a fractional
&lt;code&gt;b&lt;/code&gt;. The program uses constant space, examines fewer than &lt;code&gt;sum / 3&lt;/code&gt; candidates,
and prints &lt;code&gt;31875000&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>How to set up Emacs on Windows</title><link>https://rselbach.com/how-to-set-up-emacs-on-windows/</link><pubDate>Mon, 13 Jun 2011 18:38:49 +0000</pubDate><guid>https://rselbach.com/how-to-set-up-emacs-on-windows/</guid><description>&lt;p&gt;Just so I have it documented somewhere for future reference, here’s how to quickly set &lt;a href="http://www.gnu.org/software/emacs/"&gt;GNU Emacs&lt;/a&gt; up on Windows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download it from &lt;a href="http://ftp.gnu.org/gnu/emacs/windows/"&gt;http://ftp.gnu.org/gnu/emacs/windows/&lt;/a&gt;&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unzip it to, say, C:emacs or something like that&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set the environment variable HOME to C:emacs and include C:emacsbin to the PATH&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you want to have a “&lt;strong&gt;Open with _G_NU Emacs&lt;/strong&gt;” option on the context menu, just create a registry file (call it emacs.reg or whatever.reg) with the contents below and double-click it to import it into the registry.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;REGEDIT4 
[HKEY\_CLASSES\_ROOT*shell]

[HKEY\_CLASSES\_ROOT*shellemacsmenu] 
@=&amp;amp;ldquo;Open with &amp;amp;GNU; Emacs&amp;amp;rdquo;

[HKEY\_CLASSES\_ROOT*shellemacsmenucommand] 
@=&amp;amp;ldquo;C:emacsbinrunemacs.exe &amp;amp;rdquo;%1&amp;amp;ldquo;&amp;amp;rdquo;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;Et voilà&lt;/em&gt;! As well, for my preferred colour scheme, we need to use &lt;strong&gt;color-theme&lt;/strong&gt; from &lt;a href="http://www.nongnu.org/color-theme/"&gt;http://www.nongnu.org/color-theme/&lt;/a&gt; and set it up in your .emacs file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(add-to-list 'load-path &amp;quot;c:/emacs/.emacs.d&amp;quot;)
(require 'color-theme)
(color-theme-initialize)
(when (display-graphic-p)
 (color-theme-subtle-hacker)
&lt;/code&gt;&lt;/pre&gt;</description></item><item><title>Euler 15 in Python</title><link>https://rselbach.com/euler-15-in-python/</link><pubDate>Sat, 07 Aug 2010 22:39:27 +0000</pubDate><guid>https://rselbach.com/euler-15-in-python/</guid><description>&lt;p&gt;This one isn’t even funny…&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting in the top left corner of a &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;2 \times 2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; grid, there are 6 routes (without backtracking) to the bottom right corner.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/euler-15-in-python/p_015.gif"
 alt="The six routes through a 2 by 2 grid, using only right and down moves"
 loading="lazy"
 decoding="async" width="208" height="151"&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;How many routes are there through a &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;20 \times 20&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; grid?&lt;/p&gt;
&lt;p&gt;Your first thought would be to generate the routes, but for a &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;20 \times 20&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; grid, those amount to BILLIONS and you’d try to do it recursively too! Forget it.&lt;/p&gt;
&lt;p&gt;But if you have some CompSci-level math background, though, you’ll remember this one—reading &lt;a href="http://www.amazon.com/gp/product/0321637135?ie=UTF8&amp;amp;tag=itsuptoyoma-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0321637135"&gt;The Art of Computer Programming, Vol. 4&lt;/a&gt; also helps. It’s a matter of combinatorics and if we take &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;w&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; for the width and &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;h&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;h&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; for the height, all we need to calculate is:&lt;/p&gt;
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;h&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;mo stretchy="false"&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mi&gt;w&lt;/mi&gt;&lt;mo stretchy="false"&gt;!&lt;/mo&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mi&gt;h&lt;/mi&gt;&lt;mo stretchy="false"&gt;!&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mi mathvariant="normal"&gt;.&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;
\frac{(w+h)!}{w!\,h!}.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Every path consists of exactly 20 moves right and 20 moves down. Choosing which
20 of the 40 positions are right moves uniquely determines a path, so the
answer is the binomial coefficient &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo fence="true"&gt;(&lt;/mo&gt;&lt;mfrac linethickness="0px"&gt;&lt;mn&gt;40&lt;/mn&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;/mfrac&gt;&lt;mo fence="true"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;\binom{40}{20}&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; math &lt;span style="color:#f92672"&gt;import&lt;/span&gt; comb
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;width &lt;span style="color:#f92672"&gt;=&lt;/span&gt; height &lt;span style="color:#f92672"&gt;=&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;print(comb(width &lt;span style="color:#f92672"&gt;+&lt;/span&gt; height, width))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;math.comb&lt;/code&gt; computes the integer result directly, without constructing three
factorials. The answer is &lt;code&gt;137846528820&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Euler 11 in Python</title><link>https://rselbach.com/euler-11-in-python/</link><pubDate>Fri, 06 Aug 2010 17:19:56 +0000</pubDate><guid>https://rselbach.com/euler-11-in-python/</guid><description>&lt;p&gt;Project Euler’s problem #11 statement goes:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In the &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;20 \times 20&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; grid below, four numbers along a diagonal line have been marked in bold.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08&lt;br&gt;
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00&lt;br&gt;
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65&lt;br&gt;
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91&lt;br&gt;
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80&lt;br&gt;
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50&lt;br&gt;
32 98 81 28 64 23 67 10 &lt;strong&gt;26&lt;/strong&gt; 38 40 67 59 54 70 66 18 38 64 70&lt;br&gt;
67 26 20 68 02 62 12 20 95 &lt;strong&gt;63&lt;/strong&gt; 94 39 63 08 40 91 66 49 94 21&lt;br&gt;
24 55 58 05 66 73 99 26 97 17 &lt;strong&gt;78&lt;/strong&gt; 78 96 83 14 88 34 89 63 72&lt;br&gt;
21 36 23 09 75 00 76 44 20 45 35 &lt;strong&gt;14&lt;/strong&gt; 00 61 33 97 34 31 33 95&lt;br&gt;
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92&lt;br&gt;
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57&lt;br&gt;
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58&lt;br&gt;
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40&lt;br&gt;
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66&lt;br&gt;
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69&lt;br&gt;
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36&lt;br&gt;
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16&lt;br&gt;
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54&lt;br&gt;
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48&lt;/p&gt;
&lt;p&gt;The product of these numbers is &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;26&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;63&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;78&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;14&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mn&gt;788&lt;/mn&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mn&gt;696&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;26 \times 63 \times 78 \times 14 = 1\,788\,696&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;What is the greatest product of four adjacent numbers in any direction (up, down, left, right, or diagonally) in the &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;20&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;20 \times 20&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; grid?&lt;/p&gt;
&lt;p&gt;This one is remarkably easy but also was quite fun. I think it’s because it reminds me of the kind of work we’d do during our Algorithms classes during my first year in college. And so this one goes to my Algorithms teacher, Ricardo Vargas Dornelles—best teacher I’ve ever had too.&lt;/p&gt;
&lt;p&gt;Only four directions are necessary: reversing any group gives the same
product. Describing those directions as row and column offsets keeps one loop
responsible for every case and makes the boundary check explicit.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; math &lt;span style="color:#f92672"&gt;import&lt;/span&gt; prod
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;grid_text &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&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:#e6db74"&gt;08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48
&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;&amp;#34;&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;grid &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; [int(number) &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; number &lt;span style="color:#f92672"&gt;in&lt;/span&gt; line&lt;span style="color:#f92672"&gt;.&lt;/span&gt;split()]
&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; line &lt;span style="color:#f92672"&gt;in&lt;/span&gt; grid_text&lt;span style="color:#f92672"&gt;.&lt;/span&gt;splitlines()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;directions &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ((&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;), (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;), (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&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:#ae81ff"&gt;1&lt;/span&gt;))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;length &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;rows &lt;span style="color:#f92672"&gt;=&lt;/span&gt; len(grid)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;columns &lt;span style="color:#f92672"&gt;=&lt;/span&gt; len(grid[&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;largest &lt;span style="color:#f92672"&gt;=&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;for&lt;/span&gt; row &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(rows):
&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; column &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(columns):
&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; row_step, column_step &lt;span style="color:#f92672"&gt;in&lt;/span&gt; directions:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end_row &lt;span style="color:#f92672"&gt;=&lt;/span&gt; row &lt;span style="color:#f92672"&gt;+&lt;/span&gt; (length &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; row_step
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; end_column &lt;span style="color:#f92672"&gt;=&lt;/span&gt; column &lt;span style="color:#f92672"&gt;+&lt;/span&gt; (length &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; column_step
&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;not&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; end_row &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; rows &lt;span style="color:#f92672"&gt;and&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; end_column &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; columns):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&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; prod(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; grid[row &lt;span style="color:#f92672"&gt;+&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;*&lt;/span&gt; row_step][column &lt;span style="color:#f92672"&gt;+&lt;/span&gt; offset &lt;span style="color:#f92672"&gt;*&lt;/span&gt; column_step]
&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; offset &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(length)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(largest, product)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(largest)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Every valid group is visited exactly once. Since there are four fixed
directions and four values per product, the traversal is linear in the number
of grid cells. It prints &lt;code&gt;70600674&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Euler 10 in Python</title><link>https://rselbach.com/euler-10-in-python/</link><pubDate>Fri, 06 Aug 2010 01:07:00 +0000</pubDate><guid>https://rselbach.com/euler-10-in-python/</guid><description>&lt;p&gt;I decided to take on Project Euler’s problem #10. Its statement goes like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The sum of the primes below 10 is &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;7&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;17&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;2 + 3 + 5 + 7 = 17&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Find the sum of all the primes below two million.&lt;/p&gt;
&lt;p&gt;A Sieve of Eratosthenes finds every prime below the limit together instead of
running a separate divisibility test for every number. A &lt;code&gt;bytearray&lt;/code&gt; stores one
byte per candidate and supports crossing out a whole arithmetic progression at
once.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; math &lt;span style="color:#f92672"&gt;import&lt;/span&gt; isqrt
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;limit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2_000_000&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sieve &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bytearray(&lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x01&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; limit
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sieve[:&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x00\x00&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, isqrt(limit &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:#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:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; sieve[prime]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&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; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;*&lt;/span&gt; prime
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (limit &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; start) &lt;span style="color:#f92672"&gt;//&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;+&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; sieve[start:limit:prime] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x00&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&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;print(sum(number &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; number, is_prime &lt;span style="color:#f92672"&gt;in&lt;/span&gt; enumerate(sieve) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; is_prime))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Starting at &lt;code&gt;prime * prime&lt;/code&gt; is sufficient because smaller multiples already
have a smaller prime factor. The sieve runs in &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;O(n\log\log n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; time, uses
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;O(n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; space, and prints &lt;code&gt;142913828922&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Euler 9 in C</title><link>https://rselbach.com/euler-9-in-c/</link><pubDate>Wed, 04 Aug 2010 00:43:42 +0000</pubDate><guid>https://rselbach.com/euler-9-in-c/</guid><description>&lt;p&gt;The language can make a brute-force search faster, but eliminating unnecessary
work is better. Substituting &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;c = 1000 - a - b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; into the Pythagorean equation
and solving for &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; leaves only one variable to 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c" data-lang="c"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;stdio.h&amp;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:#66d9ef"&gt;int&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;main&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;void&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;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; sum &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1000&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; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; a &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;; a &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; sum &lt;span style="color:#f92672"&gt;/&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;; &lt;span style="color:#f92672"&gt;++&lt;/span&gt;a) {
&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:#66d9ef"&gt;int&lt;/span&gt; numerator &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sum &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (sum &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; a);
&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:#66d9ef"&gt;int&lt;/span&gt; denominator &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; (sum &lt;span style="color:#f92672"&gt;-&lt;/span&gt; a);
&lt;/span&gt;&lt;/span&gt;&lt;span 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; (numerator &lt;span style="color:#f92672"&gt;%&lt;/span&gt; denominator &lt;span style="color:#f92672"&gt;!=&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 style="color:#66d9ef"&gt;continue&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;const&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; numerator &lt;span style="color:#f92672"&gt;/&lt;/span&gt; denominator;
&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:#66d9ef"&gt;int&lt;/span&gt; c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; sum &lt;span style="color:#f92672"&gt;-&lt;/span&gt; a &lt;span style="color:#f92672"&gt;-&lt;/span&gt; b;
&lt;/span&gt;&lt;/span&gt;&lt;span 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; (a &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; c) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;printf&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;%d %d %d = %d&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, a, b, c, a &lt;span style="color:#f92672"&gt;*&lt;/span&gt; b &lt;span style="color:#f92672"&gt;*&lt;/span&gt; c);
&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 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&gt;&lt;/span&gt;&lt;span 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;fputs&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;no solution&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\n&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;, stderr);
&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 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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The divisibility check ensures that &lt;code&gt;b&lt;/code&gt; is an integer. This searches fewer
than &lt;code&gt;sum / 3&lt;/code&gt; candidates in constant space and prints
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;200&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;375&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;425&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;31&lt;/mn&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mn&gt;875&lt;/mn&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mn&gt;000&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;200 \times 375 \times 425 = 31\,875\,000&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;</description></item><item><title>Euler 9</title><link>https://rselbach.com/euler-9/</link><pubDate>Tue, 03 Aug 2010 17:45:17 +0000</pubDate><guid>https://rselbach.com/euler-9/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across Project Euler a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #9 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A Pythagorean triplet is a set of three natural numbers, &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo&gt;&amp;lt;&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo&gt;&amp;lt;&lt;/mo&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;a &amp;lt; b &amp;lt; c&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, for which:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mi mathvariant="normal"&gt;.&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;
a^2 + b^2 = c^2.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;For example, &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;3&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;4&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;9&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;16&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;25&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;5&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;3^2 + 4^2 = 9 + 16 = 25 = 5^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;There exists exactly one Pythagorean triplet for which &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;a + b + c = 1000&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Find the product &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;abc&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Using &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;c = 1000 - a - b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; removes one variable immediately. Substituting that
into &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mi&gt;c&lt;/mi&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;a^2 + b^2 = c^2&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; and solving for &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;b&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; gives&lt;/p&gt;
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;b&lt;/mi&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mfrac&gt;&lt;mrow&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;mrow&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mn&gt;1000&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mi&gt;a&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;/mfrac&gt;&lt;mi mathvariant="normal"&gt;.&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;
b = \frac{1000(1000 - 2a)}{2(1000 - a)}.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;That leaves only the possible values of &lt;code&gt;a&lt;/code&gt; to 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1000&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; a &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, total &lt;span style="color:#f92672"&gt;//&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; numerator &lt;span style="color:#f92672"&gt;=&lt;/span&gt; total &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (total &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; a)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; denominator &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; (total &lt;span style="color:#f92672"&gt;-&lt;/span&gt; a)
&lt;/span&gt;&lt;/span&gt;&lt;span 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; numerator &lt;span style="color:#f92672"&gt;%&lt;/span&gt; denominator:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&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; b &lt;span style="color:#f92672"&gt;=&lt;/span&gt; numerator &lt;span style="color:#f92672"&gt;//&lt;/span&gt; denominator
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; c &lt;span style="color:#f92672"&gt;=&lt;/span&gt; total &lt;span style="color:#f92672"&gt;-&lt;/span&gt; a &lt;span style="color:#f92672"&gt;-&lt;/span&gt; b
&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; a &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; b &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; c:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; print(a &lt;span style="color:#f92672"&gt;*&lt;/span&gt; b &lt;span style="color:#f92672"&gt;*&lt;/span&gt; c)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The divisibility check ensures that &lt;code&gt;b&lt;/code&gt; is a natural number. The triplet is
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mn&gt;200&lt;/mn&gt;&lt;mo separator="true"&gt;,&lt;/mo&gt;&lt;mn&gt;375&lt;/mn&gt;&lt;mo separator="true"&gt;,&lt;/mo&gt;&lt;mn&gt;425&lt;/mn&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;(200, 375, 425)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, so the program prints &lt;code&gt;31875000&lt;/code&gt; in linear time and constant
space.&lt;/p&gt;</description></item><item><title>Euler 6</title><link>https://rselbach.com/euler-6/</link><pubDate>Tue, 03 Aug 2010 17:35:28 +0000</pubDate><guid>https://rselbach.com/euler-6/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #6 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The sum of the squares of the first ten natural numbers is:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;msup&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mo&gt;⋯&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;385.&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;
1^2 + 2^2 + \cdots + 10^2 = 385.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;The square of the sum of the first ten natural numbers is:&lt;/p&gt;
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML" display="block"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mn&gt;1&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mo&gt;⋯&lt;/mo&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mn&gt;10&lt;/mn&gt;&lt;msup&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;msup&gt;&lt;mn&gt;55&lt;/mn&gt;&lt;mn&gt;2&lt;/mn&gt;&lt;/msup&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;3025.&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;
(1 + 2 + \cdots + 10)^2 = 55^2 = 3025.
&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;p&gt;Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;3025&lt;/mn&gt;&lt;mo&gt;−&lt;/mo&gt;&lt;mn&gt;385&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;2640&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;3025 - 385 = 2640&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.&lt;/p&gt;
&lt;p&gt;The two sums have closed forms, so there is no need to iterate at 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;100&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;total &lt;span style="color:#f92672"&gt;=&lt;/span&gt; n &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (n &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:#ae81ff"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sum_of_squares &lt;span style="color:#f92672"&gt;=&lt;/span&gt; n &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (n &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:#ae81ff"&gt;2&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&lt;/span&gt; n &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:#ae81ff"&gt;6&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;print(total &lt;span style="color:#f92672"&gt;*&lt;/span&gt; total &lt;span style="color:#f92672"&gt;-&lt;/span&gt; sum_of_squares)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This performs a constant number of integer operations and prints &lt;code&gt;25164150&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Euler 5</title><link>https://rselbach.com/euler-5/</link><pubDate>Tue, 03 Aug 2010 17:30:36 +0000</pubDate><guid>https://rselbach.com/euler-5/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #5 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?&lt;/p&gt;
&lt;p&gt;The number must be the least common multiple of every integer from 1 through
20. Python’s integer &lt;code&gt;lcm&lt;/code&gt; implements exactly that operation:&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; math &lt;span style="color:#f92672"&gt;import&lt;/span&gt; lcm
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;answer &lt;span style="color:#f92672"&gt;=&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 style="color:#66d9ef"&gt;for&lt;/span&gt; number &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;21&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; answer &lt;span style="color:#f92672"&gt;=&lt;/span&gt; lcm(answer, number)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(answer)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This prints &lt;code&gt;232792560&lt;/code&gt;. Folding the LCM keeps only the prime powers required
by the numbers seen so far, with no brute-force search.&lt;/p&gt;</description></item><item><title>Euler 4</title><link>https://rselbach.com/euler-4/</link><pubDate>Tue, 03 Aug 2010 17:26:51 +0000</pubDate><guid>https://rselbach.com/euler-4/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #4 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;9009&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;91&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;99&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;9009 = 91 \times 99&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Find the largest palindrome made from the product of two 3-digit numbers.&lt;/p&gt;
&lt;p&gt;Searching downward lets us stop as soon as the remaining products cannot beat
the best palindrome already found. Starting the inner loop at &lt;code&gt;a&lt;/code&gt; also avoids
checking both &lt;code&gt;a * b&lt;/code&gt; and &lt;code&gt;b * a&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;largest &lt;span style="color:#f92672"&gt;=&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;for&lt;/span&gt; a &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;999&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;99&lt;/span&gt;, &lt;span style="color:#f92672"&gt;-&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 style="color:#66d9ef"&gt;if&lt;/span&gt; a &lt;span style="color:#f92672"&gt;*&lt;/span&gt; a &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; largest:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&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; b &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(a, &lt;span style="color:#ae81ff"&gt;99&lt;/span&gt;, &lt;span style="color:#f92672"&gt;-&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; product &lt;span style="color:#f92672"&gt;=&lt;/span&gt; a &lt;span style="color:#f92672"&gt;*&lt;/span&gt; b
&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; product &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; largest:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&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; str(product) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; str(product)[::&lt;span style="color:#f92672"&gt;-&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; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; product
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(largest)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This prints &lt;code&gt;906609&lt;/code&gt; (&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mn&gt;993&lt;/mn&gt;&lt;mo&gt;×&lt;/mo&gt;&lt;mn&gt;913&lt;/mn&gt;&lt;mo&gt;=&lt;/mo&gt;&lt;mn&gt;906&lt;/mn&gt;&lt;mtext&gt; &lt;/mtext&gt;&lt;mn&gt;609&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;993 \times 913 = 906\,609&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;).&lt;/p&gt;</description></item><item><title>Euler 3</title><link>https://rselbach.com/euler-3/</link><pubDate>Tue, 03 Aug 2010 17:24:39 +0000</pubDate><guid>https://rselbach.com/euler-3/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #3 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The prime factors of 13195 are 5, 7, 13 and 29.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What is the largest prime factor of the number 600851475143 ?&lt;/p&gt;
&lt;p&gt;There is no need to build a list of every divisor and test each one for
primality. Instead, divide each factor out as soon as it is found. This makes
the remaining number smaller throughout the 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;number &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;600_851_475_143&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;largest_factor &lt;span style="color:#f92672"&gt;=&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:#66d9ef"&gt;while&lt;/span&gt; number &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:#ae81ff"&gt;0&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; largest_factor &lt;span style="color:#f92672"&gt;=&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; number &lt;span style="color:#f92672"&gt;//=&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;factor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; factor &lt;span style="color:#f92672"&gt;*&lt;/span&gt; factor &lt;span style="color:#f92672"&gt;&amp;lt;=&lt;/span&gt; number:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; number &lt;span style="color:#f92672"&gt;%&lt;/span&gt; factor &lt;span style="color:#f92672"&gt;==&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; largest_factor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; factor
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; number &lt;span style="color:#f92672"&gt;//=&lt;/span&gt; factor
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; factor &lt;span style="color:#f92672"&gt;+=&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; number &lt;span style="color:#f92672"&gt;&amp;gt;&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; largest_factor &lt;span style="color:#f92672"&gt;=&lt;/span&gt; number
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(largest_factor)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;After removing every possible factor up to the square root of the remaining
number, anything left must itself be prime. The answer is &lt;code&gt;6857&lt;/code&gt;, found in
constant space and at most square-root time.&lt;/p&gt;</description></item><item><title>Euler 8</title><link>https://rselbach.com/euler-8/</link><pubDate>Tue, 03 Aug 2010 12:50:50 +0000</pubDate><guid>https://rselbach.com/euler-8/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #8 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Find the greatest product of thirteen consecutive digits in the 1000-digit number.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;73167176531330624919225119674426574742355349194934&lt;br&gt;
96983520312774506326239578318016984801869478851843&lt;br&gt;
85861560789112949495459501737958331952853208805511&lt;br&gt;
12540698747158523863050715693290963295227443043557&lt;br&gt;
66896648950445244523161731856403098711121722383113&lt;br&gt;
62229893423380308135336276614282806444486645238749&lt;br&gt;
30358907296290491560440772390713810515859307960866&lt;br&gt;
70172427121883998797908792274921901699720888093776&lt;br&gt;
65727333001053367881220235421809751254540594752243&lt;br&gt;
52584907711670556013604839586446706324415722155397&lt;br&gt;
53697817977846174064955149290862569321978468622482&lt;br&gt;
83972241375657056057490261407972968652414535100474&lt;br&gt;
82166370484403199890008895243450658541227588666881&lt;br&gt;
16427171479924442928230863465674813919123162824586&lt;br&gt;
17866458359124566529476545682848912883142607690042&lt;br&gt;
24219022671055626321111109370544217506941658960408&lt;br&gt;
07198403850962455444362981230987879927244284909188&lt;br&gt;
84580156166097919133875499200524063689912560717606&lt;br&gt;
05886116467109405077541002256983155200055935729725&lt;br&gt;
71636269561882670428252483600823257530420752963450&lt;/p&gt;
&lt;p&gt;A sliding window avoids multiplying the same twelve digits again for every
position. Zeroes need a little care because they cannot be divided back out of
the running product, so the window tracks how many it contains.&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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;number &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&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:#e6db74"&gt;73167176531330624919225119674426574742355349194934
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;96983520312774506326239578318016984801869478851843
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;85861560789112949495459501737958331952853208805511
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;12540698747158523863050715693290963295227443043557
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;66896648950445244523161731856403098711121722383113
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;62229893423380308135336276614282806444486645238749
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;30358907296290491560440772390713810515859307960866
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;70172427121883998797908792274921901699720888093776
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;65727333001053367881220235421809751254540594752243
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;52584907711670556013604839586446706324415722155397
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;53697817977846174064955149290862569321978468622482
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;83972241375657056057490261407972968652414535100474
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;82166370484403199890008895243450658541227588666881
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;16427171479924442928230863465674813919123162824586
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;17866458359124566529476545682848912883142607690042
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;24219022671055626321111109370544217506941658960408
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;07198403850962455444362981230987879927244284909188
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;84580156166097919133875499200524063689912560717606
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;05886116467109405077541002256983155200055935729725
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#e6db74"&gt;71636269561882670428252483600823257530420752963450
&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;&amp;#34;&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;digits &lt;span style="color:#f92672"&gt;=&lt;/span&gt; [int(digit) &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; digit &lt;span style="color:#f92672"&gt;in&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;.&lt;/span&gt;join(number&lt;span style="color:#f92672"&gt;.&lt;/span&gt;split())]
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;window_size &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;13&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;zeroes &lt;span style="color:#f92672"&gt;=&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;largest &lt;span style="color:#f92672"&gt;=&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;for&lt;/span&gt; index, digit &lt;span style="color:#f92672"&gt;in&lt;/span&gt; enumerate(digits):
&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; digit &lt;span style="color:#f92672"&gt;==&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; zeroes &lt;span style="color:#f92672"&gt;+=&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 style="color:#66d9ef"&gt;else&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; digit
&lt;/span&gt;&lt;/span&gt;&lt;span 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; index &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; window_size:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; outgoing &lt;span style="color:#f92672"&gt;=&lt;/span&gt; digits[index &lt;span style="color:#f92672"&gt;-&lt;/span&gt; window_size]
&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; outgoing &lt;span style="color:#f92672"&gt;==&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; zeroes &lt;span style="color:#f92672"&gt;-=&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 style="color:#66d9ef"&gt;else&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; outgoing
&lt;/span&gt;&lt;/span&gt;&lt;span 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; index &lt;span style="color:#f92672"&gt;&amp;gt;=&lt;/span&gt; window_size &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;and&lt;/span&gt; zeroes &lt;span style="color:#f92672"&gt;==&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; largest &lt;span style="color:#f92672"&gt;=&lt;/span&gt; max(largest, product)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(largest)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each digit enters and leaves the product once, making this linear-time. It
prints &lt;code&gt;23514624000&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Euler 7</title><link>https://rselbach.com/euler-7/</link><pubDate>Mon, 02 Aug 2010 19:27:58 +0000</pubDate><guid>https://rselbach.com/euler-7/</guid><description>&lt;p&gt;So the other night I was a bit bored and decided to do something to pass the time. I first came across &lt;a href="http://projecteuler.net"&gt;Project Euler&lt;/a&gt; a while ago, but had never gone further than problem #1. Boredom is a great motivator and I went through problems #2 thru #9 last night and I decided to post my solutions in search of better ones. Feel free to comment with your suggestions.&lt;/p&gt;
&lt;p&gt;Project Euler’s Problem #7 statement is —&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What is the 10001st prime number?&lt;/p&gt;
&lt;p&gt;A sieve finds all the primes up to a limit in one pass. For the nth prime,
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;+&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;n(\log n + \log\log n)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; is an upper bound when &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;mo&gt;≥&lt;/mo&gt;&lt;mn&gt;6&lt;/mn&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;n \ge 6&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, so the sieve can be
sized without guessing or repeatedly growing 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;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;from&lt;/span&gt; math &lt;span style="color:#f92672"&gt;import&lt;/span&gt; ceil, isqrt, log
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;n &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;10_001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;limit &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ceil(n &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (log(n) &lt;span style="color:#f92672"&gt;+&lt;/span&gt; log(log(n))))
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;sieve &lt;span style="color:#f92672"&gt;=&lt;/span&gt; bytearray(&lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x01&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;) &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (limit &lt;span style="color:#f92672"&gt;+&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;sieve[:&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x00\x00&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&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(&lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, isqrt(limit) &lt;span style="color:#f92672"&gt;+&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 style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#f92672"&gt;not&lt;/span&gt; sieve[prime]:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;continue&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; start &lt;span style="color:#f92672"&gt;=&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;*&lt;/span&gt; prime
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; count &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (limit &lt;span style="color:#f92672"&gt;-&lt;/span&gt; start) &lt;span style="color:#f92672"&gt;//&lt;/span&gt; prime &lt;span style="color:#f92672"&gt;+&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; sieve[start : limit &lt;span style="color:#f92672"&gt;+&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; : prime] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;b&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;\x00&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;&lt;/span&gt; &lt;span style="color:#f92672"&gt;*&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;primes &lt;span style="color:#f92672"&gt;=&lt;/span&gt; (number &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; number, is_prime &lt;span style="color:#f92672"&gt;in&lt;/span&gt; enumerate(sieve) &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; is_prime)
&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; _ &lt;span style="color:#f92672"&gt;in&lt;/span&gt; range(n &lt;span style="color:#f92672"&gt;-&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; next(primes)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;print(next(primes))
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Multiples below &lt;code&gt;prime * prime&lt;/code&gt; were already crossed out by smaller primes.
The answer is &lt;code&gt;104743&lt;/code&gt;; for sieve limit &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;L&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;, it runs in
&lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;log&lt;/mi&gt;&lt;mo&gt;⁡&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;O(L\log\log L)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; time and &lt;span class="katex"&gt;&lt;math xmlns="http://www.w3.org/1998/Math/MathML"&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;O&lt;/mi&gt;&lt;mo stretchy="false"&gt;(&lt;/mo&gt;&lt;mi&gt;L&lt;/mi&gt;&lt;mo stretchy="false"&gt;)&lt;/mo&gt;&lt;/mrow&gt;&lt;annotation encoding="application/x-tex"&gt;O(L)&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt; space.&lt;/p&gt;</description></item><item><title>Why can’t people buy newer cars in Argentina?</title><link>https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/</link><pubDate>Fri, 21 May 2010 14:57:41 +0000</pubDate><guid>https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/</guid><description>&lt;p&gt;This is part of &lt;a href="https://rselbach.com/?cat_ID=239"&gt;a series of posts&lt;/a&gt; about Argentina and the City of Cordoba. These are little facts I wish I knew about before I came here as an expat.&lt;/p&gt;
&lt;p&gt;One of the first things I noticed when I arrived in Cordoba, Argentina, was the amount of old cars going around. And I’m not talking about 10-year-old cars, I’m talking about 30 years or so!&lt;/p&gt;
&lt;p&gt;You can really find some rarities such as the Citroën 3cv happily racing around town all the time. As a consequence of all that, you can also find a disproportionate amount of cars stalled on the streets, people trying to do something under the hood. It’s really amazing how many broken down cars you’ll see every day.&lt;/p&gt;
&lt;p&gt;&lt;a href="./IMG_01041.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;picture&gt;
 &lt;source type="image/webp" srcset="https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_414a77ac9864075e.webp 320w, https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_bfd6436e9631fe31.webp 480w, https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_20163477b9c28272.webp 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;img src="https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1.jpg"
 alt="Citroën 3cv"
 loading="lazy"
 decoding="async" width="644" height="484" srcset="https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_941e18ced89bd45.jpg 320w, https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_6d4ee77e08915b3f.jpg 480w, https://rselbach.com/why-cant-people-buy-newer-cars-in-argentina/IMG_0104_thumb1_hu_867c852382ad7aed.jpg 640w" sizes="(max-width: 1100px) 100vw, 1100px"&gt;
 &lt;/picture&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I used to wonder why that was. Now I understand.&lt;/p&gt;
&lt;p&gt;There is no credit in Argentina. Well, technically there is, but it’s so expensive that it’s as if it doesn’t exist. That’s why people normally need to buy stuff with cash upfront. Cars, of course, happen to be expensive and most people can’t save enough to buy newer cars like that. The same is true for several other goods, but cars happen to be the most visible symptom.&lt;/p&gt;
&lt;p&gt;From time to time, coincidently around election time, the federal government creates some credit program. These programs are temporary and limited in the number of people who can apply.&lt;/p&gt;
&lt;p&gt;And then there’s a second problem—informality. In order to avoid taxes and benefits, most companies hire people either with no documentation or with phoney pay information, e.g. if someone’s salary is, say, $1,000, the companies would register the employee as being paid $250 instead, thus being able to pay less taxes.&lt;/p&gt;
&lt;p&gt;And thus even with those government credit programs, most people can’t even qualify as they can’t show enough income.&lt;/p&gt;</description></item><item><title>How can I get rid of (not so) old books?</title><link>https://rselbach.com/how-can-i-get-rid-of-not-so-old-books/</link><pubDate>Sat, 27 Mar 2010 00:02:37 +0000</pubDate><guid>https://rselbach.com/how-can-i-get-rid-of-not-so-old-books/</guid><description>&lt;p&gt;I’m not a big believer in keeping books forever, even though I do keep some. Most books, however, I just want to read and pass along.&lt;/p&gt;
&lt;p&gt;It just so happens that I have a ton of books that I want to get rid of. I could throw them away, but it somehow feels wrong. Also, I could donate them to some school, but considering where I live now, I assume this too will bring me one hell of a bureaucratic nightmare. Also, most of the books are either in English or Portuguese, so schools would probably not use them anyway.&lt;/p&gt;
&lt;p&gt;With very few exceptions, all the books are either mathematics, economics, programming, or scifi. I thought I’d simply take the books to the office and give them away to anyone who’d like to have them.&lt;/p&gt;
&lt;p&gt;But then my wife gave me an idea. Instead of taking the books and giving them outright, I started sending random emails to a mailing list with geeky questions, often related to the book topic. Whoever answers correctly first, gets the book.&lt;/p&gt;
&lt;p&gt;This worked amazingly well.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;It created a fun environment for all involved. People actually want to get the answers right. Others learn by, well, learning the winning answer.&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For books that normally no one would care about, the fact that you have to win to get it suddenly adds value. It’s no longer a book no one wants, it now is an award.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;All of a sudden everybody loves me 😉&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Had I just announced I had a bunch of books, people would get by, pick a few they cared about and that would have been it. No fun. So every now and then I take a few books from home and then give them away to quiz winners.&lt;/p&gt;
&lt;p&gt;It’s actually lots of fun.&lt;/p&gt;</description></item><item><title>Why do things take so long in Argentina?</title><link>https://rselbach.com/why-do-things-take-so-long-in-argentina/</link><pubDate>Fri, 26 Mar 2010 14:12:20 +0000</pubDate><guid>https://rselbach.com/why-do-things-take-so-long-in-argentina/</guid><description>&lt;p&gt;Coincidences have a way of catching one’s attention, haven’t they? Yesterday I was riding the bus home with a colleague from work. I was telling him how my life here wasn’t perfect because I couldn’t buy a car. It turns out that I would need to hold a national ID card (called DNI) to purchase a car. Actually, one needs to obtain the DNI to do pretty much anything like opening a bank account. Interestingly enough, you need the DNI for small, simple tasks like ordering cable TV, phone lines, Internet access, etc, but one can easily buy a house – or many houses – without the DNI.&lt;/p&gt;
&lt;p&gt;The process of obtaining the DNI is simple. If you’re a citizen, your parents will have used your birth certificate to get your first DNI, which is mandatory from a very early age. Over the years, you are required to update your DNI when you reach certain ages (8, 16, 25, if I’m not mistaken) and everytime you change address. All you need is the original DNI and some address verification and you’re done. If you’re an expat like myself, you need your passport, your resident visa, and some address verification. Again, very simple. You go, apply and wait for the document to be ready. And that’s where the problem resides… in parts of the country, the DNI takes &lt;em&gt;years&lt;/em&gt; to be ready. Even in Buenos Aires, it can take more than one year for your DNI to be ready for pick-up.&lt;/p&gt;
&lt;p&gt;Now how could such a simple and yet much needed document take so long to be manufactured?&lt;/p&gt;
&lt;p&gt;So there I was, talking to this colleague about all of this. He told me that when he had to change his at 16, it took two years. And he’s a national. He told me it’s usually because government workers don’t have the incentive to work harder. He’s probably right, as this is the case everywhere else in the world. Still, this might explain why a simple document can take 2-3 weeks to be created, but not years. When I stepped out of my ride, I bumped into a girl who lives in our building. She told me she was frustrated because she had lost her DNI a couple of years back and the new one wasn’t ready yet. She had just went to the Identification Office to check. Coincidences… I then told her about my story and she told me so other tales of horror and I shared some of my own, like when I needed a document notarized and it took a month to do it. And that was it.&lt;/p&gt;
&lt;p&gt;Fast forward a day.&lt;/p&gt;
&lt;p&gt;My wife is currently having classes and she asked me to get her a textbook she needs. I went to this bookstore on my way to work. I strolled into the store and worked my way to the textbooks section and quickly found the one my wife needed. I picked it up and headed to the checkout, content that I wouldn’t have to hunt for the book in other stores. I held the book and the cash in my hand. “I’d like to take this” I told the lady at checkout, who in turn looks around and goes “Did you pick a number?”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;What. Fucking. Number.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;“What number?” I asked, confused. She then pointed me to this little thingamajig near the entrance. “I’m sorry, I don’t get it…” And then she half-mockingly explained to me that I had to pick the number and wait my turn for a salesperson to help me. “But I don’t need any help. I have the book and the money right here!”&lt;/p&gt;
&lt;p&gt;No use. I had to pick a number and wait for one of the two saleswomen to call it. So there I was, holding the book I wanted, with the cash to pay for it, standing by the checkout – where the lady was doing nothing by the way – and waiting for someone to “help me.” In any half-civilized place our business would have been over by now. The store would have made money and I would be on my way to work. Simple.&lt;/p&gt;
&lt;p&gt;“&lt;em&gt;Twenty!!!!!!!”&lt;/em&gt; shouted a lady. It was my number. So I went to her – looking pissed, I’m sure. I told her I’d like to please take this textbook. “Oh, certainly!” So she goes to a computer terminal and asks if I’m paying with a credit card. I wave the cash and say “&lt;em&gt;efectivo”&lt;/em&gt; to make sure she gets it. Then she asks what the book is for. &lt;em&gt;For reading, duh!&lt;/em&gt; I tell her that it’s for a course, then she types something and asks which school. “Lady, sorry but all I want to to take this textbook with me. I have the cash right here. I’ve had both the book and the cash in my hands for like 15 minutes already. All that’s missing is for that lady at checkout to get the money so I can get out of here.” And yes, I was pissed and I didn’t really sounded friendly and half the store was staring at me. She said apologetically, “I’m sorry, but I need to register who purchased the book and where it will be used.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Why?!?!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;“Lady, seriously, I don’t want to give any more information. I want to buy this book and that’s it. You either take my money or not. Your choice. Just let me know so I can get out of here.”&lt;/p&gt;
&lt;p&gt;The whole store was following our exchange. Everyone surely thinking that I was the mean foreigner who’s going to explore them and steal their water (it’s a local thing.) She told me to go to the checkout I had been to 15 minutes earlier. The lady took my money and asked if I wanted a receipt. D-U-H!&lt;/p&gt;
&lt;p&gt;So I realized it has to be a cultural thing. Bureaucracy is so entrenched in their lives that they don’t even realize it anymore. They just think it’s the best way of doing things. The problem I had to get a document notarized is a perfect example. I went to a notary, who then notarized my document. It took two days, which is already ridiculous, but still… the problem is that after he notarizes it, he has to send the document to the Notary Guild Office so that they in turn can verify that the notarizing notary was in fact a certified notary and that the notarization was correctly notarized. I am not even kidding.&lt;/p&gt;
&lt;p&gt;Argentina has a lot of great things. Things that I actually love. But in terms of bureaucracy, it’s really, really a mess. And the very people have been “infected” by it so that they are bureaucratic at heart. It’s actually really sad.&lt;/p&gt;</description></item><item><title>Suquia Creek</title><link>https://rselbach.com/suquia-creek/</link><pubDate>Wed, 27 Jan 2010 17:49:56 +0000</pubDate><guid>https://rselbach.com/suquia-creek/</guid><description>&lt;p&gt;&lt;a href="./image1.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/suquia-creek/image_thumb2.jpg"
 alt="image"
 loading="lazy"
 decoding="async" width="158" height="240"&gt;
&lt;/figure&gt;
&lt;/a&gt;Things are starting to get busier on my new project. It is, alas, still listed as Restricted Secret, which means I cannot really talk about its details. Nor could I even reveal its codename, even though codenames reveal nothing about what a project really is. For the sake of making referring to it slightly less annoying for me, I’ll refer to it using a made-up codename in the best traditions of the company’s history of naming things for lakes, peaks, creeks, and towns. So the secret project shall henceforth be known as &lt;strong&gt;Suquia Creek&lt;/strong&gt; (being the creek across from my house.)&lt;/p&gt;
&lt;p&gt;Since I cannot give any details on Suquia Creek, what could I possibly talk about? A lot, it turns out. I can talk about things I’m learning while working on the project.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Ah boy, am I learning!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It turns out Suquia Creek is going to be the longest, most complex project I’ve worked on. Until SC came along, the longest project of my life had been Lava Peak, which ran for about two years. But that included post-launch activities. We managed to go from ideation to shipping in just under a quarter (I even won an award because of that back in 2007.) Suquia Creek, on the other hand, is scheduled to ship in 2014! That’s nearly half a decade of work.&lt;/p&gt;
&lt;p&gt;As well, for earlier projects I only had to worry about software. Suquia Creek, however, is also hardware. And on the software side, off the top of my head, it involves —&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Processor µcode&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Chipset code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;BIOS extensions&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Firmware code&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Drivers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;An SDK&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;User-level apps&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And a few other things I can’t say without revealing more than I should.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Bottom line: it’s huge as far as I’m concerned! And we should support multiple versions of Windows and one distro release of Linux. It also involves several cross-functional, geographically-dispersed teams based mainly in Argentina and the US, but also with some smaller efforts coming out of China, India, and Israel. That amounts to six different timezones, for a current maximum time difference of 17 hours.&lt;/p&gt;
&lt;p&gt;And then we come back to schedule. I have no idea what I’ll be doing at home during this weekend, but I have to have a rough idea of what we’ll be delivering on, say, week 41 of 2013! &lt;em&gt;That assumes the world will not end in 2012, of course&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;This week we had our first engineering meeting to plan on a tentative schedule. Late next month I’ll be flying around between Silicon Valley and Silicon Forest to work out the (semi-)hard schedule. By then, we’ll actually have one internal alpha release in place already. After that, we’ll have another alpha before our first “release”, an internal proof of concept, which will then be used by customer as part of a (quasi-confidential) pilot. The customer? One of the world’s largest… well, can’t say what is their industry yet. It’s huge though.&lt;/p&gt;
&lt;p&gt;After that pilot I expect to be able to open up a bit on what the project actually does. In the meantime, I’m going to be sharing my learning experience.&lt;/p&gt;
&lt;p&gt;It’s going to be an exciting half-decade for me :–)&lt;/p&gt;</description></item><item><title>A Decade as a Linux Pro</title><link>https://rselbach.com/a-decade-as-a-linux-pro/</link><pubDate>Fri, 18 Dec 2009 18:44:49 +0000</pubDate><guid>https://rselbach.com/a-decade-as-a-linux-pro/</guid><description>&lt;p&gt;After &lt;a href="http://robteix.com/english/2009/12/16/the-grumpiest-post-ive-ever-written/"&gt;I recently accepted my old age&lt;/a&gt;, I was talking to some friends who, to their own surprise, found they were old too. We were talking about when each of us started &lt;em&gt;working&lt;/em&gt; with Linux and a friend noticed &lt;a href="http://ruda.livejournal.com/138637.html"&gt;he had been working with Linux for 10 years&lt;/a&gt;. That’s when &lt;em&gt;I&lt;/em&gt; realized it’s been a decade since I was first paid to work with the operating system.&lt;/p&gt;
&lt;p&gt;I had been using Linux for a little while. My first contact with it was in mid 1995. I had bought a computer magazine from the UK that had this pink CD-ROM with something called &lt;strong&gt;Linux-FT&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;One of the cool things about Linux-FT was that it had a licensed copy of the Motif window manager, which was pretty cool at the time. I had been using some RISC boxes in college running CDE, so I felt right at home.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://rselbach.com/images/posts/2009/12/infomagic.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/images/posts/2009/12/infomagic_thumb.jpg"
 alt="infomagic"
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/a&gt;Not long after that, a friend who was an administrator at a new ISP let me borrow his Walnut Creek CD-set containing Slackware 2.1 or 2.2 (I’m not sure anymore. Old age, remember?) Then I purchased a copy of the wonderful InfoMagic 5-CD set containing Slackware, Red Hat, mirrors of some FTP site and another distro I can’t remember either (&lt;em&gt;Shit! I’m old…&lt;/em&gt;)&lt;/p&gt;
&lt;p&gt;Then in 1999 I was hired to help this company migrate from Windows to Linux. It was the first time I was ever paid to do anything related to Linux.&lt;/p&gt;
&lt;p&gt;I went to work at Conectiva the next year, where I learned I didn’t know anything. &lt;em&gt;What the heck do I do now?&lt;/em&gt; That’s also where I learned like never before, made lots of friends, and found the love of my life. (&lt;em&gt;No, not another Linux distribution! I mean my wife! What’s wrong with you?)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In 2004 I left Conectiva and started working with one of the company’s founders on another Linux project. The work itself was interesting, but it was also my first contact with something that I would see a lot more in the future: the downright dishonesty of Linux entrepreneurs in Brazil. Of course, I then thought it was an isolated thing and decided I didn’t want to be a part of that and left the company in 2005.&lt;/p&gt;
&lt;p&gt;That’s when I came to Intel to work at the CSO, a “personal” project of Andy Grove. CSO had a simple mission: to foster Linux usage by financing and providing engineering to business with good ideas. How great is &lt;em&gt;that?&lt;/em&gt; I was going to work on my passion (Linux) and meet all kinds of people who shared it with me. What could possibly go wrong?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Oh boy.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In the following year I’d see things that would still make me sick years later. From businesspeople to self-appointed free software leaders, all I saw was guile and greed. It was such a disappointment that I requested a change. I stopped working with Brazilian businesses and projects completely, moving to support projects in other countries. Things were much better, which is another disappointment and one of the reasons why I hold us Brazilians in such low regard as a people.&lt;/p&gt;
&lt;p&gt;Outside Brazil things were very different and despite some funny things here and there, I am proud of the Linux work I’ve done, especially the megalarge project with the government of Venezuela. I even met Hugo Chavez, which is funny considering my political stand 🙂&lt;/p&gt;
&lt;p&gt;Regardless, I was also getting disappointed at other things as well. After years developing projects such as KDE, I was bored to death. I started witnessing things being done on other platforms and suddenly the Linux desktop just felt stale to me. &lt;em&gt;It was dull and lifeless&lt;/em&gt; and at the same time I was doing all these cool stuff on Windows.&lt;/p&gt;
&lt;p&gt;Add to all that the fact that KDE started getting full of kids adding more and more useless features… and politics… ever heard the one about the moppet who decided that all KDE About boxes should contain a thank-you note to American troops worldwide? And he wasn’t even American? You know what?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Screw Linux.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I dropped it like you wouldn’t believe. And it felt good. Not having to edit configuration files to do something simple made stuff pleasant again. I was introduced to Mac OS X and I loved it. It was Linux on steroids. All the good stuff without the kludge.&lt;/p&gt;
&lt;p&gt;No more politics. No more GNU Slash Linux. No more open source vs. free software. No revolutionary-audio-framework-of-the-month. No juvenile cockiness. No rWindoze. No Micro$oft.&lt;/p&gt;
&lt;p&gt;Just fun.&lt;/p&gt;
&lt;p&gt;After nearly 15 years, I’m truly free.&lt;/p&gt;</description></item><item><title>The Grumpiest Post I’ve Ever Written</title><link>https://rselbach.com/the-grumpiest-post-ive-ever-written/</link><pubDate>Wed, 16 Dec 2009 22:31:19 +0000</pubDate><guid>https://rselbach.com/the-grumpiest-post-ive-ever-written/</guid><description>&lt;p&gt;So today &lt;a href="http://twitter.com/sulagarcia/statuses/6728366820"&gt;I was called grumpy&lt;/a&gt;, which was inconsiderate and uncalled-for, so I did what every stable adult man would do. I complained to the one person who’s required by God and Country to always be supportive: my wife. “Who the hell does she think she is calling me grumpy?” I asked. And my wife went “well, you totally are,” which was an inconsiderate and uncalled-for answer. “Hey! You’re my wife! You’re &lt;em&gt;supposed&lt;/em&gt; to &lt;em&gt;love&lt;/em&gt; me!” and she was “I do love you! And that’s why I need to be honest.”&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Crap.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;It’s not that I could not live with the idea of being grumpy. It’s just that I don’t see myself like that. Or rather I didn’t. Now I kind of do.&lt;/p&gt;
&lt;p&gt;There are a couple of young kids who are all high on the whole GNU Fucking Slash Linux. Not unusual. When we’re young, the world does seem a lot more black and white. That’s fine. Kids will rebel and Linux… &lt;em&gt;Ops, sorry!&lt;/em&gt; GNU &lt;em&gt;Slash&lt;/em&gt; Linux provides an outlet from which all the rebellion that a young nerd can muster. I mean, it’s not like these kids get invited to the parties where popular kids do their rebelling, &lt;em&gt;right&lt;/em&gt;? Again, that’s all fine and dandy. Been there.&lt;/p&gt;
&lt;p&gt;But then these kids—high as they are on GNU Slash Kool-aid—forget that &lt;em&gt;they&lt;/em&gt;’re the new kids on the block. I mean, seriously people, Linux isn’t new anymore. There is a lot of people who came before you kids came out of daddy’s place. &lt;em&gt;No shit&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let’s face it, this is part of being young. You feel like you have all the answers and everyone else in the world failed to see The Truth. No problem with that. Except…&lt;/p&gt;
&lt;p&gt;Except that shit means I’m now officially old!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Motherfucker.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Today I see a friend commenting about Linux and one of these cocky, know-it-all kids jumped on his case like he was some kind of newbie or something. Hell, that actually got me angry. And then it hit me.&lt;/p&gt;
&lt;p&gt;Doesn’t &lt;em&gt;that&lt;/em&gt; make me grumpy?&lt;/p&gt;
&lt;p&gt;&lt;em&gt;So. Fucking. Be. It.&lt;/em&gt; I hereby accept the label of grumpy and shall sport it with pride.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://rselbach.com/images/posts/2009/12/Grumpiness.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/images/posts/2009/12/Grumpiness_thumb.jpg"
 alt="Grumpiness"
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So my friend works with and improves Linux for years and then a kid comes out of nowhere—having never done more than tweeting about Linux—and dares berate the guy? &lt;em&gt;Go suck an egg, son!&lt;/em&gt; The guy I’m talking about knows more Linux—and note how I dropped that slash shit and I don’t even care—on his pinky toe than all your friends together. &lt;em&gt;Kid!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Goddammit.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;-rst.&lt;/p&gt;
&lt;p&gt;P.S.: I’m not really angry. Or am I?&lt;/p&gt;</description></item><item><title>The Delusion of Not Being Deluded</title><link>https://rselbach.com/the-delusion-of-not-being-deluded/</link><pubDate>Tue, 15 Dec 2009 17:04:40 +0000</pubDate><guid>https://rselbach.com/the-delusion-of-not-being-deluded/</guid><description>&lt;p&gt;Ever came across someone who wanted to show off how independent and free their own thinking was? I have, many times. Discussing with these people is an amusing and yet wretched event. It’s like goping to a funny dentist. She may be funny, but she’s still going to bore a deep hole in your tooth. And boy, will it hurt! At least one of you will be laughing, right?&lt;/p&gt;
&lt;p&gt;I happen to know a couple of people who have got such strong conviction of their own uniqueness in being free from delusion that they cannot avoid being delusional themselves. Both are on this righteous crusade to extricate people from their self-foisted enslavement in the hands of evil Apple. &lt;em&gt;Wait, what?&lt;/em&gt; Yes, that’s right. From Apple! Apparently to some people, the operating system or the phone we use is the wrong one and we are pulling the wools over our own eyes into thinking otherwise. Fortunately for all of us, there are these cavaliers in shiny armor who will rescue us from ourselves. &lt;em&gt;Dude, seriously? How much more fucked-up delusional can one get?!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://rselbach.com/images/posts/2009/12/image.jpg"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/images/posts/2009/12/image_thumb.jpg"
 alt="image"
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don’t know if those guys did too much role-playing in their lives (and I don’t mean in bed) or it’s just too much &lt;a href="http://www.worldofwarcraft.com"&gt;World of Warcraft&lt;/a&gt;. I don’t really know. But if there is one thing I &lt;em&gt;do&lt;/em&gt; know, it’s that it’s delusional. Why is this so? It’s really a synthesis of a couple of things —&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Belief that one is right about something.&lt;/strong&gt; Being fair, we are all like this. If you ponder about it, this is the very definition of having an opinion. Nobody thinks they are wrong about their opinions.&lt;/p&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self-aggrandizement.&lt;/strong&gt; Again, to a variable degree, we’re all like that. We all love to think we are more than what we really are. Probably a defense mechanism. Without that, we’d all have killed ourselves by the time we reached puberty. Or maybe not all of us, because I am super awesome.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Strong illusions.&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What exactly is a delusion? A delusion is the belief that an illusion is real. We all have illusions, but a few go farther. They have such strong illusions that they begin believing in them.&lt;/p&gt;
&lt;p&gt;These people actually &lt;em&gt;believe&lt;/em&gt; that there is a war going on. A struggle between good and evil. A conflict between freedom and slavery. And let’s face it, &lt;em&gt;everyone&lt;/em&gt; wants to be one of the good guys. Except supervilains. Supervilains want to be on the wrong side. &lt;em&gt;Always&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Once you are set on the idea that (a) there is something like black-and-white good and evil, and that (b) the fucking clash is on, there’s really no stopping you. You need to fight to Good Fight. But since there really isn’t a war going on, the only way to fight it is by making up stuff as you go. So you pick your villains (say, Microsoft and Apple) and your heroes (say, Google and GNU) and you go out saving us ignorant civilians. From ourselves. And from Apple. Because Apple is, like, super evil. &lt;em&gt;Ah, if everyone was like Google…&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Holy. Shit. People.&lt;/em&gt; Grow the fuck up!&lt;/p&gt;
&lt;p&gt;Let’s all pretend for a moment that there really &lt;em&gt;is&lt;/em&gt; a war going on. That Google is out there fighting for our rights to free goodies in exchange for nothing at all. That Microsoft really owns the world’s Secret Cabal and Bill Gates sits down everyday to plan what evil deed needs to be done that day. Let’s say all of this is real. Wouldn’t it be a lot more productive for you to focus on your enemies and leave us civilians alone? Go ahead, wear your fancy penguin costume and take pictures of yourself peeing on the Microsoft logo. A lot of people will &lt;em&gt;love&lt;/em&gt; that. And more importantly, you will feel like you’ve finally accomplished something! You won a battle for the good guys! &lt;em&gt;Hooray!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;But please leave the rest of us alone.&lt;/p&gt;
&lt;p&gt;-rst.&lt;/p&gt;
&lt;p&gt;P.S.: a friend just sent me this picture, which says a lot. I think the message is: &lt;em&gt;Dude, don’t be a fag!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="http://theduty.tumblr.com/post/283874180/fyi"&gt;&lt;figure class="post-figure"&gt;&lt;img src="https://rselbach.com/images/posts/2009/12/image1.jpg"
 alt="image"
 loading="lazy"
 decoding="async"&gt;
&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;</description></item></channel></rss>