<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Ruben Vermeersch (rubenv) </title>
        <link>https://rocketeer.be/</link>
        <atom:link href="https://rocketeer.be/index.xml" rel="self" type="application/rss+xml" />
        <language>en-us</language>
        
        
        <description>Ruben Vermeersch (rubenv)</description>
        <lastBuildDate>Sat, 01 Feb 2025 03:01:43 &#43;0000</lastBuildDate>
        
        <item>
            <title>Home Assistant &amp; Volvo</title>
            <link>https://rocketeer.be/blog/2024/08/home-assistant-volvo/</link>
            <pubDate>Tue, 13 Aug 2024 09:11:24 &#43;0200</pubDate>
            
            <guid>https://rocketeer.be/blog/2024/08/home-assistant-volvo/</guid>
            <description>&lt;p&gt;Here&amp;rsquo;s a neat little trick for those of you using &lt;a href=&#34;https://www.home-assistant.io/&#34;&gt;Home
Assistant&lt;/a&gt; while also driving a Volvo.&lt;/p&gt;

&lt;p&gt;To get your Volvo driving data (fuel level, battery state, &amp;hellip;) into Home
Assistant, there&amp;rsquo;s the excellent
&lt;a href=&#34;https://github.com/Dielee/volvo2mqtt&#34;&gt;volvo2mqtt&lt;/a&gt; addon.&lt;/p&gt;

&lt;p&gt;One little annoyance is that every time it starts up, you will receive an
e-mail from Volvo with a two-factor authentication code, which you then have to
enter in Home Assistant.&lt;/p&gt;

&lt;p&gt;Fortunately, there&amp;rsquo;s a solution for that, you can automate this using the
built-in imap support of Home Assistant, with an automation such as this one:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;alias&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Volvo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;OTP&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;description&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;trigger&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;platform&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;event&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;event_type&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;imap_content&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;event_data&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;initial&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;sender&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;no-reply@volvocars.com&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;subject&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Your&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Volvo&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;ID&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;Verification&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;code&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;condition&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;action&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;service&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;mqtt.publish&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;metadata&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;{}&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;data&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;topic&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;volvoAAOS2mqtt/otp_code&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;payload&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&amp;gt;&lt;span class=&#34;sd&#34;&gt;-
&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;        {{ trigger.event.data[&amp;#39;text&amp;#39;] | regex_findall_index(find=&amp;#39;Your Volvo ID verification code is:\s+(\d+)&amp;#39;, index=0) }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;-&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;service&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;imap.delete&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;data&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;entry&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ trigger.event.data[&amp;#39;entry_id&amp;#39;] }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;uid&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;{{ trigger.event.data[&amp;#39;uid&amp;#39;] }}&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;mode&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;single&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will post the OTP code to the right location and then delete the message
from your inbox (if you&amp;rsquo;re using Google Mail, that means archiving it).&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2024/08/home-assistant-volvo/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Recent tech reading</title>
            <link>https://rocketeer.be/blog/2021/03/tech-reading/</link>
            <pubDate>Tue, 09 Mar 2021 22:46:29 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2021/03/tech-reading/</guid>
            <description>

&lt;p&gt;So much things going on these days, it&amp;rsquo;s already shaping up to be a pretty
crazy year, in the good sense. Pretty much &lt;a href=&#34;https://rocketeer.be/blog/2021/01/twenty-twentyone&#34;&gt;as I
predicted&lt;/a&gt; at the start of the year, though it
must be said that 2020 didn&amp;rsquo;t exactly raise the bar much. Pretty easy to clear
that hurdle.&lt;/p&gt;

&lt;p&gt;But that&amp;rsquo;s for another day. For now, here&amp;rsquo;s some interesting things I&amp;rsquo;ve been
reading recently, in no particular order / theme:&lt;/p&gt;

&lt;h2 id=&#34;modules-monoliths-and-microservices-https-tailscale-com-blog-modules-monoliths-and-microservices&#34;&gt;&lt;a href=&#34;https://tailscale.com/blog/modules-monoliths-and-microservices/&#34;&gt;Modules, monoliths, and microservices&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Pretty common sense way of looking at this whole discussion. I&amp;rsquo;ve seen both
ends of the spectrum and as always the right answer is: it depends. Inform
yourself and choose wisely.&lt;/p&gt;

&lt;p&gt;There certainly isn&amp;rsquo;t a solution that works for everyone, in every situation.&lt;/p&gt;

&lt;h2 id=&#34;you-need-to-be-able-to-run-your-system-http-catern-com-run-html&#34;&gt;&lt;a href=&#34;http://catern.com/run.html&#34;&gt;You need to be able to run your system&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;So much truth in this one. It requires a bit of investment, but it&amp;rsquo;s one of
those things that act as a force multiplier: it speeds up developers, giving
you faster development, more head-space to build a solid product and more time
to focus on what actually matters.&lt;/p&gt;

&lt;p&gt;Just consider the inverse: if you make their day jobs as cumbersome and
frustrating as possible, how do you expect your development team to perform?&lt;/p&gt;

&lt;p&gt;Any project I&amp;rsquo;ve helped roll this way of working out has benefited massively,
so I recommend it each and every time. Talk to me if you need help with this.&lt;/p&gt;

&lt;h2 id=&#34;breaking-down-and-fixing-kubernetes-https-itnext-io-breaking-down-and-fixing-kubernetes-4df2f22f87c3&#34;&gt;&lt;a href=&#34;https://itnext.io/breaking-down-and-fixing-kubernetes-4df2f22f87c3&#34;&gt;Breaking down and fixing Kubernetes&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;As an ops person, I&amp;rsquo;m a big fan of these kind of fire drills, where you
deliberatly damage a system and then try to fix it. Doing this as an exercise,
when things aren&amp;rsquo;t on fire, gives you so much more confidence when
things do break down for real.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2021/03/tech-reading/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>2021</title>
            <link>https://rocketeer.be/blog/2021/01/twenty-twentyone/</link>
            <pubDate>Sun, 03 Jan 2021 13:29:09 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2021/01/twenty-twentyone/</guid>
            <description>&lt;p&gt;And suddenly, before you notice it, the year has passed. And what a year it has
been&amp;hellip;&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s easy to brush 2020 off as a year to quickly forget, given the pandemic we
suddenly find ourselves in. But I&amp;rsquo;d rather not. Looking back, despite everthing
we took for granted but currently can no longer do, it&amp;rsquo;s been a year full of
great experiences, new friends, new business, launching things and lots of joy
with the family.&lt;/p&gt;

&lt;p&gt;I for one am very optimistic and excited what 2021 brings in terms of plot
twists. You can&amp;rsquo;t always predict what will come, but flexibility goes a long
way. Onwards and upwards!&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2021/01/twenty-twentyone/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Angular: ng not watching for changes</title>
            <link>https://rocketeer.be/blog/2020/01/angular-linux-watch/</link>
            <pubDate>Tue, 28 Jan 2020 19:15:44 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2020/01/angular-linux-watch/</guid>
            <description>&lt;p&gt;Running Linux and noticed that &lt;code&gt;ng serve&lt;/code&gt; or &lt;code&gt;ng test&lt;/code&gt; don&amp;rsquo;t observe changes
and thus won&amp;rsquo;t reload/test automatically?&lt;/p&gt;

&lt;p&gt;This might be caused by simply having too many files in your tree, causing your
system to hit an internal limit. This limit can be raised using the following
command:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You&amp;rsquo;ll have to do this after every boot. Want it persistently? Use the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo fs.inotify.max_user_watches=524288 | sudo tee /etc/sysctl.d/40-max-user-watches.conf
sudo sysctl --system
&lt;/code&gt;&lt;/pre&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2020/01/angular-linux-watch/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Rust: First Impressions</title>
            <link>https://rocketeer.be/blog/2020/01/rust-first-impressions/</link>
            <pubDate>Tue, 14 Jan 2020 21:03:15 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2020/01/rust-first-impressions/</guid>
            <description>&lt;p&gt;I&amp;rsquo;ve been studying the &lt;a href=&#34;https://www.rust-lang.org/&#34;&gt;Rust Programming Language&lt;/a&gt;
over the holidays, here are some of my first impressions. My main interest in
Rust is compiling performance-critical code to WebAssembly for usage in the
browser.&lt;/p&gt;

&lt;p&gt;Rust is an ambitious language: it tries to eliminate broad categories of
browser errors by detecting them during compilation. This requires more help
from the programmer: reasoning about what a program does exactly is famously
impossible (&lt;a href=&#34;https://en.wikipedia.org/wiki/Halting_problem&#34;&gt;the halting problem&lt;/a&gt;),
but that doesn&amp;rsquo;t mean we can&amp;rsquo;t think about some aspects, provided that we give
the compiler the right inputs. Memory management is the big thing in Rust where
this applies. By indicating where a value is owned and where it is only
temporarily borrowed, the compiler is able to infer the life-cycle of values.
Similar things apply for type safety, handling errors, multi-threading and
preventing null references.&lt;/p&gt;

&lt;p&gt;All very cool off-course, but nothing in life is for free: it requires a much
higher level of precise input with regards to what exactly you&amp;rsquo;re trying to
achieve. So programming in Rust is less careless than other languages, but the
end result is guaranteed correctness. I&amp;rsquo;d say that&amp;rsquo;s worth it.&lt;/p&gt;

&lt;p&gt;This very strict mode of compilation also means that the compiler is &lt;em&gt;very&lt;/em&gt;
picky about what it accepts. You can expect many error messages and much
fighting (initially) to even get your program to compile. The error messages
are very good though, so usually (but not always) they give a pretty good
indication of what to fix. And once it compiles you&amp;rsquo;re rather certain that the
result is good.&lt;/p&gt;

&lt;p&gt;Another consequence is that Rust is by no means a small language. Compared to
the rather succinct &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt;, there&amp;rsquo;s an enormous amount of
concepts and syntax. All needed, but it certainly doesn&amp;rsquo;t make things easier to
read.&lt;/p&gt;

&lt;p&gt;Other random thoughts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It&amp;rsquo;s a mistake to see a reference as a pointer. They&amp;rsquo;re not the same thing,
but it&amp;rsquo;s very easy to confuse them while learning Rust. Thinking about
moving ownership takes some adaptation.&lt;/li&gt;
&lt;li&gt;Lifetimes are hard and confusing at first. This is one of the points where I
feel you spend more attention to getting the language right than the actual
functionality of your code.&lt;/li&gt;
&lt;li&gt;Rust has the same composable IO abstractions
(&lt;a href=&#34;https://doc.rust-lang.org/std/io/index.html&#34;&gt;Read/Write&lt;/a&gt;) as in the Go &lt;a href=&#34;https://godoc.org/io&#34;&gt;io
package&lt;/a&gt;. These are excellent and a joy to work with.&lt;/li&gt;
&lt;li&gt;My main worry is the complexity of the language: each new corner-case of
correctness will lead to the addition of more language complexity. Have we
reached the end or will things keep getting harder? One example of where the
model already feels like it&amp;rsquo;s reaching the limits is &lt;code&gt;RefCell&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all, I&amp;rsquo;d say Rust is a good addition to the toolbox, for places where it
makes sense. But I don&amp;rsquo;t foresee it replacing Go yet as my go-to language on
the backend. It all comes down to the situation, finding the right balance
between the need for performance/correctness and productivity: the right tool
for the job. To be continued.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2020/01/rust-first-impressions/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Go: io.Reader gotchas</title>
            <link>https://rocketeer.be/blog/2019/11/go-reader-gotchas/</link>
            <pubDate>Mon, 25 Nov 2019 19:47:20 &#43;0200</pubDate>
            
            <guid>https://rocketeer.be/blog/2019/11/go-reader-gotchas/</guid>
            <description>&lt;p&gt;I&amp;rsquo;ve really come to appreciate the elegance in the &lt;a href=&#34;https://godoc.org/io&#34;&gt;io&lt;/a&gt;
abstractions in &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt;. The seemingly simple patterns of
&lt;code&gt;io.Reader&lt;/code&gt; and &lt;code&gt;io.Writer&lt;/code&gt; open up a world of easily composable data
pipelines.&lt;/p&gt;

&lt;p&gt;Need to add compression? Just wrap the &lt;code&gt;Writer&lt;/code&gt; with a &lt;code&gt;gzip.Writer&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;But there are some subtleties to be aware off, that might bite you.&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s have a look at the description of &lt;code&gt;io.Reader.Read()&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Read(p []byte) (n int, err error)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Read reads up to len(p) bytes into p. It returns the number of bytes read (0
&amp;lt;= n &amp;lt;= len(p)) and any error encountered. Even if Read returns n &amp;lt; len(p),
it may use all of p as scratch space during the call. If some data is
available but not len(p) bytes, Read conventionally returns what is
available instead of waiting for more.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is fairly straightforward. You call &lt;code&gt;Read()&lt;/code&gt; with a byte slice, which it
may fill up. The key point here being &lt;em&gt;may&lt;/em&gt;. Most IO sources (e.g. a file) will
generally read the full buffer, until you reach the end of the file.&lt;/p&gt;

&lt;p&gt;But not all of them. For instance, a &lt;code&gt;gzip.Writer&lt;/code&gt; tends to do incomplete
reads, requiring multiple &lt;code&gt;Read()&lt;/code&gt; calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: If you need to read a buffer in full, use
&lt;a href=&#34;https://godoc.org/io#ReadFull&#34;&gt;&lt;code&gt;io.ReadFull()&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;Read()&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When Read encounters an error or end-of-file condition after successfully
reading n &amp;gt; 0 bytes, it returns the number of bytes read. It may return the
(non-nil) error from the same call or return the error (and n == 0) from a
subsequent call. An instance of this general case is that a Reader returning
a non-zero number of bytes at the end of the input stream may return either
err == EOF or err == nil. The next Read should return 0, EOF.&lt;/p&gt;

&lt;p&gt;Callers should always process the n &amp;gt; 0 bytes returned before considering
the error err. Doing so correctly handles I/O errors that happen after
reading some bytes and also both of the allowed EOF behaviors.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means it&amp;rsquo;s perfectly legal to return both &lt;code&gt;n&lt;/code&gt; (and thus read a number of
bytes) and an error at the same time.&lt;/p&gt;

&lt;p&gt;It also means that the standard pattern of immediately checking for an error is
wrong:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// Don&amp;#39;t do this
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Read&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// Handle err
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;o&#34;&gt;//&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Do&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;something&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;and&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;buf&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Always process &lt;code&gt;n / buf&lt;/code&gt; first, then check for the presence of an error.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Implementations of Read are discouraged from returning a zero byte count
with a nil error, except when len(p) == 0. Callers should treat a return of
0 and nil as indicating that nothing happened; in particular it does not
indicate EOF.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The important take-away here: always check for &lt;code&gt;err == io.EOF&lt;/code&gt;, some
implementations might give you an empty read even if there is still data to
come.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Running into either of these corner cases is generally rare, since most IO
sources are quite well-behaved. But being aware of the corner cases will save
you a massive amount of debugging once you do run into them.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2019/11/go-reader-gotchas/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Go: JSON and broken APIs</title>
            <link>https://rocketeer.be/blog/2019/10/go-json-broken-apis/</link>
            <pubDate>Tue, 22 Oct 2019 20:19:47 &#43;0200</pubDate>
            
            <guid>https://rocketeer.be/blog/2019/10/go-json-broken-apis/</guid>
            <description>&lt;p&gt;If you&amp;rsquo;ve ever used &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt; to decode the JSON response
returned by a PHP API, you&amp;rsquo;ll probably have ran into this error:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;json: cannot unmarshal array into Go struct field Obj.field of type map[string]string
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The problem here being that PHP, rather than returning the empty object you
expected (&lt;code&gt;{}&lt;/code&gt;), returns an empty array (&lt;code&gt;[]&lt;/code&gt;). Not completely unexpected: in
PHP there&amp;rsquo;s no difference between maps/objects and arrays.&lt;/p&gt;

&lt;p&gt;Sometimes you can fix the server:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;return (object)$mything;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This ensures that an empty &lt;code&gt;$mything&lt;/code&gt; becomes &lt;code&gt;{}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But that&amp;rsquo;s not always possible, you might have to work around it on the client.
With Go, it&amp;rsquo;s not all that hard.&lt;/p&gt;

&lt;p&gt;First, define a custom type for your object:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MyObj&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;Field&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`json:&amp;#34;field&amp;#34;`&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Becomes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MyField&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MyObj&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;Field&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;MyField&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;`json:&amp;#34;field&amp;#34;`&lt;/span&gt;
    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then implement the &lt;a href=&#34;https://godoc.org/encoding/json#Unmarshaler&#34;&gt;&lt;code&gt;Unmarshaler&lt;/code&gt; interface&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;MyField&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;UnmarshalJSON&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;  
    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Equal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;[]&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

    &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;t&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;json&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Unmarshal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;m&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And that&amp;rsquo;s it! JSON deserialization will now gracefully ignore empty arrays returned by PHP.&lt;/p&gt;

&lt;p&gt;Some things of note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The method is defined on a pointer receiver (&lt;code&gt;*MyField&lt;/code&gt;). This is needed to correctly update the underlying map.&lt;/li&gt;
&lt;li&gt;I&amp;rsquo;m casting the &lt;code&gt;t&lt;/code&gt; object to &lt;code&gt;map[string]string&lt;/code&gt;. This avoids infinite recursion when we later call &lt;code&gt;json.Unmarshal()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2019/10/go-json-broken-apis/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Retro Operations</title>
            <link>https://rocketeer.be/blog/2019/05/retro-operations/</link>
            <pubDate>Tue, 21 May 2019 16:40:03 &#43;0200</pubDate>
            
            <guid>https://rocketeer.be/blog/2019/05/retro-operations/</guid>
            <description>&lt;p&gt;In his post &lt;a href=&#34;http://www.righto.com/2019/04/iconic-consoles-of-ibm-system360.html&#34;&gt;Iconic consoles of the IBM System/360 mainframes, 55 years
old&lt;/a&gt;, Ken
Shirrif gives a beautiful overview of how IBM mainframes were operated.&lt;/p&gt;

&lt;p&gt;I particularly liked this bit:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The second console function was &amp;ldquo;operator intervention&amp;rdquo;: program debugging
tasks such as examining and modifying memory or registers and setting
breakpoints. The Model 30 console controls below were used for operator
intervention. To display memory contents, the operator selected an address with
the four hexadecimal dials on the left and pushed the Display button,
displaying data on the lights above the dials. To modify memory, the operator
entered a byte using the two hex dials on the far right and pushed the Store
button. (Although the Model 30 had a 32-bit architecture, it operated on one
byte at a time, trading off speed for lower cost.) The Address Compare knob in
the upper right set a breakpoint.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;img src=&#34;https://rocketeer.be/blog/2019/05/sys360-debug.jpg&#34; alt=&#34;IBM System/360 Model 30 console, lower part&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Debugging a program was built right into the hardware, to be performed at the
console of the machine. Considering the fact that these machines were usually
placed in rooms optimized for the machine rather than the human, that must have
been a difficult job. Think about that the next time you&amp;rsquo;re poking at a
Kubernetes cluster using your laptop, in the comfort of your home.&lt;/p&gt;

&lt;p&gt;Also recommended is the book &lt;a href=&#34;https://www.goodreads.com/book/show/784082.Core_Memory&#34;&gt;Core Memory: A Visual Survey of Vintage
Computers&lt;/a&gt;. It really
shows the intricate beauty of some of the earlier computers. It also shows how
incredibly cumbersome these machines must have been to handle.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://rocketeer.be/blog/2019/05/core-memory.jpg&#34; alt=&#34;Core Memory: A Visual Survey of Vintage Computers&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Even when you&amp;rsquo;re in IT operations, it&amp;rsquo;s getting more and more rare to see
actual hardware and that&amp;rsquo;s probably a good thing. It never hurts to look at
history to get a taste of how far we&amp;rsquo;ve come. Life in operations has never been
more comfortable: let&amp;rsquo;s enjoy it by celebrating the past!&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2019/05/retro-operations/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>New beginnings</title>
            <link>https://rocketeer.be/blog/2019/05/new-beginnings/</link>
            <pubDate>Tue, 07 May 2019 17:03:21 &#43;0200</pubDate>
            
            <guid>https://rocketeer.be/blog/2019/05/new-beginnings/</guid>
            <description>&lt;p&gt;A couple of weeks ago our first-born daughter appeared into my life. All the
clichés of what this miracle does with a man are very well true. Not only is this
(quite literally) the start of a new life, it also gives you a pause to reflect
on your own life.&lt;/p&gt;

&lt;p&gt;Around the same time I&amp;rsquo;ve finished working on the project that has occupied
most of my time over the past years: helping a software-as-a-service company
completely modernize and rearchitect their software stack, to help it grow
further in the coming decade.&lt;/p&gt;

&lt;p&gt;Going forward, I&amp;rsquo;ll be applying the valuable lessons learned while doing this,
combined with all my previous experiences, as a consultant. More
specifically I&amp;rsquo;ll be focusing on DevOps and related concerns. More
information on that can be found on &lt;a href=&#34;https://rocketeer.be/consulting/&#34;&gt;this page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also have a new business venture in the works, but that&amp;rsquo;s the subject of a
future post.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2019/05/new-beginnings/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Let&#39;s talk about the developer experience</title>
            <link>https://rocketeer.be/blog/2018/10/awsug-dev-exp/</link>
            <pubDate>Fri, 05 Oct 2018 21:58:35 &#43;0000</pubDate>
            
            <guid>https://rocketeer.be/blog/2018/10/awsug-dev-exp/</guid>
            <description>&lt;p&gt;Yesterday, at the &lt;a href=&#34;https://www.meetup.com/AWS-User-Group-Belgium/&#34;&gt;AWS User Group Belgium
Meetup&lt;/a&gt; I presented a short
lightning talk. It was a call to action for the fact that operations people
should pay more attention to the developer experience.&lt;/p&gt;

&lt;p&gt;Annotated slides of the talk can be found &lt;a href=&#34;https://rocketeer.be/articles/awsug-dev-exp/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is an important subject to me: how can we make sure developers stay
productive in the ever more complex environment of the cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://rocketeer.be/articles/awsug-dev-exp/&#34;&gt;&lt;img src=&#34;https://rocketeer.be/blog/2018/10/awsug-dev-exp.jpg&#34; alt=&#34;Let&#39;s talk about the developer experience&#34; /&gt;&lt;/a&gt;
&lt;em&gt;Photo: &lt;a href=&#34;https://twitter.com/ndemoor&#34;&gt;Nils De Moor&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2018/10/awsug-dev-exp/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Jupyter lab with an Octave kernel</title>
            <link>https://rocketeer.be/blog/2018/03/jupyter-octave/</link>
            <pubDate>Wed, 07 Mar 2018 18:44:11 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2018/03/jupyter-octave/</guid>
            <description>&lt;p&gt;&lt;a href=&#34;https://www.gnu.org/software/octave/&#34;&gt;Octave&lt;/a&gt; is a good choice for getting
some serious computing done (it&amp;rsquo;s largely an open-source Matlab). But for
interactive exploration, it feels a bit awkward. If you&amp;rsquo;ve done any data
science work lately, you&amp;rsquo;ll undoubtedly have used the fantastic
&lt;a href=&#34;http://jupyter.org/&#34;&gt;Jupyter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s a way to combine both and have the great UI of Jupyter with the
processing core of Octave:&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://github.com/rubenv/jupyter-octave&#34;&gt;&lt;img src=&#34;https://rocketeer.be/blog/2018/03/jupyter-octave.png&#34; alt=&#34;Jupyter lab with an Octave kernel&#34; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve built a variant of the standard Jupyter Docker images that uses Octave as
a kernel, to make it trivial to run this combination. &lt;a href=&#34;https://github.com/rubenv/jupyter-octave&#34;&gt;You can find it
here&lt;/a&gt;.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2018/03/jupyter-octave/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Go: debugging multiple response.WriteHeader calls</title>
            <link>https://rocketeer.be/blog/2018/01/multiple-response-writeheader-calls/</link>
            <pubDate>Fri, 26 Jan 2018 16:11:43 &#43;0100</pubDate>
            
            <guid>https://rocketeer.be/blog/2018/01/multiple-response-writeheader-calls/</guid>
            <description>&lt;p&gt;Say you&amp;rsquo;re building a HTTP service in &lt;a href=&#34;https://golang.org/&#34;&gt;Go&lt;/a&gt; and suddenly it starts giving you these:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;http: multiple response.WriteHeader calls
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Horrible when that happens, right?&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s not always very easy to figure out why you get them and where they come
from. Here&amp;rsquo;s a hack to help you trace them back to their origin:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;kd&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;debugLogger&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;struct&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;d&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;debugLogger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;byte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;n&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;int&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
	&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;strings&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Contains&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;multiple response.WriteHeader&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
		&lt;span class=&#34;nx&#34;&gt;debug&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;PrintStack&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
	&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
	&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Stderr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Write&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Now use the logger with your http.Server:
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;logger&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;New&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;debugLogger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{},&lt;/span&gt; &lt;span class=&#34;s&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;Addr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;     &lt;span class=&#34;s&#34;&gt;&amp;#34;:3001&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;Handler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;  &lt;span class=&#34;nx&#34;&gt;s&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;ErrorLog&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Fatal&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;server&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;ListenAndServe&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will output a nice stack trace whenever it happens.
Happy hacking!&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2018/01/multiple-response-writeheader-calls/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Distrinet R&amp;D Bites</title>
            <link>https://rocketeer.be/blog/2017/12/distrinet-rd-bites/</link>
            <pubDate>Mon, 04 Dec 2017 18:49:02 &#43;0000</pubDate>
            
            <guid>https://rocketeer.be/blog/2017/12/distrinet-rd-bites/</guid>
            <description>&lt;p&gt;The &lt;a href=&#34;https://distrinet.cs.kuleuven.be/&#34;&gt;Distrinet Research Group&lt;/a&gt; at &lt;a href=&#34;https://www.kuleuven.be/&#34;&gt;KULeuven&lt;/a&gt; (where I studied!), recently asked me to speak about &amp;ldquo;Cloud Native&amp;rdquo; at one of their R&amp;amp;D Bites sessions. My talk covered Kubernetes, cloud automation and all the cool new things we can do in this brave new cloud native world.&lt;/p&gt;

&lt;p&gt;Annotated slides of the talk can be found &lt;a href=&#34;https://rocketeer.be/articles/distrinet-rd-bites-2017/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://rocketeer.be/articles/distrinet-rd-bites-2017/&#34;&gt;&lt;img src=&#34;https://rocketeer.be/articles/distrinet-rd-bites-2017/00.png&#34; alt=&#34;Experiences in building cloud-native businesses: the Ticketmatic case&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2017/12/distrinet-rd-bites/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>CoreOS Fest 2017</title>
            <link>https://rocketeer.be/blog/2017/06/coreos-fest-2017/</link>
            <pubDate>Mon, 12 Jun 2017 09:51:52 &#43;0000</pubDate>
            
            <guid>https://rocketeer.be/blog/2017/06/coreos-fest-2017/</guid>
            <description>&lt;p&gt;&lt;a href=&#34;https://coreos.com/fest/&#34;&gt;CoreOS Fest 2017&lt;/a&gt; happened earlier this month in San Francisco. I had the joy of attending this conference. With a vendor-organized conference there&amp;rsquo;s always the risk of it being mostly a thinly-veiled marketing excercise, but this didn&amp;rsquo;t prove to be the case: there was a good community and open-source vibe to it, probably because &lt;a href=&#34;https://coreos.com/&#34;&gt;CoreOS&lt;/a&gt; itself is for the most part an open-source company.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://rocketeer.be/blog/2017/06/fest-view.jpg&#34; alt=&#34;Not bad for a view&#34; /&gt;&lt;/p&gt;

&lt;p&gt;Also fun was encountering a few old-time &lt;a href=&#34;https://www.gnome.org/&#34;&gt;GNOME&lt;/a&gt; developers such as &lt;a href=&#34;https://mjg59.dreamwidth.org/&#34;&gt;Matthew Garrett&lt;/a&gt; (now at Google) and &lt;a href=&#34;https://twitter.com/blixtra&#34;&gt;Chris Kühl&lt;/a&gt; (who now runs &lt;a href=&#34;https://kinvolk.io/&#34;&gt;kinvolk&lt;/a&gt;). It&amp;rsquo;s remarkable how good of a talent incubator the GNOME project is. Look at any reasonably successful project and chances are high you’ll find some (ex-)GNOME people.&lt;/p&gt;

&lt;p&gt;&lt;img src=&#34;https://rocketeer.be/blog/2017/06/fest-hall.jpg&#34; alt=&#34;Main room&#34; /&gt;&lt;/p&gt;

&lt;p&gt;I also had the pleasure of presenting the experiences and lessons learned related to introducing &lt;a href=&#34;https://kubernetes.io/&#34;&gt;Kubernetes&lt;/a&gt; at &lt;a href=&#34;https://www.ticketmatic.com/&#34;&gt;Ticketmatic&lt;/a&gt;. Annotated slides and a video of the talk can be found &lt;a href=&#34;https://rocketeer.be/articles/coreos-fest-2017/&#34;&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&#34;https://rocketeer.be/articles/coreos-fest-2017/&#34;&gt;&lt;img src=&#34;https://rocketeer.be/articles/coreos-fest-2017/01.jpg&#34; alt=&#34;Making your company cloud‑native: the Ticketmatic story&#34; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2017/06/coreos-fest-2017/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
        <item>
            <title>Commercial open-source: Sentry</title>
            <link>https://rocketeer.be/blog/2016/12/sentry/</link>
            <pubDate>Thu, 29 Dec 2016 17:18:32 &#43;0000</pubDate>
            
            <guid>https://rocketeer.be/blog/2016/12/sentry/</guid>
            <description>&lt;p&gt;Commercial open-source software is usually based around some kind of asymmetry: the owner possesses something that you as a user do not, allowing them to make money off of it.&lt;/p&gt;

&lt;p&gt;This asymmetry can take on a number of forms. One popular option is to have dual licensing: the product is open-source (usually GPL), but if you want to deviate from that, there’s the option to buy a commercial license. These projects are recognizable by the fact that they generally require you to sign a Contributor License Agreement (CLA) in which you transfer all your rights to the code over to the project owners. A very bad deal for you as a contributor (you work but get nothing in return) so I recommend against participating in those projects. But that’s a subject for a different day.&lt;/p&gt;

&lt;p&gt;Another option for making asymmetry is open core: make a limited version open-source and sell a full-featured version. Typically named “the enterprise version”. Where you draw the line between both versions determines how useful the project is in its open-source form versus how much potential there is to sell it. Most of the time this tends towards a completely useless open-source version, but there are exceptions (e.g. &lt;a href=&#34;https://about.gitlab.com/&#34;&gt;Gitlab&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;These models are so prevalent that I was pleasantly surprised to see who &lt;a href=&#34;https://sentry.io/&#34;&gt;Sentry&lt;/a&gt; does things: as little asymmetry as possible. The entire product is open-source and under a very liberal license. The hosted version (the SaaS product that they sell) is claimed to be running using exactly the same source code. The created value, for which you’ll want to pay, is in the belief that a) you don’t want to spend time running it yourself and b) they’ll do a better job at it than you do.&lt;/p&gt;

&lt;p&gt;This model certainly won’t work in all contexts and it probably won’t lead to &lt;a href=&#34;https://en.wikipedia.org/wiki/MySQL#Legal_disputes_and_acquisitions&#34;&gt;a billion dollar exit&lt;/a&gt;, but that doesn&amp;rsquo;t always have to be the goal.&lt;/p&gt;

&lt;p&gt;So kudos to &lt;a href=&#34;https://sentry.io/&#34;&gt;Sentry&lt;/a&gt;, they’re certainly trying to make money in the nicest way possible, without giving contributors and hobbyists a bad deal. I hope they do well.&lt;/p&gt;

&lt;p&gt;More info on their open-source model can be read on their blog: &lt;a href=&#34;https://blog.sentry.io/2016/10/24/building-an-open-source-service.html&#34;&gt;Building an Open Source Service&lt;/a&gt;.&lt;/p&gt;
&lt;br /&gt;&lt;p&gt;&lt;a href="https://rocketeer.be/blog/2016/12/sentry/#comments"&gt;Comments&lt;/a&gt; | &lt;a href="https://rocketeer.be/"&gt;More on rocketeer.be&lt;/a&gt; | &lt;a href="https://twitter.com/rubenv"&gt;@rubenv on Twitter&lt;/a&gt;&lt;/p&gt;</description>
        </item>
        
    </channel>
</rss>
