<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Edoardo Tenani</title><link>https://endorama.dev/</link><description>Recent content on Edoardo Tenani</description><generator>Hugo -- gohugo.io</generator><language>en-gb</language><copyright>🄯 2022-{year} Edoardo Tenani - Some Rights Reserved</copyright><lastBuildDate>Wed, 14 Feb 2024 14:52:25 +0200</lastBuildDate><atom:link href="https://endorama.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Improving LUKS performances on SSDs</title><link>https://endorama.dev/2024/improving-luks-performances-on-ssds/</link><pubDate>Wed, 14 Feb 2024 14:52:25 +0200</pubDate><guid>https://endorama.dev/2024/improving-luks-performances-on-ssds/</guid><description>&lt;p>Full disk encryption is a security measure that involves encrypting the entire contents of a computer&amp;rsquo;s hard drive or storage device. This includes the operating system, system files, application data, and user files.&lt;/p>
&lt;p>Especially at work, having such protection in place provides so many benefits it&amp;rsquo;s a no-brainer wanting to have it enabled everywhere! Full disk encryption ensures that even if someone gains unauthorized access to the physical device or its storage media, they won&amp;rsquo;t be able to read the stored data without the correct decryption key.&lt;/p>
&lt;p>Think of GDPR or HIPAA regulations and their strict privacy requirements and think of hybrid working environments (or full remote) where having access to a device is easier that in other settings, Full Disk Encryption make your device as usuful as a brick.&lt;/p>
&lt;p>But is has a major disadvantage: performances.&lt;/p>
&lt;p>In case you are using LUKS on a Solid State Drive, there is a nice trick that could be used to improve performances: adding &lt;code>no-read-workqueue&lt;/code> and/or &lt;code>no-write-workqueue&lt;/code> to &lt;code>/etc/crypttab&lt;/code> flag section.&lt;/p>
&lt;p>These flags disable forcing each read and write operation going through Kernel queues. Originally on spinning hard drives having ordered reads or writes was a performance optimization but nowdays SSDs are snappier when doing random accesses to the disk, making these optimization actually hurt performances. If you have I/O intensive workloads you can benefit by disabling the queues.&lt;/p>
&lt;p>Source for this is the ever awesome &lt;a href="https://wiki.archlinux.org/title/dm-crypt/Specialties#Disable_workqueue_for_increased_solid_state_drive_(SSD)_performance">Arch Linux Wiki&lt;/a>, which provides further links to learn more about where those flags come from.&lt;/p></description></item><item><title>To make or not to make</title><link>https://endorama.dev/2023/to-make-or-not-to-make/</link><pubDate>Mon, 18 Sep 2023 23:11:00 +0200</pubDate><guid>https://endorama.dev/2023/to-make-or-not-to-make/</guid><description>&lt;p>I had this post in the making for more than &lt;strong>one year&lt;/strong> 😰 but I got re-inspired by &lt;a href="https://cloudnativeengineer.substack.com/p/ep-5-taskfile-a-modern-alternative">an article&lt;/a> from my colleague Giuseppe Santoro about it.&lt;/p>
&lt;p>Why writing about &lt;code>make&lt;/code>? &lt;code>make&lt;/code> is a renowned tool and you can find it in a lot of projects. You can see it used for a wide range of tasks, involved in
all kinds of developer tooling gimmicks. One of them is using it as a &lt;strong>task runner&lt;/strong>, something that it is not, nor is ergonomic for.&lt;/p>
&lt;p>It&amp;rsquo;s purpose is to &lt;strong>build&lt;/strong> your program from its source files.&lt;/p>
&lt;p>In its &lt;a href="https://cloudnativeengineer.substack.com/p/ep-5-taskfile-a-modern-alternative">post&lt;/a> Giuseppe claims that Make is too old for the present day and that we could do better:&lt;/p>
&lt;blockquote>
&lt;p>As anyone that writes or interacts with software these days, I need to run automation scripts every day but I am still stuck with a tool that (according to Wikipedia) has been written 47 years ago. Don&amp;rsquo;t get me wrong, Makefile was really useful 20 years ago but I think we can do better than that in 2023.&lt;/p>
&lt;/blockquote>
&lt;p>I agree we could do better, but for slightly different reasons:&lt;/p>
&lt;ul>
&lt;li>software that ages that much exhibit some of these proprieties: it&amp;rsquo;s useful, it&amp;rsquo;s reliable, has a community, is actively developed, has no replacement. Those qualities are good qualities and we should aim at more software like that!&lt;/li>
&lt;li>it may also be that is affected by &amp;ldquo;we always do it like that&amp;rdquo; or &amp;ldquo;no one has ever been fired for buying IBM&amp;rdquo; and this is a culture I&amp;rsquo;m more than happy to abandon.&lt;/li>
&lt;/ul>
&lt;p>I have full respect for a software that has been around 4.7x the time I&amp;rsquo;ve been in the trade 😁 But Make is far from perfect &lt;strong>when used as a task runner&lt;/strong>, something it has not been designed for.&lt;/p>
&lt;h2 id="pitfalls">Pitfalls&lt;/h2>
&lt;p>&lt;code>make&lt;/code> is defined in the &lt;a href="https://www.gnu.org/software/make/manual/html_node/index.html">first page of it&amp;rsquo;s manual&lt;/a> as &amp;ldquo;[…] the GNU &lt;code>make&lt;/code> utility, which determines automatically which pieces of a large program need to be recompiled, and issues the commands to recompile them.&amp;rdquo;&lt;/p>
&lt;p>Make documentation is for sure extensive, but &lt;strong>searching it is complex&lt;/strong> as is referencing to it. This is a huge pain for &lt;em>learning&lt;/em> how to properly use it or &lt;em>referencing&lt;/em> documentation for someone else. Some documentation formats allow for text search but, personal opinion, that is not enough in 2023. It feels like a lot is given for granted, like if you (the dev) where already using it and the doc is just your reference. You know what you&amp;rsquo;re looking for, just need to refresh it.&lt;/p>
&lt;p>(as a partial solution, Make documentation is searchable through &lt;a href="https://devdocs.io">https://devdocs.io&lt;/a> 🥳).&lt;/p>
&lt;p>Now that we can search the documentation, let&amp;rsquo;s start learning as I want to change the automation in my repo! Looking at the first &amp;ldquo;straightforward&amp;rdquo; (the doc says it!) &lt;a href="https://www.gnu.org/software/make/manual/html_node/Simple-Makefile.html">Makefile&lt;/a> I admit I&amp;rsquo;m confused:&lt;/p>
&lt;pre tabindex="0">&lt;code>edit : main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o : command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
insert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o : files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean :
rm edit main.o kbd.o command.o display.o \
insert.o search.o files.o utils.o
&lt;/code>&lt;/pre>&lt;p>I have little knowledge around building C code, but what about automation? I would not define this example as straightforward in the context of a task runner, is &lt;em>not even relevant&lt;/em>.&lt;/p>
&lt;p>Well let&amp;rsquo;s move on, I just needed to add a new variable to gather the git SHA commit of my build, so let&amp;rsquo;s search what the manual says about variables. As the docs suggests:&lt;/p>
&lt;blockquote>
&lt;p>[…] duplication is error-prone […]. We can eliminate the risk and simplify the makefile by using a variable. Variables allow a text string to be defined once and substituted in multiple places later.&lt;/p>
&lt;/blockquote>
&lt;p>That&amp;rsquo;s great! It links to &lt;a href="https://www.gnu.org/software/make/manual/html_node/Using-Variables.html">How to Use Variables&lt;/a> which does not explain how, so we proceed. The first link goes to &lt;a href="https://www.gnu.org/software/make/manual/html_node/Reference.html">Basics of Variable References&lt;/a> which states:&lt;/p>
&lt;blockquote>
&lt;p>To substitute a variable’s value, write a dollar sign followed by the name of the variable in parentheses or braces: either ‘$(foo)’ or ‘${foo}’ is a valid reference to the variable foo. This special significance of ‘$’ is why you must write ‘$$’ to have the effect of a single dollar sign in a file name or recipe.&lt;/p>
&lt;/blockquote>
&lt;p>I got it, now I know how to reference variables!&lt;/p>
&lt;blockquote>
&lt;p>Variable references can be used in any context: targets, prerequisites, recipes, most directives, and new variable values.&lt;/p>
&lt;/blockquote>
&lt;p>Great, flexibility!&lt;/p>
&lt;blockquote>
&lt;p>A dollar sign followed by a character other than a dollar sign, open-parenthesis or open-brace &lt;strong>treats that single character as the variable name. Thus, you could reference the variable x with ‘$x’. However, this practice can lead to confusion (e.g., ‘$foo’ refers to the variable f followed by the string oo) so we recommend using parentheses or braces around all variables&lt;/strong>, even single-letter variables, unless omitting them gives significant readability improvements.&lt;br>
&lt;em>Emphasis mine&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;p>&lt;img src="https://endorama.dev/gifs/blinking-eyes-white-guy.gif" alt="A person blinking eyes baffled">&lt;/p>
&lt;p>That&amp;rsquo;s fine, once you know about it? Wrong, this is bad developer experience, we &lt;strong>should&lt;/strong> do better.&lt;/p>
&lt;p>Custom variables are important but maybe there are built-in variables that you can use? Sure, &lt;a href="https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html">Automatic Variables&lt;/a>! This is useful and variants are useful too, until they aren&amp;rsquo;t.&lt;br>
Without looking, what is the difference between &lt;code>$(@F)&lt;/code> and &lt;code>$(*F)&lt;/code>? Again, this is bad developer experience, we &lt;strong>should&lt;/strong> do better!&lt;/p>
&lt;p>Make also supports some other &lt;a href="https://www.gnu.org/software/make/manual/html_node/Special-Variables.html">Special Variables&lt;/a> but which again are not useful in the context of a task runner.&lt;/p>
&lt;p>The last bits that I struggle with Make are:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html">&lt;code>.PHONY&lt;/code>&lt;/a>. Ever heard of it? From the docs: &amp;ldquo;A phony target is one that is not really the name of a file&amp;rdquo; (which means all other targets &lt;em>are&lt;/em> file names). If you&amp;rsquo;re not using it for all targets that do not create a file you may end up introducing file caching in your automation. Good luck searching for it the first time you encounter it though, as the docs don&amp;rsquo;t have search built-in.&lt;/li>
&lt;li>there is no &amp;ldquo;help&amp;rdquo; or target description in make (yes, some &lt;em>workaround&lt;/em> exists, but still are workarounds, is not built-in nor documented).&lt;/li>
&lt;li>errors can be obscure and searching the web often don&amp;rsquo;t solves your problem.&lt;/li>
&lt;li>breaking a single &lt;code>Makefile&lt;/code> into different ones is a pain;&lt;/li>
&lt;li>ironing out cross platform difference makes things worse.&lt;/li>
&lt;/ul>
&lt;p>I know that you can use other search engines or there are other ways to get these information, but I think a good developer experience requires great documentation.&lt;/p>
&lt;p>Note that these are not all Make&amp;rsquo;s faults, as the issue is in using it way beyond the original scope. Writing great &lt;code>Makefile&lt;/code>s is possible, this &lt;a href="https://www.thapaliya.com/en/writings/well-documented-makefiles/">blog post&lt;/a> is an awesome example of it, but that requires high discipline, deep knowledge and (having tried to modify it) trial and errors. It also not friendly for newcomers. &lt;code>make&lt;/code>is an &lt;strong>awesome&lt;/strong> &lt;em>&lt;strong>build&lt;/strong>&lt;/em> tool, but when used as a task runner you lose a lot of features and get back a lot of unnecessary complexity.&lt;/p>
&lt;p>Funny note while writing this post I discovered that someone implemented &lt;a href="https://www.reddit.com/r/programming/comments/412kqz/comment/cyzxqlx/">FizzBuzz in GNU Make&lt;/a> 🤯. Mind-boggling 🤣&lt;/p>
&lt;h2 id="can-we-do-better">Can we do better?&lt;/h2>
&lt;p>Alternatives are available that &lt;strong>do better&lt;/strong> on the developer experience and are:&lt;/p>
&lt;ul>
&lt;li>easier to use;&lt;/li>
&lt;li>kinder to your contributors;&lt;/li>
&lt;li>have a smaller learning curve;&lt;/li>
&lt;li>simpler internals, in case you need to troubleshoot them;&lt;/li>
&lt;li>support advanced use cases (cross-platform specificity, import, overrides, help text).&lt;/li>
&lt;/ul>
&lt;p>Of all the available alternatives there is a set that stands out to me: &lt;code>rake&lt;/code>, &lt;code>task&lt;/code> and &lt;code>just&lt;/code>.&lt;/p>
&lt;p>Why?&lt;/p>
&lt;p>I want this tool to be &lt;strong>ergonomic&lt;/strong>: it should be intuitive (or at worst easy) to write task definition, dependencies and leverage external tools.&lt;br>
I want this tool to be &lt;strong>powerful&lt;/strong>: I need advanced features, like cross-platform overrides or imports.
I want this tool to be &lt;strong>welcoming&lt;/strong>: documentation should be good and details how to do things, allowing to iterate fast without deep knowledge of the internals.&lt;/p>
&lt;h2 id="rake">Rake&lt;/h2>
&lt;p>&lt;a href="https://ruby.github.io/rake">Rake&lt;/a> is a make-like build utility for Ruby.
It uses a DSL to write terse and concise tasks in Ruby code, automating the toil.&lt;/p>
&lt;p>To me this is the best tool out there, its major drawback is relying on Ruby. This makes adding it to any pipeline for other languages a chore and require contributors to know Ruby. Not to mention having to install Ruby in your CI just for this. I would not recommend to use this unless you&amp;rsquo;re in a Ruby project.&lt;/p>
&lt;h2 id="task">Task&lt;/h2>
&lt;p>&lt;a href="https://taskfile.dev">Task&lt;/a> is a task runner / simpler Make alternative written in Go.
It&amp;rsquo;s a single binary, which makes it incredibly convenient to use and install and works (in most cases) as you expect.&lt;/p>
&lt;p>This is my preferred choice as of today, it&amp;rsquo;s packed with a lot of features, easy to start with and iterate. Personally I prefer to keep things simple and move logic to external scripts when needed than making it a spaghetti-YAML mess.
This is a list from it&amp;rsquo;s great feature set: allows to use Go template engine, has a cross-platform shell interpreter (works &lt;strong>on Windows&lt;/strong>, for real!), Taskfile import, &lt;code>.env&lt;/code> support, variables, dry run mode and a file watcher (and there are more!).&lt;/p>
&lt;p>And yes, it uses YAML. I&amp;rsquo;m not happy about it but given how pervasive it has become I consider it a decent compromise (but I suggest to keep things simple).&lt;/p>
&lt;h2 id="just">Just&lt;/h2>
&lt;p>&lt;a href="https://just.systems/">Just&lt;/a> is a handy way to save and run project-specific commands.
It&amp;rsquo;s a single binary like Task. It&amp;rsquo;s DSL is as good as Rake, feels like a Makefile with a feature set appropriate for a task runner.&lt;/p>
&lt;p>Features include: supports different languages, task parameters, &lt;code>.env&lt;/code> support, running recipes in different folders.&lt;/p>
&lt;p>I don&amp;rsquo;t like the &lt;a href="https://just.systems/man/en/chapter_54.html">fallback to parent feature&lt;/a> that feels a bit unpredictable or the need to write recipes in different languages within the same file (I generally put them in separate files and run them) thus is the one that gets my least preferred, but is impressive and has some interesting patterns.&lt;/p>
&lt;h2 id="conclusions">Conclusions&lt;/h2>
&lt;p>Since some years ago I found my task runner in &lt;code>task&lt;/code>, it evolved since I started using it (current &lt;code>Taskfile&lt;/code> version is &lt;code>3&lt;/code>) and packs all the feature I need (plus some more). But most of all, I can use it without even looking at the documentation for basic use cases and I can jump to the docs when I want to make things more complex.&lt;/p>
&lt;p>In the end no DSL will ever be perfect. I think Make DSL is great for it&amp;rsquo;s intended purpose (building C code) as much as it&amp;rsquo;s painful for the task runner use case.&lt;br>
A task runner goal is to run a &amp;ldquo;task&amp;rdquo;, a command or a set of commands to achieve something. That&amp;rsquo;s why alternative &lt;strong>for this specific use case&lt;/strong> exists.&lt;/p>
&lt;p>An example is &lt;a href="https://github.com/endorama/2ami/blob/master/Taskfile.yml">&lt;code>2ami&lt;/code> &lt;code>Taskfile&lt;/code>&lt;/a> that helped me streamline the tool &lt;a href="https://github.com/endorama/2ami/blob/master/.github/workflows/release.yml">release pipeline&lt;/a> where I build it, compute checksum and create a GitHub release for 3 different Operating Systems.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter which tool you end up using, but we &lt;strong>must&lt;/strong> ask and use better tooling, not because the one we have are old but because we need tooling to be ergonomic and fit-for-purpose.&lt;/p>
&lt;p>&lt;strong>We have the right to more ergonomic tooling, like you have the right to an ergonomic chair to prevent long term damage of repeated actions.&lt;/strong>&lt;/p></description></item><item><title>Smartphone Ecodesign labelling comes to EU</title><link>https://endorama.dev/2023/smartphone-ecodesign-labelling-comes-to-eu/</link><pubDate>Sun, 17 Sep 2023 19:18:25 +0200</pubDate><guid>https://endorama.dev/2023/smartphone-ecodesign-labelling-comes-to-eu/</guid><description>&lt;p>Ecodesign comes to smartphones!&lt;/p>
&lt;p>I suggest to read the excellet, as usual, &lt;a href="https://repair.eu/news/new-eu-rules-smartphones-and-tablets-will-follow-new-ecodesign-requirements-by-june-2025/">Right To Repair blog post for further details&lt;/a>.&lt;/p>
&lt;p>The TL;DR is that from June 2025 those devices will have to factor in repairability, forcing them to provide hardware spare parts and information for &lt;em>8at least 7 year&lt;/em>* after stopping distributing the product (and 5 years for software updates).&lt;/p>
&lt;p>This is &lt;strong>huge&lt;/strong> really! I&amp;rsquo;ve been fixing smartphones for &amp;ldquo;passion&amp;rdquo; since at least 10 years ago. I fondly remember the first time I pried open by Samsung Galaxy SII 😱&lt;/p>
&lt;p>During the last years most smartphones have come so much repair unfriendly (both on costs, spare parts &amp;amp; manufacturing &amp;ldquo;complexity&amp;rdquo; - like using a lot of plastic connections or glue) that repairing them is a real challenge, and is quite difficult to make them last more than 5 years. To me, 5 years is the minimum target life for a smartphone given the abundance of scarce metals and resources needed in its manufacturing process.&lt;/p>
&lt;p>So I&amp;rsquo;m more than happy!&lt;/p>
&lt;p>And in case you want the future now, there are two active manufacturing companies that got 10/10 score on iFixit for all their devices:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://www.fairphone.com/en/">Fairphone&lt;/a> have been doing this for some years alreay for smartphones (and just announced their last flagship device) - &lt;a href="https://www.ifixit.com/Device/Fairphone">iFixit&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://frame.work/">framework&lt;/a> laptops, their started a couple of years ago and it looks awesome (plus they got excellent reviews) - &lt;a href="https://www.ifixit.com/Device/Framework">iFixit&lt;/a>&lt;/li>
&lt;/ul></description></item><item><title>Per project LSP settings in LunarVim</title><link>https://endorama.dev/2023/per-project-lsp-settings-in-lunarvim/</link><pubDate>Thu, 07 Sep 2023 12:32:15 +0200</pubDate><guid>https://endorama.dev/2023/per-project-lsp-settings-in-lunarvim/</guid><description>&lt;p>I&amp;rsquo;ve been using LunarVim successfully for years now and I just discover you can set specific LSP settings per project!&lt;/p>
&lt;p>I&amp;rsquo;ve been developing with go and one thing that happens frequently is using build tags. Once a build tag is added,
&lt;code>gopls&lt;/code> and other tooling will ignore the file where is specified, unless told not to do so.&lt;/p>
&lt;p>To edit local configuration you have to run &lt;code>:LspSettings local gopls&lt;/code>&lt;/p>
&lt;p>This will open a file in a local &lt;code>.nlsp-settings/&lt;/code> folder where you can add you configuration. In my case, adding the &lt;code>integration&lt;/code> build tag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;gopls&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;build.buildFlags&amp;#34;&lt;/span>: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#e6db74">&amp;#34;-tags=integration&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="bonus-trick">Bonus trick&lt;/h2>
&lt;p>If you add this snippet&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-json" data-lang="json">&lt;span style="display:flex;">&lt;span>{
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;gopls&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;ui.diagnostic.analyses&amp;#34;&lt;/span>: {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;fieldalignment&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;nilness&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;unusedparams&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f92672">&amp;#34;unusedwrite&amp;#34;&lt;/span>: &lt;span style="color:#66d9ef">true&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>to your global &lt;code>gopls&lt;/code> LSP settings (open it with &lt;code>:LspSettings gopls&lt;/code>) you get autocompletion for settings like in other editors. 🌟&lt;/p></description></item><item><title>Old blog, new blog</title><link>https://endorama.dev/2023/old-blog-new-blog/</link><pubDate>Wed, 06 Sep 2023 01:04:20 +0000</pubDate><guid>https://endorama.dev/2023/old-blog-new-blog/</guid><description>&lt;p>Small changes happened!&lt;/p>
&lt;p>Recently Google Domains announced is going to be acquired by Squarespace. So much for a good Google product :/
Squarespace has some really dauting prices, so I started evaluating a strategy.&lt;/p>
&lt;p>So far nothing major happened, but I decided meanwhile to move away from GitHub pages for hosting the website, and trying out Netlify.&lt;br>
The main reason are avoid the DNS coupling GitHub pages require and being able to preview the content online and not having to rely on a local editor.&lt;br>
Hopefully I can setup a nice blogging environment with a devcontainer and have PRs acting as a draft system!&lt;/p>
&lt;p>Another change is the blog repo no longer being public on GitHub. This repo didn&amp;rsquo;t have a license, so wasn&amp;rsquo;t really intended to be public; it was due to a GitHub limit, which allows using Pages on a private repo only on the paid plan.&lt;/p>
&lt;p>So long and thanks for all the fish GitHub! The transition have been quite smooth, if not for the fact that changing DNS brought the site down for some days :( Opsfail, sorry.&lt;/p>
&lt;p>I also decided to remove any links point to &amp;ldquo;the site formerly known as Twitter&amp;rdquo;, as I&amp;rsquo;m really not comfortable linking to that platform anymore. I&amp;rsquo;ll keep an account just to prevent squatting the username but I will not advertise it anymore.&lt;/p></description></item><item><title>Inspecting TLS traffic golang</title><link>https://endorama.dev/2023/inspecting-tls-traffic-golang/</link><pubDate>Tue, 09 May 2023 18:55:24 +0200</pubDate><guid>https://endorama.dev/2023/inspecting-tls-traffic-golang/</guid><description>&lt;p>While debugging a Go application, I needed to troubleshoot its connections to a remote HTTPS endpoint.&lt;/p>
&lt;p>Without fear, I started the mighty Wireshark and prepared to inspect some TCP packets!&lt;/p>
&lt;p>The application I needed to debug is a small sample application I wrote, so I was in complete control of the source code. Quite handy, as we will demonstrate. The application is part of my study on the Elastic APM Agents communication protocol. Is a sample Go HTTP server with a couple of endpoints that emit all possible Elastic APM data, using simple instrumentation of HTTP function handlers.&lt;/p>
&lt;p>The first step were to determine the IP address and port/s the application was connecting to. After starting the application, in this case named &lt;code>eapm&lt;/code>, I used &lt;code>nestat&lt;/code> and &lt;code>grep&lt;/code> to check which connection were opened.&lt;/p>
&lt;pre tabindex="0">&lt;code>$ netstat -alpn | grep
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 10.10.10.10:55932 1.2.3.4:443 ESTABLISHED 658594/./eapm
tcp 0 0 10.10.10.10:35122 1.2.3.4:443 ESTABLISHED 658594/./eapm
tcp6 0 0 :::8080 :::* LISTEN 658594/./eapm
&lt;/code>&lt;/pre>&lt;p>IPs have been redacted, but so far so good, we have 2 outgoing connection to the same IP address and a listening server by our small service.&lt;/p>
&lt;p>With this information, we can start searching around in Wireshark. As connections go to port 443 I was expecting encrypted traffic, which meant looking at the content would be impossible without a decryption key.&lt;/p>
&lt;p>As this is a known problem, there are workarounds! Being in control of the client or the server of a TLS connection, is possible to get the decryption keys needed for inspecting the traffic. In this case we are in control of the client, as is a library in a program we control the source of.&lt;/p>
&lt;p>Wireshark allow decrypting traffic, as long as the &lt;a href="https://wiki.wireshark.org/TLS#using-the-pre-master-secret">Pre-Master-Secret&lt;/a> is known.&lt;/p>
&lt;p>&lt;code>SSLKEYLOGFILE&lt;/code> is an environment variable that can be set tell browsers to save SSL decryption keys to a specific path on the file system. With can then pass that file to Wireshark and decrypt any TLS traffic.&lt;/p>
&lt;p>So far so good, but Golang does not support it out of the box. But it supports (at least as for Golang 1.20.4) the &lt;code>KeyLogWriter&lt;/code> option in the &lt;code>tls.Config&lt;/code> struct (&lt;a href="https://pkg.go.dev/crypto/tls@go1.20.4#Config.KeyLogWriter">docs&lt;/a>). It accepts a &lt;code>io.Writer&lt;/code>, which is handily returned by &lt;code>os.Create&lt;/code>.&lt;/p>
&lt;p>To print the keys the only thing left was to find out where in the code to add this code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fi&lt;/span>, &lt;span style="color:#a6e22e">_&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Create&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;/tmp/foo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">cfg&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">tls&lt;/span>.&lt;span style="color:#a6e22e">Config&lt;/span>{&lt;span style="color:#a6e22e">KeyLogWriter&lt;/span>: &lt;span style="color:#a6e22e">fi&lt;/span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In Elastic APM Go Agent, the HTTP client is configured in &lt;a href="https://github.com/elastic/apm-agent-go/blob/4c885061af28b5978fd287cefdbe5fc57c56bbf1/transport/http.go#L257">transport/http.go&lt;/a>. The function creates a &lt;code>tls.Config&lt;/code> and returns it after some initialization.&lt;/p>
&lt;p>The complete code for this little mischief was:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-golang" data-lang="golang">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> &lt;span style="color:#a6e22e">newEnvTLSClientConfig&lt;/span>() (&lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">tls&lt;/span>.&lt;span style="color:#a6e22e">Config&lt;/span>, &lt;span style="color:#66d9ef">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">verifyServerCert&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">configutil&lt;/span>.&lt;span style="color:#a6e22e">ParseBoolEnv&lt;/span>(&lt;span style="color:#a6e22e">envVerifyServerCert&lt;/span>, &lt;span style="color:#66d9ef">true&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">!=&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#66d9ef">nil&lt;/span>, &lt;span style="color:#a6e22e">err&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">tlsClientConfig&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#f92672">&amp;amp;&lt;/span>&lt;span style="color:#a6e22e">tls&lt;/span>.&lt;span style="color:#a6e22e">Config&lt;/span>{&lt;span style="color:#a6e22e">InsecureSkipVerify&lt;/span>: !&lt;span style="color:#a6e22e">verifyServerCert&lt;/span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e">// [...] no changes here
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span> &lt;span style="color:#a6e22e">fi&lt;/span>, &lt;span style="color:#a6e22e">_&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">os&lt;/span>.&lt;span style="color:#a6e22e">Create&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;/tmp/foo&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">tlsClientConfig&lt;/span>.&lt;span style="color:#a6e22e">KeyLogWriter&lt;/span> = &lt;span style="color:#a6e22e">fi&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">tlsClientConfig&lt;/span>, &lt;span style="color:#66d9ef">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>With this done, the only thing left was to add the &lt;code>/tmp/foo&lt;/code> file in Wireshark, under &lt;code>Edit &amp;gt; Preferences &amp;gt; Protocols &amp;gt; TLS &amp;gt; (Pre)-Master-Secret log filename&lt;/code> and I were done.&lt;/p>
&lt;p>Filtering for destination IP allowed to view all interesting traffic, which was now fully decrypted. Horray 🎉&lt;/p></description></item><item><title>Better detection of Golang wrapped multiple errors</title><link>https://endorama.dev/2023/better-detection-of-golang-wrapped-multiple-errors/</link><pubDate>Mon, 27 Mar 2023 19:32:22 +0200</pubDate><guid>https://endorama.dev/2023/better-detection-of-golang-wrapped-multiple-errors/</guid><description>&lt;p>Golang 1.20 is out and one the most interesting features for me has been multiple errors. I wrote about them in &lt;a href="https://endorama.dev/2023/golang-1.20-multiple-errors/">Golang 1.20 multiple errors&lt;/a>.&lt;/p>
&lt;p>I was recently researching some Go idioms and patterns for a lightning talk and stumbled upon what I would defined as &amp;ldquo;anonymous interface&amp;rdquo; (the correct term would be &amp;ldquo;anonymous type assertion&amp;rdquo; probably).&lt;/p>
&lt;p>I would defined it as a one-off interface declaration used to answer the question: &amp;ldquo;does this struct implement this method?&amp;rdquo;&lt;/p>
&lt;p>This helps solving one of the open questions of my previous blog post on errors: how to assess if an error is a &amp;ldquo;joined error&amp;rdquo;? We know that for an error to wrap multiple errors it has to implement the &lt;code>Unwrap&lt;/code> method with this signature:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">Unwrap&lt;/span>() []&lt;span style="color:#66d9ef">error&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Go does not provide an out of the box interface for this case, but using an anonymous interface allow us to overcome this limitation easily:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">Join&lt;/span>(&lt;span style="color:#a6e22e">err1&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>, &lt;span style="color:#a6e22e">err3&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">e&lt;/span>, &lt;span style="color:#a6e22e">ok&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">err&lt;/span>.(&lt;span style="color:#66d9ef">interface&lt;/span>{ &lt;span style="color:#a6e22e">Unwrap&lt;/span>() []&lt;span style="color:#66d9ef">error&lt;/span> }), &lt;span style="color:#a6e22e">ok&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">_&lt;/span>, &lt;span style="color:#a6e22e">ue&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#66d9ef">range&lt;/span> &lt;span style="color:#a6e22e">e&lt;/span>.&lt;span style="color:#a6e22e">Unwrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;Unwrapped: &amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">ue&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is not as neat as a i.e. &lt;code>errors.Multiple&lt;/code> interface out of the box (but implementing it is straightforward and also helps with inversion of dependency principle) but I still find it pretty readable.&lt;/p>
&lt;p>It also allowed me to discover a powerful Golang pattern, as the same pattern can be used to asses behaviour of a struct without knowing anything of the interfaces the same struct implements.&lt;/p>
&lt;p>I also found a thread on in on &lt;a href="https://www.reddit.com/r/golang/comments/v7o7yq/casting_to_an_anonymous_interface_to_answer_can_i/">Reddit&lt;/a>, with this and other use cases! Common complain, to which I agree is: why don&amp;rsquo;t make it an exported interface? (The answer is probably to keep the public API small and help with forward compatibility)&lt;/p></description></item><item><title>A philosophy of CI design</title><link>https://endorama.dev/2023/a-philosophy-of-ci-design/</link><pubDate>Wed, 01 Mar 2023 11:51:53 +0100</pubDate><guid>https://endorama.dev/2023/a-philosophy-of-ci-design/</guid><description>&lt;p>When designing a Continuous Integration system, there are a lot of possible challenges and choices. Like the Blank page syndrome, we are faced with (almost) infinite possibilities. What is the right choice? Which system should we choose? How much should we invest? How to train developers/users?&lt;/p>
&lt;p>To navigate complex choices I tend to resort to ignore all the practical details and use principles and values as North Star to guide me through the mess. The risk here is to end up with a flame war kind of debate where the discussion goes around &amp;ldquo;what is the best tool™&amp;rdquo;. Such a discussion &lt;strong>is wrong&lt;/strong>. Is pointless, time consuming and will nowhere bring you near the solution you need.&lt;/p>
&lt;p>The discussion to have is which trade offs you are willing to make &lt;strong>today&lt;/strong>. Not tomorrow, when you don&amp;rsquo;t know what will be the context around you. Today.&lt;br>
Choosing a CI system is a revocable decision, but the costs associated with changing it will make it happen only when it had become really detrimental (precondition: the willness to change). Due to this factor, I consider this choice as based on a simple and long lasting trade off: the more aligned to your current needs, the less complex, the less expensive (money, time, people) in the long run. There is no &amp;ldquo;best&amp;rdquo; here, but &amp;ldquo;better in the long run for us&amp;rdquo;.&lt;/p>
&lt;h1 id="the-ci-problem">The CI problem&lt;/h1>
&lt;p>Before stating the principles, let me take a detour on my analysis of the &amp;ldquo;CI problem&amp;rdquo;.&lt;/p>
&lt;p>I consider this discussion as composed by these separate problems:&lt;/p>
&lt;ul>
&lt;li>developer &lt;strong>toolchain&lt;/strong>; answers to &amp;ldquo;how do I install all the needed tools &amp;amp; requirements?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>reproducibility&lt;/strong> across environments; answers to &amp;ldquo;how to I run the same tools that others are running?&amp;rdquo;&lt;/li>
&lt;li>command &lt;strong>discovery&lt;/strong>; answers to &amp;ldquo;which is the command to perform this task?&amp;rdquo; and &amp;ldquo;how do I make sure I&amp;rsquo;m using the same command everyone is using?&amp;rdquo;&lt;/li>
&lt;li>&lt;strong>troubleshooting&lt;/strong>; answers to &amp;ldquo;this is not working for me, who may I get help from?&amp;rdquo;&lt;/li>
&lt;/ul>
&lt;p>All of them are intertwined, but not so much that can&amp;rsquo;t be considered each one by itself.&lt;/p>
&lt;h2 id="developer-toolchain">Developer toolchain&lt;/h2>
&lt;p>This issue complexity grows with the number of OSs and system architectures to support. This is a hard problem to solve. Being able to install tools reliably across all 3 of them is hard, evaluating cost/benefit is difficult and setting up your working environment is not something that happens frequently.&lt;/p>
&lt;p>There are promising tools that may help here and there (looking at you &lt;a href="https://nixos.org/">Nix OS&lt;/a>!) but then you have opinions. Vim or Emacs? Maybe VSCode or IntelliJ? What about Docker or Podman? &lt;code>goenv&lt;/code> or &lt;code>asdf&lt;/code>? &lt;code>make&lt;/code> or &lt;code>mage&lt;/code>? What about &lt;code>task&lt;/code>? This list can go on forever.&lt;/p>
&lt;p>We don&amp;rsquo;t care how tools are installed, we care those tools are &lt;strong>available&lt;/strong> and at the version we expect. So let&amp;rsquo;s strat from here: provide guidance to check for tool availability and check the version you expect is installed. Installation is left to the reader.&lt;/p>
&lt;p>Doing this does not mean having to write documentation that no one maintains. Write a small scripts that is capable of checking tools &amp;amp; versions, have your devs run it regularly. &lt;a href="https://github.com/github/scripts-to-rule-them-all">Scripts to run them all&lt;/a> is the perfect example.&lt;/p>
&lt;h2 id="reproducibility-across-environments">Reproducibility across environments&lt;/h2>
&lt;p>Keep in mind how deep the rabbit hole is. Depending on the depth you want to achieve there are various tools.&lt;/p>
&lt;p>Why not start from keeping things simple? Just start by providing tools and versions informations. Are we running the &lt;code>linter@v1.2.3&lt;/code>? This information should be added to your (Git?) repo. Make it part of your codebase. Write it in a file. Write small tooling to double check this. Aliging your local environment and the CI is just a matter of keeping this in sync, not the easiest task but the involved complexity is low. Being it a file is possible to &lt;code>cat &amp;lt;file&amp;gt; | grep &amp;lt;tool&amp;gt;&lt;/code> for comparing versions. Tools like &lt;a href="https://asdf-vm.com/">asdf&lt;/a> may prove very helpful here.&lt;/p>
&lt;p>Growing on the complexity scale, Docker images are the next step, but this heavily depends on what does your CI system of choice integrates (mostly will support Docker). Tools like &lt;a href="https://localstack.cloud/">localstack&lt;/a> or &lt;a href="https://github.com/testcontainers">testcontainers&lt;/a> may be handy here. Be mindful of disk space thouh, as Docker images may ehaust disk space quickly.&lt;/p>
&lt;h2 id="command-discovery">Command discovery&lt;/h2>
&lt;p>Command discovery is the ability to know which command everybody else (and the CI runners) run to perform a task. How do you build the binary? The dev one? The relase one? How do you lint the code?&lt;/p>
&lt;p>This is where I think tools like &lt;code>make&lt;/code> do not really shine. They bundle too much complexity already, but their primary goal is not being simple, is being flexible. Not that simplicity and flexibility must be antagonists, but they usually are. Allowing for creating complex things, they bring in more cognitive load. They also tend to fail around debugging and being easy for newcomers (i.e. support for &amp;ldquo;dry-run modes&amp;rdquo;).&lt;/p>
&lt;p>This is also where documentation tends to fail in the long term. It becomes stale and out of date.&lt;/p>
&lt;p>To solve this, is great to write down commands in a task runner (a software whose goal is to run tasks, more or less integrated with your development environment) that developers use regularly.&lt;/p>
&lt;h2 id="troubleshooting">Troubleshooting&lt;/h2>
&lt;p>Linter failed on CI for someone else, but you can&amp;rsquo;t reproduce. These issues can take an incredible amount of time to be solved, can be flaky, can happen for unrelated reasons and terribly frustrate developers and maintainers.&lt;/p>
&lt;p>In my experience the only way to address these is to make all steps explicit enough that providing help does not require prior or deep knowledge of the toolchain. If you resort to always asking to the same person or team for &amp;ldquo;CI problems&amp;rdquo;, if you need guides or documentation, the system may be too complex and frustrating.&lt;/p>
&lt;p>This is the area where I think the Unix philosophy of small single purpose tools shines and make our life easier. Instead of trying to have a single tool to do everything, make it small and precise, but with a high degree of reliability.&lt;/p>
&lt;p>For any setup you choose there will always be something that do not work, but are devs able to unblock themselves or will they get stuck and need help from someone else? Will they be able to get help by anyone or just very specific (busy) people?&lt;/p>
&lt;h1 id="principles">Principles&lt;/h1>
&lt;p>Detour done, here are the principles:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Ease of use&lt;/strong>&lt;br>
The choosen system should be the easiest fulfilling the requirements. Additional complexity &amp;ldquo;for future use&amp;rdquo; should be avoided at all costs.
The more complex the tool, the more cognitive demanding its maintenance, and as our focus is on the product having heavy maintenance toolchain is a big risk in the long run in my opinion.&lt;/li>
&lt;li>&lt;strong>Needs to be compatible with the build tool of choice&lt;/strong>&lt;br>
There is no point in having tools that do not integrate well together. Look for constraints in your setup and build with them in mind.&lt;/li>
&lt;li>&lt;strong>CI is just another dev computer&lt;/strong>&lt;br>
Whatever runs on the CI needs to be reproducible locally or in a clean env. There must be no need to run things in CI to ensure the CI will pass. If you consider the CI just another development computer (with added security requirements), your setup will be reproducible in another CI system or on the new dev laptop for example.&lt;/li>
&lt;li>&lt;strong>Manage complex dependencies (or don&amp;rsquo;t)&lt;/strong>&lt;br>
Are you a multinational company with millions of lines of code, thousands of developers working on it and complex dependencies across project? If the answer is no, don&amp;rsquo;t think about managing complex dependencies (or tools to do that) and take the opposite route: aim for 0 dependencies. Don&amp;rsquo;t try to handled them, remove them.&lt;/li>
&lt;li>&lt;strong>Delegation over complication&lt;/strong>&lt;br>
Instead of trying to cram everything into a single tool, delegate to specialised tools where needed, and keep the integration point simple.&lt;/li>
&lt;/ul>
&lt;p>All these principles together aims at a single goal: in 6 months when you will be looking at your local dev environment and CI integration you will be able &lt;strong>to undertand it&lt;/strong> and &lt;strong>modify it&lt;/strong>.&lt;/p></description></item><item><title>Shortening the critical path</title><link>https://endorama.dev/2023/shortening-the-critical-path/</link><pubDate>Tue, 14 Feb 2023 21:50:33 +0100</pubDate><guid>https://endorama.dev/2023/shortening-the-critical-path/</guid><description>&lt;p>After the recent post &lt;a href="https://endorama.dev/2023/remove-yourself-from-the-critical-path/">Remove yourself from the critical path&lt;/a> my friend and ex-colleague &lt;a href="https://www.linkedin.com/in/fabrizio-mirabito-5951a68/">Fabrizio Mirabito&lt;/a> gave me an insightful review: my definition of critical path is a synonym for &lt;em>dependency&lt;/em> and while removing yourself from the critical path is a great operational technique, the strategic and tactical approach should be to &lt;strong>remove all dependencies&lt;/strong> altogether. Will you be able to remove all of them? Maybe not, but to him this process is part of &lt;a href="https://en.wikipedia.org/wiki/Kaizen">&lt;em>kaizen&lt;/em>&lt;/a>, the &amp;ldquo;change for the better&amp;rdquo; Japanese business philosophy which I would roughly translate as &lt;em>continuous improvement&lt;/em>.&lt;/p>
&lt;p>I totally agree with his review, so here I am delving into the topic. This post is called &lt;em>Shortening&lt;/em> the critical path for a reason: while you must tend to remove &lt;strong>all&lt;/strong> dependencies, this will likely be an enormous task to undertake. In respect to &lt;em>kaizen&lt;/em> what we want is to &lt;em>aiming&lt;/em> at removing all dependencies, thus shortening the critical path. The shortest critical path possible is &lt;strong>zero&lt;/strong>: the only thing to do is doing the thing to do. No overhead.&lt;/p>
&lt;h1 id="if-its-impossible-does-it-make-sense-trying">If it&amp;rsquo;s impossible, does it make sense trying?&lt;/h1>
&lt;p>&lt;img src="https://endorama.dev/gifs/joueur-du-grenier-jdg.gif" alt="mission impossible to impossible mission">&lt;/p>
&lt;p>Short answer is &lt;strong>yes&lt;/strong>.&lt;/p>
&lt;p>Long answer starts from understanding the goal that I think we, as engineers working for a product company, have: &lt;strong>build great products that customer love to use, solving real needs, in an efficient and sustainable way&lt;/strong>. Is my opinion that this cannot be achieved just by focusing on making money, it requires an active focus on the main keywords of the previous sentence: &lt;strong>great product&lt;/strong>, &lt;strong>customer&lt;/strong>, &lt;strong>needs&lt;/strong>, &lt;strong>efficient&lt;/strong>, &lt;strong>sustainable&lt;/strong>. In my experience making money is a side effect of a great product, and a great product is a side effect of focusing on customer needs.&lt;/p>
&lt;p>Is not just about the goal, a long critical path (or a lot of dependencies) will bring in some relevant risks:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>partial done work&lt;/strong>: when you don&amp;rsquo;t finish something that partial work will be left in an uncompleted state; you may work on it again soon enough to complete it or it will become waste, as a raw food will become waste;&lt;/li>
&lt;li>&lt;strong>over production&lt;/strong>: if you are at constant risk of interruption you will naturally try to foresee possible future requests, baking them into your current product, hoping for them to prevent future work; most of the time this overproduction will become waste;&lt;/li>
&lt;li>&lt;strong>extra processing&lt;/strong>: handling dependencies (upstream or downstream) will require more work: more coordination, more planning, more meetings, more documents; Steve Jobs said &amp;ldquo;less is more&amp;rdquo; for a reason: you can&amp;rsquo;t cope with too much overhead, in the end it will just become a bloat that no one likes;&lt;/li>
&lt;li>&lt;strong>hand-offs&lt;/strong>: nothing kills velocity like hand-offs; have you ever watched a relay race? There is a clear additional challenge in passing the baton in a relay race, with require to slow down and great synchronisation.&lt;/li>
&lt;/ul>
&lt;p class="tip">It&amp;rsquo;s curious that &lt;a href="https://en.wikipedia.org/wiki/4_%C3%97_100_metres_relay">4×100m relay race world records&lt;/a> are &lt;strong>less&lt;/strong> than 4 times the &lt;a href="https://en.wikipedia.org/wiki/100_metres">100m records&lt;/a>. Why? In the 4×100 only 1 runner start from 0 speed, so that &amp;ldquo;penalty&amp;rdquo; does not impact that much the overall time.&lt;br>
It&amp;rsquo;s also curios that 4×100m world records are &lt;strong>less&lt;/strong> than 400m records of ~5 seconds. They are requiring 4 times the people involved for a ~12% increase.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>waiting&lt;/strong>: a key risk is waiting times and delay that reflect across the entire chain; if you&amp;rsquo;re part of a chain, any upstream delay will affect you and your delay will affect your downstream dependencies; waiting also hides in plain sight in some of the other risks, like hand-offs, extra processing or over production;&lt;/li>
&lt;li>&lt;strong>context switching&lt;/strong>: all the above risks sum up to generate the &lt;a href="https://powerrangers.fandom.com/wiki/Megazord">Megazorg&lt;/a> of velocity killers; context switch also plays a bug part in &lt;em>rework&lt;/em> (when something needs to be reworked because is not working as intended);&lt;/li>
&lt;li>&lt;strong>defects&lt;/strong>: what do you expect to happen if you never finish work, over produce, add additional processing, more hand-off and wait times and context switch across multiple domains? Defects. And in a downward spiral that will increase other risks.&lt;/li>
&lt;/ul>
&lt;h1 id="the-bad-parts">The bad parts&lt;/h1>
&lt;p>&lt;img src="https://endorama.dev/gifs/michael-jackson-bad.gif" alt="Michael Jackson singing Bad">&lt;/p>
&lt;p>While trying to mitigate this risks there are some anti-patterns that I suggest to look for and avoid doing.&lt;/p>
&lt;h2 id="-waterfall-development">🟨 Waterfall development&lt;/h2>
&lt;p>This post is not about Agile per se, even if is heavily inspired by it. Doing waterfall development (or any &amp;ldquo;agile&amp;rdquo; framework that package waterfall in shorter time frames) is in my opinion an anti-pattern. Waterfall &lt;em>implies&lt;/em> hand-offs, waiting and context switching.&lt;/p>
&lt;p>It may be I&amp;rsquo;ve never been exposed to a working waterfall development cycle, but I don&amp;rsquo;t think is the correct approach for building great software products. At least not in this present days with the fast changing landscape of requirements, needs and user expectations.&lt;/p>
&lt;h2 id="-layered-development">🟨 Layered development&lt;/h2>
&lt;p>When deciding how to work on a feature, fight the tendency to split it across infrastructure boundaries: there is no DB/Back-end/Front-end in a feature. No customer will ever see your Back-end. This will be looking at &lt;strong>your application&lt;/strong> and judge it in its entirety.&lt;/p>
&lt;p>Splitting across boundaries seems a great solution but tend to bring in partial done work (feature implemented in Back-end but not in Front-end), over production, hand-offs and waiting.&lt;/p>
&lt;h2 id="-100-utilisation">🟨 100% utilisation&lt;/h2>
&lt;p>You never want to be running at 100% utilisation. I spent a lot of time as SysAdmin and a 100% utilised resource (say CPU or disk space) &lt;strong>always&lt;/strong> signal troubles.&lt;/p>
&lt;p>The reason is intuitive: if you are running at 100% utilisation there is no room for error, no room for any unexpected occurrences. Anything different than &amp;ldquo;planned&amp;rdquo; will mean troubles, leading to partial done work, extra processing, context switch, defects.&lt;/p>
&lt;h2 id="-bus-factor-1">🟨 Bus factor 1&lt;/h2>
&lt;p>&lt;a href="https://en.wikipedia.org/wiki/Bus_factor">Bus factor&lt;/a> is a risk measurement that aims to surface risks associated with information silos: what is going to happen if - put here key person in your team, gets hit by a bus? We really hope this is just speculative, as being hit by a bus is bad.&lt;/p>
&lt;p>The bus factor is &amp;ldquo;the minimum number of team members that have to suddenly disappear from a project before the project stalls due to lack of knowledgeable or competent personnel.&amp;rdquo; (Thanks Wikipedia! 💗)&lt;/p>
&lt;p>How would your team/org/company cope with such an occurrence? If your bus factor is 1, all risks will be amplified but in particular waiting can become an infinite wait time.&lt;/p>
&lt;h1 id="the-good-parts">The good parts&lt;/h1>
&lt;p>&lt;a href="https://www.youtube.com/watch?v=iIq9JQ1qbbs">&lt;img src="https://endorama.dev/gifs/perfectenschlag-german.gif" alt="Used by Dwight Schrute, a fictional character of The Office, to mean when everything is perfect in a person’s life.">&lt;/a>&lt;/p>
&lt;p>There are some corrections that could be applied to mitigate these risks.&lt;/p>
&lt;h2 id="-self-organised-and-self-managed-teams">🟦 Self-organised and self-managed teams&lt;/h2>
&lt;p>The first countermeasure for the risks above is having self-organised teams that can self-manage themselves. Given a purpose, the right cross functional skills and a strategy to execute, use self-organisation and self-management as empowering tools for your teams.&lt;/p>
&lt;p>What do I mean by self-organised and self-managed team? Is a team where the work to be done is not assigned by the manager. The team members find their own work and manage the associated responsibilities and timelines. Is a team that has authority to choose the most effective way to organise their work. Is a team that is constantly looking around for ways to improve.&lt;/p>
&lt;p>There are some key principles for this to happen:&lt;/p>
&lt;ul>
&lt;li>ownership and responsibility: the core, having full ownership of a domain (it may be a specific software, or system, or library, …); this full ownership is balanced by full responsibilities on that domain.&lt;/li>
&lt;li>trust: no team can be a great team without trust; trust is based on psychological safety.&lt;/li>
&lt;li>communication: they require high bandwidth communication channels within the team itself; managing communications is probably one of the key challenges of creating such teams.&lt;/li>
&lt;/ul>
&lt;p>In the long term this kind of team will increase their efficiency and problem-solving capabilities, becoming true experts of the domain they have ownership on.&lt;/p>
&lt;p>A key strategy here for managers of such a team is to build bridges with other teams. Your team will be focused on delivery, understanding the domain and becoming experts. They will probably struggle with keeping in touch with other teams or other part of the organisation. There is a huge part to play here in building bridges for your team, as bridges will be the communication channels the team can use.&lt;/p>
&lt;p>This is also where some of the strategies highlighted in &lt;a href="https://endorama.dev/2023/remove-yourself-from-the-critical-path/">Remove yourself from the critical path&lt;/a> may come handy.&lt;/p>
&lt;h2 id="-own-your-tools">🟦 Own your tools&lt;/h2>
&lt;p>Tool ownership is paramount. By this I don&amp;rsquo;t mean &amp;ldquo;they should own their own bare metal servers&amp;rdquo;, I mean they need to own the tool directly involving their day to day work. All the DevOps principles, self-servicing internal platforms of sort are the foundation for tool ownership.&lt;/p>
&lt;p>Think of it like this: if you were an artisan (any craft applies) would you &lt;em>borrow&lt;/em> your tools? Maybe that one that you use not often, costs a lot and you need only for a brief time. You would never borrow the entire toolbox each time. So why do we allow or force our teams to work like that?&lt;/p>
&lt;h2 id="-kaizen">🟦 Kaizen&lt;/h2>
&lt;p>To become better you can&amp;rsquo;t leave improvements to chance. You need discipline and a process. &lt;a href="https://en.wikipedia.org/wiki/Kaizen">&lt;em>Kaizen&lt;/em>&lt;/a> is the Japanese term but you could just use &amp;ldquo;continuous improvement&amp;rdquo;. Invest part of your time in sharpening your tools, improving them, fixing them, revisiting processes and looking around constantly for things that may be improved.&lt;/p>
&lt;p>This attitude, more than process, will be groundbreaking. We &lt;em>always&lt;/em> underestimate the power of consistent small wins. Is the same concept as &amp;ldquo;compound interest&amp;rdquo;: each small win will be on top of other small wins. Each win stake on top and after some iteration you will end up with &lt;em>big&lt;/em> wins that would have been otherwise out of reach.&lt;/p>
&lt;p>A key strategy here is to &lt;strong>always question the process&lt;/strong>: does this process/step still provides value? Be obsessive in asking this question and ruthless in pursuing improvements. Be mindful of dependencies though; in a fully self-organised team there may be 0 dependencies affected by a process, but in case the number of dependencies is not 0 keep in mind the effort required for others to adjust to the new process.&lt;/p>
&lt;h2 id="-stop-starting-start-finishing">🟦 &amp;ldquo;Stop starting, start finishing&amp;rdquo;&lt;/h2>
&lt;p>How many things are you trying to do at once? If the answer is &amp;ldquo;too many&amp;rdquo;, you are failing prey of an anti-pattern. Context switching and high wait times leads to starting many things at once. You are waiting, &amp;ldquo;wasting&amp;rdquo; precious time, so the next &lt;em>obvious&lt;/em> step is to &lt;em>start something else&lt;/em>. &lt;strong>Wrong!&lt;/strong>&lt;/p>
&lt;p>When you are blocked, stuck in a queue or waiting for any reason the only logical next step is to &lt;strong>remove the blocker that is making you wait&lt;/strong>. This idea builds on top of Owning your tools and Kaizen. Instead of context switching to a new task, think how you could improve the current situation. Is your CI taking 1 hour to run? How could you make it run in 10 minutes? Do you need to get answers from outside your team? How can you arrange your communication channels to reduce wait?&lt;/p>
&lt;p>Stop starting new things, &lt;strong>focus&lt;/strong> on finishing what you have on your plate. 100 in progress and 0 done tasks provide 0 value to your company and your customers.&lt;/p>
&lt;p>A key strategy here is &lt;strong>limiting work in progress&lt;/strong>. Like for real, don&amp;rsquo;t do more.&lt;/p>
&lt;h2 id="-is-done-really-done">🟦 Is done really done?&lt;/h2>
&lt;p>One of the key elements in Scrum is the &lt;em>definition of done&lt;/em>. I think this concept is powerful and well worth exploring.&lt;/p>
&lt;p>Define what does it mean to be &amp;ldquo;done&amp;rdquo; with a task. And make it complete enough so that once done &lt;strong>it creates value&lt;/strong>. &amp;ldquo;Implement a feature&amp;rdquo; does mean nothing if it&amp;rsquo;s not deployed and customers are not using it. It will just be another &amp;ldquo;In review&amp;rdquo; (or worst, &amp;ldquo;Done&amp;rdquo;) item that will hunt your future self. If you happen to work with long release cycles you know this feeling, and is not great when 3 months after you merge a PR you discover there was a bug, but no one noticed because it slipped through testing (and no, no amount of testing will ever replace &lt;strong>real user behaviour&lt;/strong>).&lt;/p>
&lt;p>This strategy may be impractical to apply, but shortening your release cycles and focusing on providing short and effective feedback loops so that &amp;ldquo;done is really done&amp;rdquo; and you can move forward without worries is a superpower.&lt;/p>
&lt;h1 id="let-it-be">Let it be&lt;/h1>
&lt;p>I don&amp;rsquo;t think it&amp;rsquo;s ever possible to condense all the information useful for such a journey in a blog post. Maybe in some hundreds books, but still, there is so much to say, to explore, to try.&lt;/p>
&lt;p>Don&amp;rsquo;t make yourself overwhelmed by the challenge though. Use &lt;em>kaizen&lt;/em> as your guide, start looking for things to improve and allocate time to improve them. I&amp;rsquo;m quite sure you will never reach the perfection, as I&amp;rsquo;m sure than in 1 year from now looking back you will see a huge leap forward.&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;The longest journey begins with a single step.&amp;rdquo; - &lt;a href="https://en.wikipedia.org/wiki/Patanjali">Pantanjali&lt;/a>&lt;/p>
&lt;/blockquote></description></item><item><title>SSH config for ec2 machines</title><link>https://endorama.dev/2023/ssh-config-for-ec2-machines/</link><pubDate>Tue, 07 Feb 2023 18:45:12 +0100</pubDate><guid>https://endorama.dev/2023/ssh-config-for-ec2-machines/</guid><description>&lt;p>Recently I had the need to configure ssh access for some AWS EC2 machines. One annoying element of EC2 machines is that they default to user &lt;code>ec2-user&lt;/code>, which is easy to forget.&lt;/p>
&lt;p>Public routable EC2 instances hostnames is in the form of: &lt;code>ec2-&amp;lt;IP&amp;gt;.compute-1.amazonaws.com&lt;/code>. Due to this is easy to create a SSH config file entry to set the default user to &lt;code>ec2-user&lt;/code> for these hostnames:&lt;/p>
&lt;pre tabindex="0">&lt;code>Host *.amazonaws.com
User ec2-user
&lt;/code>&lt;/pre>&lt;p>From this point onward all connections to instances whose hostname ends in &lt;code>.amazonaws.com&lt;/code> will default to the correct value.&lt;/p>
&lt;p>&lt;img src="https://endorama.dev/imgs/ssh-ec2.png" alt="Screenshot of a terminal with working login to an EC2 instance with correct default user">&lt;/p>
&lt;p>What if I have a non-publicly routable instance? Depending on the hostname, you may be able to slightly change the configuration above to suit your needs. If you&amp;rsquo;re using IP addresses though, you should either do it manually for each host or find a way to automating the manual process.&lt;/p></description></item><item><title>Golang 1.20 - multiple errors</title><link>https://endorama.dev/2023/golang-1.20-multiple-errors/</link><pubDate>Thu, 02 Feb 2023 19:02:58 +0100</pubDate><guid>https://endorama.dev/2023/golang-1.20-multiple-errors/</guid><description>&lt;p>&lt;a href="https://go.dev/blog/go1.20">Golang 1.20&lt;/a> has been released 🎉&lt;/p>
&lt;p>One of the key changes that I noticed reading the release notes, is that it adds support for handling multiple errors natively. This is awesome, and I wanted to dig deeper into how to leverage this.&lt;/p>
&lt;p>This is what &lt;a href="https://go.dev/doc/go1.20#errors">release notes&lt;/a> say:&lt;/p>
&lt;blockquote>
&lt;h1 id="wrapping-multiple-errors">Wrapping multiple errors&lt;/h1>
&lt;p>Go 1.20 expands support for error wrapping to permit an error to wrap multiple other errors.&lt;br>
An error e can wrap more than one error by providing an &lt;code>Unwrap&lt;/code> method that returns a &lt;code>[]error&lt;/code>.&lt;br>
The &lt;code>errors.Is&lt;/code> and &lt;code>errors.As&lt;/code> functions have been updated to inspect multiply wrapped errors.&lt;br>
The &lt;code>fmt.Errorf&lt;/code> function now supports multiple occurrences of the &lt;code>%w&lt;/code> format verb, which will cause it to return an error that wraps all of those error operands.&lt;br>
The new function &lt;code>errors.Join&lt;/code> returns an error wrapping a list of errors.&lt;/p>
&lt;/blockquote>
&lt;p>The implementation is lean as usual, no cruft attached.&lt;/p>
&lt;p>Let&amp;rsquo;s review the changes.&lt;/p>
&lt;p>First, an error is considered wrapping &lt;em>multiple&lt;/em> errors when it has a method with the signature:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">Unwrap&lt;/span>() []&lt;span style="color:#66d9ef">error&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All relevant functions for error checking &lt;a href="https://pkg.go.dev/errors#Is">&lt;code>error.Is()&lt;/code>&lt;/a> and &lt;a href="https://pkg.go.dev/errors#As">&lt;code>error.As()&lt;/code>&lt;/a> has been updated, so they can be used to infer error presence in a chain even with multiple errors (neat!).&lt;br>
Formatting function has been updated too, so is possible to write:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Errorf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;%w and %w&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">err1&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Last but not least, we get a function to wrap multiple errors in a single one, &lt;a href="https://pkg.go.dev/errors#Join">&lt;code>errors.Join()&lt;/code>&lt;/a>.&lt;/p>
&lt;p>It seems all the needed building blocks are there, so let&amp;rsquo;s look at how to use them.&lt;/p>
&lt;h1 id="caveats">Caveats&lt;/h1>
&lt;h2 id="error-does-not-implement-unwrap-error">&lt;code>error&lt;/code> does not implement &lt;code>Unwrap() []error&lt;/code>&lt;/h2>
&lt;p>The first thing to notice here is that the builtin &lt;code>error&lt;/code> interface, &lt;strong>does not mention&lt;/strong> &lt;code>Unwrap() []error&lt;/code> at all (&lt;a href="https://pkg.go.dev/builtin#error">docs&lt;/a>), so is not possible to run &lt;code>Unwrap()&lt;/code> on errors joined with &lt;code>errors.Join()&lt;/code> or &lt;code>fmt.Errorf&lt;/code>.&lt;/p>
&lt;p>This is a bit confusing at first, as you can join errors very easily but then is not possible to unwrap them.&lt;/p>
&lt;h2 id="errorsunwrap-will-return-nil-for-multiple-errors">&lt;code>errors.Unwrap()&lt;/code> will return &lt;code>nil&lt;/code> for multiple errors&lt;/h2>
&lt;p>As specified &lt;a href="https://github.com/golang/go/issues/53435#issuecomment-1191752789">in the proposal&lt;/a>, &lt;a href="https://pkg.go.dev/errors#Unwrap">&lt;code>errors.Unwrap()&lt;/code>&lt;/a> will returns &lt;code>nil&lt;/code> if the &lt;code>Unwrap&lt;/code> method returns &lt;code>[]error&lt;/code>.&lt;/p>
&lt;p>This decision prevent this change to break compatibility with previous Golang versions, but is a caveat to keep in mind.&lt;/p>
&lt;h1 id="examples">Examples&lt;/h1>
&lt;h2 id="using-errors">Using &lt;code>error&lt;/code>s&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err1&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">New&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;err1&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err2&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">New&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;err2&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">errs&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">Join&lt;/span>(&lt;span style="color:#a6e22e">err1&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>errs&lt;/code> will be report true to &lt;code>errors.Is&lt;/code> for both &lt;code>err1&lt;/code> and &lt;code>err2&lt;/code>.&lt;/p>
&lt;p>But &lt;code>errs&lt;/code> does not implement &lt;code>.Unwrap()&lt;/code>, so there is no way to unwrap this error and reuse single errors.&lt;/p>
&lt;p>&lt;a href="https://go.dev/play/p/i8t0mIJyKMi?v=gotip">Playground code&lt;/a>&lt;/p>
&lt;h2 id="formatting-errors">Formatting &lt;code>error&lt;/code>s&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err1&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">New&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;err1&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">err2&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">New&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;err2&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">errs&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Errorf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34;%w | %w&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">err1&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As in the previous example, &lt;code>errs&lt;/code> will be report true to &lt;code>errors.Is&lt;/code> for both &lt;code>err1&lt;/code> and &lt;code>err2&lt;/code>, but does not implement &lt;code>.Unwrap()&lt;/code>.&lt;/p>
&lt;p>&lt;a href="https://go.dev/play/p/71fPqsId8CH?v=gotip">Playground code&lt;/a>&lt;/p>
&lt;h2 id="custom-error">Custom error&lt;/h2>
&lt;p>If we build a custom type implementing &lt;code>error&lt;/code> and the new &lt;code>Unwrap()&lt;/code> function, we can range over errors, as I initially expected.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">type&lt;/span> &lt;span style="color:#a6e22e">CustomError&lt;/span> &lt;span style="color:#66d9ef">struct&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">msg&lt;/span> &lt;span style="color:#66d9ef">string&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">errs&lt;/span> []&lt;span style="color:#66d9ef">error&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> (&lt;span style="color:#a6e22e">f&lt;/span> &lt;span style="color:#a6e22e">CustomError&lt;/span>) &lt;span style="color:#a6e22e">Error&lt;/span>() &lt;span style="color:#66d9ef">string&lt;/span> { &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">f&lt;/span>.&lt;span style="color:#a6e22e">msg&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">func&lt;/span> (&lt;span style="color:#a6e22e">e&lt;/span> &lt;span style="color:#a6e22e">CustomError&lt;/span>) &lt;span style="color:#a6e22e">Unwrap&lt;/span>() []&lt;span style="color:#66d9ef">error&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#66d9ef">return&lt;/span> &lt;span style="color:#a6e22e">e&lt;/span>.&lt;span style="color:#a6e22e">errs&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#f92672">...&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">errs&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#a6e22e">CustomError&lt;/span>{&lt;span style="color:#e6db74">&amp;#34;My Error&amp;#34;&lt;/span>, []&lt;span style="color:#66d9ef">error&lt;/span>{&lt;span style="color:#a6e22e">err1&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>}}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">Is&lt;/span>(&lt;span style="color:#a6e22e">errs&lt;/span>, &lt;span style="color:#a6e22e">err1&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34; err is err1&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#a6e22e">errors&lt;/span>.&lt;span style="color:#a6e22e">Is&lt;/span>(&lt;span style="color:#a6e22e">errs&lt;/span>, &lt;span style="color:#a6e22e">err2&lt;/span>) {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#e6db74">&amp;#34; err is err2&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">_&lt;/span>, &lt;span style="color:#a6e22e">e&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#66d9ef">range&lt;/span> &lt;span style="color:#a6e22e">errs&lt;/span>.&lt;span style="color:#a6e22e">Unwrap&lt;/span>() {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Printf&lt;/span>(&lt;span style="color:#e6db74">&amp;#34; unwrapped err: %s\n&amp;#34;&lt;/span>, &lt;span style="color:#a6e22e">e&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://go.dev/play/p/NGTiVYfhzie?v=gotip.go?download=true">Playground code&lt;/a>&lt;/p>
&lt;p>This is great, and I find it as a nice improvement over what I&amp;rsquo;m doing in my tool &lt;a href="https://github.com/endorama/devid">&lt;code>endorama/devid&lt;/code>&lt;/a>.&lt;/p>
&lt;p>Admittedly the solution was decent, using 2 return values &lt;code>([]err, err)&lt;/code> where the usual error would signal the possibility for other errors to be present. But was lacking integrated support for checking the error chain, so that will make a great improvement!&lt;/p></description></item><item><title>Remove yourself from the critical path</title><link>https://endorama.dev/2023/remove-yourself-from-the-critical-path/</link><pubDate>Thu, 26 Jan 2023 22:14:33 +0100</pubDate><guid>https://endorama.dev/2023/remove-yourself-from-the-critical-path/</guid><description>&lt;p>During one of my recent mentoring sessions, an interesting discussion started with my mentee. How do you manage being a leader with being hands-on too?&lt;/p>
&lt;p>If you have some experience in leading a group I suspect you faced the same issue at some point, maybe not with coding but for sure around the &amp;ldquo;I can&amp;rsquo;t be one of the hands-on people anymore&amp;rdquo;. I don&amp;rsquo;t think this is necessarily true, but being hands-on and a great leader is a tough challenge and its practicality is heavily influenced by the environment around it.&lt;/p>
&lt;p>During our session the main issue were interruptions and meetings driving away time from focused work.&lt;/p>
&lt;p>&lt;img src="https://endorama.dev/gifs/meetings-meeting.gif" alt="meetings!">&lt;/p>
&lt;p>I there a way out of this? I don&amp;rsquo;t know, but in my experience you can help yourself. The goal in this situation is to create space for deep work, chunks of time in which you expect no interruptions and no meetings. In which you can focus and do deep work.&lt;/p>
&lt;p>There are calendar blocking techniques that can be tried out, but that&amp;rsquo;s like curing the symptoms and not the causes: if everybody needs you, you will never be able to get that focus time, because there will always be something requiring you attention.&lt;/p>
&lt;p>We can now agree that improving on this situation is hard, requires dedication and a lot of learning. Especially if you are moving from an individual contributor role to a team leading role, as you need to adjust your thinking and approach; you are now the one everybody else count on.&lt;/p>
&lt;p>My suggestion here would be: remove yourself from the critical path. Stop being the one everybody needs to go to.&lt;/p>
&lt;h1 id="whats-the-critical-path">What&amp;rsquo;s the critical path?&lt;/h1>
&lt;p>Doing something, in a generic way, involves going from a Point A to a Point B. To move from A to B a series of steps are required. The critical path is that path from A to B that you &lt;strong>cannot avoid&lt;/strong> taking; is composed of those steps that are mandatory to move from A to B.&lt;/p>
&lt;p>Let&amp;rsquo;s visualise this with some example. Let&amp;rsquo;s say we are travelling from City A to City B. It doesn&amp;rsquo;t matter which transport are we using, we will need to go through some physical places. And we hope there will be no accident, delay or impediment along our route, because that would slow us down. Or maybe we are taking a taxi, we hope there will be no slow downs as that will make the run more costly. Or maybe you need to study for an exam, so you will want to avoid getting sick, as that would reduce the time you have available for studying, making you risk to fail the exam.&lt;/p>
&lt;p>The &lt;strong>critical path&lt;/strong> is the series of &lt;strong>steps&lt;/strong> that &lt;strong>needs to be performed&lt;/strong> to &lt;strong>achieve&lt;/strong> an objective. Any impediment in the critical path will make achieving the objective be &lt;strong>more time consuming&lt;/strong>, &lt;strong>more costly&lt;/strong> and/or &lt;strong>not be ready for the deadline&lt;/strong>.&lt;/p>
&lt;p>We can say that this critical path is &lt;strong>the most important&lt;/strong> element you want to watch.&lt;/p>
&lt;h1 id="am-i-in-the-critical-path">Am I in the critical path?&lt;/h1>
&lt;p>This is a worthy question! How do you identify if you are in the critical path?&lt;/p>
&lt;p>&lt;img src="https://endorama.dev/gifs/the-emperor-kronk.gif" alt="It doesn&amp;rsquo;t make sense, Kronk showing a map in The Emperor new groove">&lt;/p>
&lt;p>Does any of this happen to you:&lt;/p>
&lt;ul>
&lt;li>other come to you for answers that they need to do their work&lt;/li>
&lt;li>only you have the knowledge needed by others&lt;/li>
&lt;li>you get interrupted frequently (by your reports, your peers or your manager)&lt;/li>
&lt;li>you need to take decisions and people depends on them, so they are blocked waiting for your decision&lt;/li>
&lt;/ul>
&lt;p>If yes &lt;strong>you are in the critical path&lt;/strong>. And now what?&lt;/p>
&lt;p>There are some strategies you may try to move away from it. Those strategies require time and trust as their main ingredients, as removing yourself from the critical path is &lt;strong>all about delegation&lt;/strong>. You need to learn how to delegate (a skill that you generally don&amp;rsquo;t need as individual contributor), and the more effective you will be at it, the more effective your work as a manager.&lt;br>
The hard part of delegation is that is not about doing something, &lt;strong>is about not doing&lt;/strong> something. But that something has to happen anyway. Delegation is hard. Delegation involves trust. Trust is even harder.&lt;/p>
&lt;p>So how do you build trust? Here are some techniques. In the next examples I&amp;rsquo;ll be using teacher and student as &amp;ldquo;the one that knows how to perform a task right now&amp;rdquo; and &amp;ldquo;the one that should learn how to perform a task in the future&amp;rdquo;.&lt;/p>
&lt;h2 id="shadowing">Shadowing&lt;/h2>
&lt;p>Is there a task you do that someone else could do with some training? How do you deliver that training? Shadowing may be a solution. The student follows (or &amp;ldquo;shadows&amp;rdquo;) the teacher while they perform the task. The goal is for the student to be independent in executing the task after a set period.&lt;/p>
&lt;p>Other literature is available, but I want to give some hints:&lt;/p>
&lt;ul>
&lt;li>define a deadline after which the shadowing period ends&lt;/li>
&lt;li>apply to task that may benefit from observation (es interviewing, meetings) but do not apply to heavy concept base tasks where a lot of prior knowledge is required&lt;/li>
&lt;/ul>
&lt;h2 id="pairing">Pairing&lt;/h2>
&lt;p>Do you have a knowledge-heavy task at hand that you would like to delegate but don&amp;rsquo;t know how as it requires a lot of prior knowledge? In this case shadowing is probably not enough. Do pairing sessions, as in &amp;ldquo;code pairing&amp;rdquo; sessions, but for other topics.&lt;/p>
&lt;p>During those sessions select a driver and a writer. The driver direct how to do things, the writer executes. Exchange roles after some time. This is a great way to get to know the other person and build trust, while sharing knowledge.&lt;/p>
&lt;h2 id="proposal-documents">Proposal documents&lt;/h2>
&lt;p>Do you have a task that needs some thinking, where multiple solutions needs to be explored and there is no clear solution? Delegate writing a proposal document and review it together. Going through a document has many benefits: it allows asynchronous communications, will last for future people (&amp;ldquo;verba volant, scripta manent&amp;rdquo; Latins used to say) and will provide for time and space to have an insightful conversation.&lt;/p>
&lt;p>Do remember to appoint a driver for the document, who will be in charge of moving from draft to the final decision. The driver does not have to be the one taking the decision too, on this subject there is a whole lot of literature, so go surfing!&lt;/p>
&lt;h1 id="pitfalls">Pitfalls&lt;/h1>
&lt;p>Delegation does not come without its risks. Watch out for things that you don&amp;rsquo;t want to happen, some of which I summarise below.&lt;/p>
&lt;p>&lt;img src="https://endorama.dev/gifs/pull-lever-kronk-izma.gif" alt="Kronk pulling the wrong lever, Izma falling">&lt;/p>
&lt;h2 id="not-all-tasks-are-the-right-candidate-for-delegation">Not all tasks are the right candidate for delegation&lt;/h2>
&lt;p>That task the CEO is really looking for and keeps you accountable for? Probably that&amp;rsquo;s not the best task to delegate.&lt;/p>
&lt;p>Not all tasks can be delegated, but fortunately not all tasks have the same importance (as &lt;code>impact * complexity * urgency&lt;/code>). Knowing which task to delegate is one of the hidden skills of great leaders. I think that for being great at it you need to remember that half of it is your judgement but the other half is the environment around you.&lt;/p>
&lt;p>Some tasks that may be good candidates are: any decision that can be reverted (from less expensive to most expensive to revert), any task outside of the critical path, any &amp;ldquo;nice to have&amp;rdquo;.&lt;/p>
&lt;p>Another judgement method you can apply here is what you are aiming for: are you aiming for more personal focus time? Are you aiming to making sure the critical path does not have any impediment? Are you trying to upskill someone? Keep in mind your goal when delegating, as it affects your decisions.&lt;/p>
&lt;p>One framework to help here is the &lt;a href="https://en.wikipedia.org/wiki/Time_management#The_Eisenhower_Method">Eisenhower Matrix&lt;/a>.&lt;/p>
&lt;h2 id="dont-setup-people-for-failure">Don&amp;rsquo;t setup people for failure&lt;/h2>
&lt;p>If you remove yourself from critical paths but is not safe to do so or is not appropriate given your team health and competence, you are setting up people for failure.&lt;/p>
&lt;p>Failure brings pressure and frustration. Frustration leads to fear or anger. And we know how this ends, fear leads to the dark side.&lt;/p>
&lt;p>Over time people will get disengaged, they may even get mentally or physically sick due to the excessive burden and in the end leave the group (or company). Don&amp;rsquo;t be that leader.&lt;/p>
&lt;h2 id="be-okay-with-people-failing">Be okay with people failing!&lt;/h2>
&lt;p>Have you ever done something for the first time? Like riding a bike, singing, studying a subject? Where you good at it? I bet not that much.&lt;/p>
&lt;p>When someone else will start doing something, in particular something you are used to do frequently, they will probably not be good at it. They are building &lt;strong>their way&lt;/strong> of doing it, and the only way for this to happen and to make them grow is to allow them to fail in a safe space. Your goal is to &lt;strong>build the safe space&lt;/strong>, not to prevent them from failing.&lt;/p>
&lt;h2 id="be-okay-with-having-hard-talks">Be okay with having hard talks&lt;/h2>
&lt;p>People you delegate to will not do everything right. As &lt;strong>you&lt;/strong> will be responsible for the outcome, you need to learn to have hard conversations.&lt;/p>
&lt;p>You need to learn how to talk about issues, missteps and errors. My advise is to learn how to be emphatetic, respectful and transparent. We tend to associate negative feedback with something bad, but learning how to give negative feedback in a human and constructive way is a golden skill, it build trust.&lt;/p>
&lt;p>If you think this is hard, consider the alternative: everything will be great until it won&amp;rsquo;t be anymore, the number of wrong things will probably be too much and you will have way worst conversations to have.&lt;/p>
&lt;h2 id="stop-thinking-about-the-effort-start-thinking-about-the-outcome">Stop thinking about the effort, start thinking about the outcome&lt;/h2>
&lt;p>When people in your team feel safe and empowered they will do awesome things together. A team &lt;strong>is more than the sum of its components&lt;/strong>. &lt;strong>Your team&lt;/strong> will be more resilient, more effective. &lt;strong>You&lt;/strong> will be more resilient and more effective.&lt;/p>
&lt;hr>
&lt;p>&lt;img src="https://endorama.dev/gifs/foo-fighters-done-done.gif" alt="Foo Fighters, done done!">&lt;/p>
&lt;p>Whew, this was a long one! This advice does not applies to managers/team-lead only, I found it useful in other roles too. As always, the goal is not to be perfect but to understand how to improve and acquire some new tools for your tool belt.&lt;/p>
&lt;p>As leaders, leading other people is a challenge but will give back many rewards if you&amp;rsquo;re committed to improve and clearly show that you care for people.&lt;/p></description></item><item><title>My note taking in 2022</title><link>https://endorama.dev/2023/my-note-taking-in-2022/</link><pubDate>Wed, 11 Jan 2023 18:52:01 +0100</pubDate><guid>https://endorama.dev/2023/my-note-taking-in-2022/</guid><description>&lt;p>During 2021 I completely restructured the way I was taking notes. The goal was to up the game and be more effective and get more proficient at it.&lt;/p>
&lt;p>At that time I was using &lt;a href="https://standardnotes.com/">Standard Notes&lt;/a>, an e2e encrypted note taking app with rich features. I still think is a great app, but after using it for some years I noticed it was coming short: note search was missing full text search and organising them through tags was not enough.&lt;br>
I would still recommend it if you take linear notes as it&amp;rsquo;s packed with features and developed by a passionate team set up for the long run.&lt;/p>
&lt;p>When I started searching for alternatives, I got exposed to the idea of &amp;ldquo;non-linear note taking&amp;rdquo;.&lt;/p>
&lt;h1 id="1-minute-history-of-note-taking">1 Minute history of note taking&lt;/h1>
&lt;p>We take notes to free our mind from having to recall all of them.&lt;/p>
&lt;p>Since the invention of writing, note taking has played an important role in human history and scientific development. &lt;a href="https://en.wikipedia.org/wiki/Note-taking">Quoting Wikipedia&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>The Ancient Greeks developed hypomnema, personal records on important subjects. In the Renaissance and early modern period, students learned to take notes in schools, academies and universities, often producing beautiful volumes that served as reference works after they finished their studies. In pre-digital times, people used many kinds of notebooks, including commonplace books, accounting waste books, and marginalia.&lt;/p>
&lt;/blockquote>
&lt;p>Note taking is complex, as it requires a moltitude of cognitive function to produce a working result. Is also a requirement to manage large amount of information.&lt;/p>
&lt;p>Note taking takes also different shapes and forms, in the effort to structure the content so it&amp;rsquo;s easier to find and consume at a later moment. We can cluster different methods in 2 main categoris: &lt;strong>linear&lt;/strong> and &lt;strong>non-linear&lt;/strong> note taking.&lt;/p>
&lt;h1 id="linear-note-taking">Linear note taking&lt;/h1>
&lt;p>Linear notes take the form of outlines or sentences. Outlines can be bullet points or phrases, organised and structured in a logical way from top to bottom, separated by headings. Sentences are short phrases that summarise a concept, and is losely organised.&lt;/p>
&lt;h1 id="non-linear-note-taking">Non-linear note taking&lt;/h1>
&lt;p>If you ever created a mind map you used a non-linear note taking method. In non-linear note taking notes are split as much as possible to bite-sized information that are then clustered.&lt;/p>
&lt;p>Some of these methods come from long time ago: &lt;a href="https://en.wikipedia.org/wiki/Cornell_Notes">Cornell Note system&lt;/a> is from 1950, &lt;a href="https://en.wikipedia.org/wiki/Zettelkasten">Zettelkasten system&lt;/a> is from &lt;strong>mid 16th Century&lt;/strong>.&lt;/p>
&lt;h1 id="welcome-to-the-digital-age">Welcome to the digital age&lt;/h1>
&lt;p>So it&amp;rsquo;s 2022 and looking at way to take notes I start digging deep into non-linear note taking. That&amp;rsquo;s the moment in which my fellow Standard Notes does not keep up. It was also the moment in which &lt;a href="https://roamresearch.com/">Roam Research&lt;/a> started becoming popular, as its (freemium + file based) alternative &lt;a href="https://obsidian.md/">Obsidian.md&lt;/a>.&lt;/p>
&lt;p>Obsidian is a great and extensible tool, powerful and file-based. This means I&amp;rsquo;m in control of the content, which is in a standard (and common for me) format, Markdown with frontmatter.&lt;/p>
&lt;p>I started using Obsidian, breaking down my notes experimenting with Zettelkasten. That proved incredibly useful for content I wanted to learn and remember.&lt;/p>
&lt;p>I was facing a new issue though, as I came to Obsidian early enough that there were still some rough edges. One of them was the ability to easily sync across devices (without paying) and collecting thoughs on the go. Another was using it to take linear notes in a fast way while following talks, videos or courses.&lt;/p>
&lt;p>To fill this gap I discovered &lt;a href="https://workflowy.com/">Workflowy&lt;/a>, which is an infinite nestable bullet list. Workflowy power lies in its flexibility. You can basically do whatever you want and build whatever system you can think of using it. It also allows to tag bullet, link and mirror them. It behaves likes an hybrid linear/non-linear On top of all this, its pricing tag is &lt;em>amazing&lt;/em> compared to the competitors.&lt;/p>
&lt;p>They both served me well and I&amp;rsquo;m happy with the setup, even if there are still rough edges that I plan to iron out in the future.&lt;/p>
&lt;h1 id="2022-ends">2022 ends&lt;/h1>
&lt;p>This is my setup at the end of 2022:&lt;/p>
&lt;ul>
&lt;li>In Workflowy:
&lt;ul>
&lt;li>all things to follow up go to Workflowy, as child of the &lt;em>Inbox&lt;/em> bullet; untagged, rough, mostly links or spare thoughs. Goal is to insert them &lt;strong>as fast as possible&lt;/strong>.&lt;/li>
&lt;li>under a &lt;em>Personal&lt;/em> bullet I have:
&lt;ul>
&lt;li>an &lt;a href="https://workflowy.com/template/eisenhower-todo">Eisenhover TODO matrix&lt;/a>&lt;/li>
&lt;li>ideas I have, tagged; generally moved from Inbox after reflection&lt;/li>
&lt;li>info on Projects I work on&lt;/li>
&lt;li>spare notes on meetings I have&lt;/li>
&lt;li>a collection of interesting quotes&lt;/li>
&lt;li>a collection of possible presents for my family and friends&lt;/li>
&lt;li>an Archive page where I move information that is not relevant anymore&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>under a &lt;em>Work&lt;/em> bullet I have:
&lt;ul>
&lt;li>an &lt;a href="https://workflowy.com/template/eisenhower-todo">Eisenhover TODO matrix&lt;/a>&lt;/li>
&lt;li>other information about day to day things organised with the &lt;a href="https://workflowy.com/systems/para-method/">PARA method&lt;/a>.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>In Obsidian:
&lt;ul>
&lt;li>a Zettelkasten system where I add information&lt;/li>
&lt;li>small notes, each linked to a single Topic and tagged with cross-note or topic&lt;/li>
&lt;li>a series of pages to collect related pages; for example: listing all conference notes, all people notes, all topics notes.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>Obsidian has a nice little feature, is able to graph the relation between notes. This is how my graph looks like:&lt;/p>
&lt;p>&lt;img src="https://endorama.dev/imgs/note-taking-2022.png" alt="My Obsidian graph">&lt;/p>
&lt;p>The power of this structure is that I&amp;rsquo;m creating links between notes, which translates to link between concepts or people. These links are powerful for creating new content and refining ideas on past content.&lt;/p>
&lt;h1 id="whats-next">What&amp;rsquo;s next?&lt;/h1>
&lt;p>I&amp;rsquo;m happy with the current process, but everything can be improved and there are some rough edges that I&amp;rsquo;d like to smooth.&lt;/p>
&lt;p>I&amp;rsquo;m currently running 3 main experiements: a reMarkable tablet, Logseq and Voiceliner.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>As an unexpected present I recently received a &lt;a href="https://remarkable.com">reMarkable 2 tablet&lt;/a>. Taking note by hand again, but in a digitalised fashion, is refreshing and something I was really looking forward. I&amp;rsquo;ve not used it enough to fully understand where to put it in my workflow, if not for highlighting technical books or articles I read. I plan to extend its usage and integrate it with other tools, how is still unclear.&lt;br>
As there are scientific studies (like &lt;a href="https://journals.sagepub.com/doi/10.1177/0956797614524581">The Pen Is Mightier Than the Keyboard&lt;/a>) that point that taking notes by hand is better that typing them for learning, what I expect to be doing in the future is add an additional first step by hand that then gets digitalised and moved to its digital storage.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The other experiment I&amp;rsquo;m running is with &lt;a href="https://logseq.com/">Logseq&lt;/a>. A thing that I&amp;rsquo;ve not been able to fit in all this so far has been journaling. Workflowy makes it too manual, Obsidian requires plugins or manual toil; overall it seems is neither provides a first hand experience on this. Logseq does. Another element that I&amp;rsquo;m eager to try are flash cards, but adding another tool seems to outweight the value, and it really does not fit in Workflowy and partially fits in Obsidian. Logseq does that to! Additionally Logseq has a bullet point approch like Workflowy and is file based like Obsidian. It&amp;rsquo;s promising, albeit still in early stage (but the community around it is great).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>The last experiment is with &lt;a href="https://a9.io/voiceliner/">Voiceliner&lt;/a>, a speech to text recognizer bundled in a mobile app that produces Markdown files. By syncing them to Obsidian or Logseq I foresee some interesting integration there!&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>All of this has been quite a journey, but I&amp;rsquo;m extremely pleased at how much value I get from it. The next step will be to consolidate and integrate tools, form better note taking habits and better define processes around &lt;strong>getting value&lt;/strong> from this personal knowledge base. In the end, that&amp;rsquo;s why we take note in the first place.&lt;/p></description></item><item><title>What I'm doing Now</title><link>https://endorama.dev/now/</link><pubDate>Mon, 28 Nov 2022 22:37:52 +0200</pubDate><guid>https://endorama.dev/now/</guid><description>&lt;p>&lt;em>This is a &lt;a href="https://nownownow.com/about">now&lt;/a> page, inspired by &lt;a href="https://sive.rs/nowff">Derek Sivers&lt;/a>.&lt;/em>&lt;/p>
&lt;h2 id="work">Work&lt;/h2>
&lt;p>Since 2021, I&amp;rsquo;m working at &lt;a href="https://www.elastic.co/">Elastic&lt;/a>, the Search, Observe and Protect company, as a software engineer in the team responsible for data intake for Elastic Observability.&lt;/p>
&lt;h2 id="open-source">Open Source&lt;/h2>
&lt;p>This is a list of project I&amp;rsquo;m actively involved:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/endorama/2ami">endorama/2ami&lt;/a> - Your easy 2FA companion that keep the secrets secret&lt;/li>
&lt;li>&lt;a href="https://github.com/ansible-collections/community.sops">ansible-collections/community.sops&lt;/a> - Simple and flexible tool for managing secrets&lt;/li>
&lt;li>&lt;a href="https://github.com/endorama/asdf-parse-tool-versions">endorama/asdf-parse-tool-versions&lt;/a> - A tool to parse &lt;a href="https://asdf-vm.com/">asdf&lt;/a> &lt;code>.tool-versions&lt;/code> in a GitHub action and reuse the specified values&lt;/li>
&lt;/ul>
&lt;h2 id="life">Life&lt;/h2>
&lt;p>Recently married, facilitator for &lt;a href="https://climatefresk.org">Climate Fresk&lt;/a> workshops. Serial note taker.&lt;/p></description></item><item><title>Making your shell scripts bulletproof pt.2</title><link>https://endorama.dev/2022/making-your-shell-scripts-bulletproof-pt.2/</link><pubDate>Mon, 28 Nov 2022 21:31:39 +0100</pubDate><guid>https://endorama.dev/2022/making-your-shell-scripts-bulletproof-pt.2/</guid><description>&lt;p>I posted some suggestion to improve robustness of shell scripts in https://endorama.dev/2022/making-your-shell-scripts-bulletproof/, but there are a few more that I usually use that I think may be useful, so here is part 2 of it.&lt;/p>
&lt;h2 id="dont-split-words-on-space">Don&amp;rsquo;t split words on space&lt;/h2>
&lt;blockquote>
&lt;p>The Internal Field Separator that is used for word splitting after expansion and to split lines into words with the read builtin command. The default value is &lt;code>&amp;lt;space&amp;gt;&amp;lt;tab&amp;gt;&amp;lt;newline&amp;gt;&lt;/code>.&lt;/p>
&lt;/blockquote>
&lt;p>From &lt;a href="https://man7.org/linux/man-pages/man1/bash.1.html">BASH manual&lt;/a>, under &lt;code>SHELL BUILTIN COMMANDS &amp;gt; Shell Variables&lt;/code>.&lt;/p>
&lt;p>The default value of this variable means BASH split lines on spaces too. Most of the time this is a very confusing behaviour, as we are not used to consider a space a sentence terminator or split point (compared to newline or tab character).&lt;/p>
&lt;p>To change this to only newlines and tabs use:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>IFS&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">$&amp;#39;\n\t&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>99% of the time I never ever noticed the difference in my shell scripts, and if you really need splitting on space you can always prefix your command with &lt;code>IFS=$' '&lt;/code>.&lt;/p>
&lt;p>You may also want to save the original IFS value and you can do so by:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>ORIGINAL_IFS&lt;span style="color:#f92672">=&lt;/span>$IFS
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>IFS&lt;span style="color:#f92672">=&lt;/span>&lt;span style="color:#e6db74">$&amp;#39;\n\t&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This way you can still use the old IFS value by prefixing commands with &lt;code>IFS=$ORIGINAL_IFS&lt;/code>.&lt;/p>
&lt;h2 id="use-traps">Use traps&lt;/h2>
&lt;p>&amp;hellip;.&lt;/p>
&lt;h2 id="use-shellcheck">Use shellcheck&lt;/h2>
&lt;p>I don&amp;rsquo;t think I came across something more beneficial to my shell script writing than Shellcheck. &lt;a href="https://github.com/koalaman/shellcheck">Shellcheck&lt;/a> is a static analysis tool for shell scripts (BASH is supported too).&lt;/p>
&lt;p>The reason why you must run this is that will help you avoid all those errors you may be unconsciously making right now.&lt;/p>
&lt;h2 id="stop-formatting-your-code">Stop formatting your code&lt;/h2>
&lt;p>&amp;hellip;manually!. Use &lt;a href="https://github.com/mvdan/sh#shfmt">&lt;code>shfmt&lt;/code>&lt;/a> to do it automatically! &lt;code>shfmt&lt;/code> is part of a Golang-based shell parser and interpreter, a really cool project.&lt;/p>
&lt;h2 id="test-bash-scripts-with-bats">Test BASH scripts with &lt;code>bats&lt;/code>&lt;/h2>
&lt;p>Unit testing, in BASH. &lt;strong>HELL YEAH&lt;/strong>! &lt;a href="https://github.com/bats-core/bats-core/">&lt;code>bats&lt;/code>&lt;/a> is a small framework to do unit testing in BASH (you can actually use it for any command that BASH can run, but let&amp;rsquo;s stay focused here).&lt;/p>
&lt;p>Happy coding 😉&lt;/p></description></item><item><title>Bring out the trash!</title><link>https://endorama.dev/2022/bring-out-the-trash/</link><pubDate>Mon, 28 Nov 2022 20:31:39 +0100</pubDate><guid>https://endorama.dev/2022/bring-out-the-trash/</guid><description>&lt;p>I recently stumbled upon the usual issue with all my development machines: &amp;ldquo;no space left on device&amp;rdquo;.&lt;/p>
&lt;p>As it happens frequently, I had to spin up the trusted disk analyser, &lt;a href="https://wiki.gnome.org/action/show/Apps/DiskUsageAnalyzer?action=show&amp;amp;redirect=Apps%2FBaobab">Baobab&lt;/a> and dig into it.&lt;/p>
&lt;p>Something was filling up my 512GB disk, but exactly what?&lt;/p>
&lt;h2 id="my-home-folder">My home folder&lt;/h2>
&lt;p>Well, that wasn&amp;rsquo;t that suprising, but:&lt;/p>
&lt;ol>
&lt;li>My Trash was &lt;code>31.2GB&lt;/code>!&lt;/li>
&lt;li>I used &lt;a href="https://endorama.dev/2022/asdf-little-awesome-tool/">asdf&lt;/a> to manage programming language interpreter/runtimes, this was taking &lt;code>~30GB&lt;/code>, 24 of them being taken by Golang installations and relative packages&lt;/li>
&lt;li>My &lt;code>code&lt;/code> folder, where I keep all code I&amp;rsquo;ve been hacking/working on, was &lt;code>~24GB&lt;/code> ⛰ Massive!&lt;/li>
&lt;/ol>
&lt;h2 id="nix">Nix&lt;/h2>
&lt;p>I started playing with &lt;a href="https://nixos.org/">Nix&lt;/a> and &lt;a href="https://github.com/nix-community/home-manager">Home Manager&lt;/a> to manage external binaries I want to add to my workstation (that runs elementary OS Hera at the moment).&lt;/p>
&lt;p>It replaces &lt;a href="https://brew.sh/">Homebrew&lt;/a>, that I loved but has it&amp;rsquo;s drawbacks on Linux: compiling things was a total nightmare, and it happened quite a lot.&lt;/p>
&lt;p>I was also extremely intrigued by the declarative approach Nix that promises reproducible configurations, something I&amp;rsquo;m trying to achieve to reduce surprises (works on my computer means work on remote server, this is a dream for a SysAdmin).&lt;/p>
&lt;p>In the end I had 99GB in &lt;code>/nix&lt;/code> 😱&lt;/p>
&lt;p>To clean them up I discovered I needed to run GC and optimise the &lt;code>nix-store&lt;/code>:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ nix-store --gc
$ nix-store --optimise
$ nix-collect-garbage -d
&lt;/code>&lt;/pre>&lt;p>These commands combined cleaned up around &lt;code>50GB&lt;/code> of disk space 😎&lt;/p>
&lt;p>I followed along &lt;a href="https://nixos.wiki/wiki/Cleaning_the_nix_store">this&lt;/a> and &lt;a href="https://nixos.wiki/wiki/Storage_optimization">this&lt;/a> articles.&lt;/p>
&lt;h2 id="snaps">Snaps&lt;/h2>
&lt;p>The next step were snaps, where &lt;code>~7GB&lt;/code> where used.&lt;/p>
&lt;p>2 steps here:&lt;/p>
&lt;ul>
&lt;li>reduced the number of retained versions to 2 (default is 3)&lt;/li>
&lt;li>cleanup all disabled snaps&lt;/li>
&lt;/ul>
&lt;p>I followed instructions on &lt;a href="https://www.debugpoint.com/clean-up-snap/">this article&lt;/a>, with the addition of using this command to check for active services:&lt;/p>
&lt;pre tabindex="0">&lt;code>$ snap info &amp;#34;$snapname&amp;#34; | grep -w active
&lt;/code>&lt;/pre></description></item><item><title>Golang string comparison benchmark</title><link>https://endorama.dev/2022/golang-string-comparison-benchmark/</link><pubDate>Thu, 10 Nov 2022 22:48:03 +0100</pubDate><guid>https://endorama.dev/2022/golang-string-comparison-benchmark/</guid><description>&lt;p>I recently read &lt;a href="https://go101.org/blog/2022-10-01-three-way-string-comparison.html">No Safe Efficient Ways to Do Three-way String Comparisons in Go - Go 101 Blog&lt;/a> about performing string comparisons and some performances issue that the &lt;code>strings.Compare&lt;/code> function has.&lt;/p>
&lt;p>The main statement of the post is&lt;/p>
&lt;blockquote>
&lt;p>Benchmark code constantly shows strings.Compare uses 2x CPU time of bytes.Compare when comparing unequal same-length byte sequences (we view both strings and byte slices as byte sequences here).&lt;/p>
&lt;/blockquote>
&lt;p>It got me curious so I jumped to the editor to check what was happening. I was not interested in debunking the claim, I was interested in understanding how different string comparison implementation performances differed.&lt;/p>
&lt;p>In the linked post you can find the source code for &lt;code>strings.Compare&lt;/code> and &lt;code>bytes.Compare&lt;/code>.&lt;/p>
&lt;p>My initial assumptions:&lt;/p>
&lt;ul>
&lt;li>&lt;code>strings.Compare&lt;/code> is 2x slower than &lt;code>bytes.Compare&lt;/code> as stated in the article&lt;/li>
&lt;li>built-ins are faster than &lt;code>strings.Compare&lt;/code> as implied by Golang documentation, that states: &amp;ldquo;It is usually clearer and always faster to use the built-in string comparison operators ==, &amp;lt;, &amp;gt;, and so on.&amp;rdquo;&lt;/li>
&lt;li>converting string to bytes and using &lt;code>bytes.Compare&lt;/code> may be beneficial (especially for longer strings)&lt;/li>
&lt;/ul>
&lt;p>To test my assumptions I implemented 4 test cases (and corresponding &lt;code>_long&lt;/code> versions):&lt;/p>
&lt;ol>
&lt;li>&lt;code>BuiltIn&lt;/code> test; string comparison using built-in operators&lt;/li>
&lt;li>&lt;code>Strings&lt;/code> test; string comparison using &lt;code>strings.Compare&lt;/code>&lt;/li>
&lt;li>&lt;code>Bytes&lt;/code> test; bytes comparison using &lt;code>bytes.Compare&lt;/code> with string to bytes conversion just for the comparison&lt;/li>
&lt;li>&lt;code>BytesWithBytes&lt;/code> test; bytes comparison using &lt;code>bytes.Compare&lt;/code> with byte sequences (as control)&lt;/li>
&lt;/ol>
&lt;p>You can find The Code™ in this repository: &lt;a href="https://github.com/endorama/go-strcmp-bench">endorama/go-strcmp-bench&lt;/a>.&lt;/p>
&lt;h2 id="the-setup">The setup&lt;/h2>
&lt;ul>
&lt;li>Golang 1.19.2&lt;/li>
&lt;li>Benchmark run with &lt;code>go test -bench=. -v --benchmem -count=20&lt;/code> and shown with &lt;code>benchstat&lt;/code>&lt;/li>
&lt;li>2 short strings (6 chars) and 2 long strings (255 chars) and their bytes conversion. There is an interesting worst case scenario in &lt;code>strings.Compare&lt;/code>: its performances are at worst when comparing 2 unequal strings of equal length. For this reason the strings are &lt;strong>equal length&lt;/strong> and &lt;strong>differ for the last char&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;h2 id="benchmark-results-with-short-strings">Benchmark results with short strings&lt;/h2>
&lt;pre tabindex="0">&lt;code>name time/op
BuiltIn-12 15.3ns ± 3%
Bytes-12 42.3ns ± 3%
BytesWithBytes-12 9.23ns ± 2%
Strings-12 15.6ns ± 1%
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>As expected &lt;code>strings.Compare&lt;/code> is ~2x slower than &lt;code>bytes.Compare&lt;/code>.&lt;/li>
&lt;li>Unexpectedly, using built-in is not that much faster?&lt;/li>
&lt;li>More unexpectedly, converting to bytes is massively less performing, ~3x less than comparing strings and ~4x less than comparing bytes.&lt;/li>
&lt;/ul>
&lt;h2 id="benchmark-results-with-long-strings">Benchmark results with long strings&lt;/h2>
&lt;pre tabindex="0">&lt;code>name time/op
BuiltIn_long-12 32.1ns ± 5%
Bytes_long-12 366ns ± 6%
BytesWithBytes_long-12 20.7ns ± 3%
Strings_long-12 37.4ns ± 3%
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>As expected, &lt;code>strings.Compare&lt;/code> is still 2x slower than &lt;code>bytes.Compare&lt;/code>. ✔&lt;/li>
&lt;li>Built-in is &lt;strong>slightly better&lt;/strong>. 🤔&lt;/li>
&lt;li>Don&amp;rsquo;t look at bytes conversion. 🙈&lt;/li>
&lt;/ul>
&lt;h2 id="conclusions--tldr">Conclusions / TL;DR&lt;/h2>
&lt;pre tabindex="0">&lt;code>name time/op (short) time/op (long) diff to bytes diff to strings
BuiltIn 15.3ns ± 3% 32.1ns ± 5% ~2x to 1.5x 1x
Bytes 42.3ns ± 3% 366ns ± 6% ~4x to ~18x ~3x to ~11x
BytesWithBytes 9.23ns ± 2% 20.7ns ± 3% 1x ~0.6x
Strings 15.6ns ± 1% 37.4ns ± 3% ~1.5x to ~2x ~1x
&lt;/code>&lt;/pre>&lt;ul>
&lt;li>built-in is not (that) faster 🤷&lt;/li>
&lt;li>use &lt;code>strings.Compare&lt;/code> if you think is more readable and you have short strings or you&amp;rsquo;re not performing a lot of comparisons&lt;/li>
&lt;li>&lt;strong>don&amp;rsquo;t&lt;/strong> convert a &lt;code>string&lt;/code> to &lt;code>bytes&lt;/code>, as it nukes performances 🪃
&lt;ul>
&lt;li>is &lt;a href="https://twitter.com/go100and1/status/1583715982904029185">possible to make this faster&lt;/a> using unsafe pointers, but 🐉 here be dragons 🐉&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>I haven&amp;rsquo;t delve into a deep comparison for &lt;code>built-ins&lt;/code> and &lt;code>strings.Compare&lt;/code> for this time.&lt;/p></description></item><item><title>Stop observing, start making</title><link>https://endorama.dev/2022/stop-observing-start-making/</link><pubDate>Thu, 03 Nov 2022 18:21:16 +0100</pubDate><guid>https://endorama.dev/2022/stop-observing-start-making/</guid><description>&lt;p>Observability has become the read-it-everywhere buzzword of software development. Like others before it.&lt;/p>
&lt;p>Observing any piece of technology is incredibly easy.&lt;br>
Look at your screen, intensely.&lt;br>
Do it some more.&lt;br>
You are observing technology. If it&amp;rsquo;s on, you are observing software too.&lt;/p>
&lt;p>So what now? You basically know nothing more about your system now that you did before observing it (except for, is it on 🔥?).&lt;/p>
&lt;p>The reason why &lt;em>observing&lt;/em> a piece of software is just a useless exercise is because observability is not an action.
There is no way you can &lt;strong>observe&lt;/strong> a system, unless the system itself is &lt;strong>observable&lt;/strong>.&lt;/p>
&lt;blockquote>
&lt;p>The term “observability” was coined by engineer Rudolf E. Kálmán in 1960.&lt;br>
In his 1960 paper, Kálmán introduced a characterization he called observability to describe mathematical control systems. In control theory, observability is defined as a measure of how well internal states of a system can be inferred from knowledge of its external outputs.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;em>The Mathematical Definition of Observability from &lt;a href="https://www.oreilly.com/library/view/observability-engineering/9781492076438/">Observability Engineering by Charity Majors, Liz Fong-Jones, George Miranda&lt;/a>&lt;/em>&lt;/p>
&lt;p>Observability is not the act of observing a system, is building its observable properties so you can &lt;strong>extract&lt;/strong> meaningful &lt;strong>information&lt;/strong> when observing it.
Gathering the data is the &lt;strong>first step&lt;/strong> in &amp;ldquo;observability&amp;rdquo;, but you &lt;strong>have to explore them&lt;/strong> to conclude they are of any value.&lt;/p>
&lt;p>So stop observing and start making your systems observable.&lt;/p>
&lt;p>If this reasoning did not convince you yet, happy to provide more clues supporting this conclusion.&lt;/p>
&lt;p>&lt;strong>Charity Majors in &lt;a href="https://charity.wtf/2020/03/03/observability-is-a-many-splendored-thing/">Observability is a Many-Splendored Definition&lt;/a>&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Plenty of tools are terrific at helping you ask the questions you could predict wanting to ask in advance. […] But if you &lt;em>can’t&lt;/em> predict all the questions you’ll need to ask in advance, or if you &lt;em>don’t&lt;/em> know what you’re looking for, then you’re in o11y territory.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Jean Yang in &lt;a href="https://www.akitasoftware.com/blog-posts/observability-isnt-about-logs-metrics-and-traces">Observability Isn&amp;rsquo;t About Logs, Metrics, and Traces&lt;/a>&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>To generalize, observability is about helping people build models of their systems so they can: Understand your system better to move faster. Get visibility into how your system is actually running. Keep an eye on how your system changes.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Dotan Horovits in &lt;a href="https://insidebigdata.com/2022/04/07/observability-is-a-data-analytics-problem/">Observability Is A Data Analytics Problem&lt;/a>&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>However logs, metrics and traces are, by themselves, not Observability. In fact, many organizations collect logs, metrics and traces, and still end up with poor observability. […] But more importantly, we need to remember that these signals are, after all, the raw data. And we’re looking for the insights and the root cause analysis, to understand our system.&lt;/p>
&lt;/blockquote>
&lt;p>&lt;strong>Multiple authors @ Google in &lt;a href="https://sre.google/workbook/reaching-beyond/">Site Reliability Workbook&lt;/a>&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Your Users, Not Your Monitoring, Decide Your Reliability&lt;br>
[…] Your monitoring, logs, and alerting are valuable only insofar as they help you notice problems before your customers do.&lt;/p>
&lt;/blockquote>
&lt;p>It&amp;rsquo;s a long journey from here, but rewarding.&lt;/p></description></item><item><title>Recovery metadata from pdf</title><link>https://endorama.dev/2022/recovery-metadata-from-pdf/</link><pubDate>Thu, 20 Oct 2022 19:14:07 +0200</pubDate><guid>https://endorama.dev/2022/recovery-metadata-from-pdf/</guid><description>&lt;p>Recently a friend of mine asked my opinion on &lt;a href="https://muun.com/">Muun&lt;/a>, an open source Bitcoin and Lighting client.&lt;/p>
&lt;p>I&amp;rsquo;m not particularly involved with cryptocurrencies due to their environmental impact but I think the technology and the evolution around its space are interesting to study.
The idea of distributed consensus may not yet have a worldwide practical application but is nevertheless an interesting achievement.&lt;/p>
&lt;p>What made it interesting is the self-custodial aspect. I got interested when I read about the Emergency Kit, which is probably still a burden for most users but it&amp;rsquo;s a great step forward in usability.&lt;/p>
&lt;p>While looking at the &lt;a href="https://github.com/muun">Muun source code&lt;/a> I discovered they have a Go tool called &lt;a href="https://github.com/muun/recovery">&lt;code>recovery&lt;/code>&lt;/a> (that allows to transfer funds out of your account without independently of the platform itself).&lt;/p>
&lt;p>I started reading the code after looking at the initial GIF displaying the tool loading the Emergency Kit data from PDF 🧐&lt;/p>
&lt;p>In the code I found an interesting function that extracts a JSON file from the PDF attachments and uses it to load emergency kit data.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// ReadMetadata returns the deserialized metadata file embedded in the SrcFile PDF.
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">func&lt;/span> (&lt;span style="color:#a6e22e">mr&lt;/span> &lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">MetadataReader&lt;/span>) &lt;span style="color:#a6e22e">ReadMetadata&lt;/span>() (&lt;span style="color:#f92672">*&lt;/span>&lt;span style="color:#a6e22e">Metadata&lt;/span>, &lt;span style="color:#66d9ef">error&lt;/span>) {
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://github.com/muun/recovery/blob/ed2c88afacfba9e14a7a528cf0494a70d75b7245/main.go#L67">Check out the code&lt;/a>&lt;/p>
&lt;p>That&amp;rsquo;s a really cool idea to provide a great human + machine experience for a sensitive operation like account recovery.&lt;/p></description></item><item><title>Making your shell scripts bulletproof</title><link>https://endorama.dev/2022/making-your-shell-scripts-bulletproof/</link><pubDate>Mon, 19 Sep 2022 21:13:23 +0200</pubDate><guid>https://endorama.dev/2022/making-your-shell-scripts-bulletproof/</guid><description>&lt;p>Writing shell scripts in BASH is something I started doing around 2008 probably, but it&amp;rsquo;s only after some years I started doing it in a way that was preventing me from shooting myself in the foot.&lt;/p>
&lt;p>BASH is great and powerful, allows to harness the power of UNIX tools, but its syntax it&amp;rsquo;s unforgiving and not easy to master (and at times even after more than 10 years, confusing).&lt;/p>
&lt;p>In this post I&amp;rsquo;ll go through some of my mandatory patterns to add robustness to my shell scripts.&lt;/p>
&lt;h2 id="tldr">TL;DR:&lt;/h2>
&lt;p>The full template for these suggestions would be:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">#!/usr/bin/env bash
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>set -euo pipefail
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="using-the-interpreter-from-the-environment">Using the interpreter from the environment&lt;/h2>
&lt;p>Use &lt;code>env&lt;/code> for the shebang:&lt;/p>
&lt;pre tabindex="0">&lt;code>#!/usr/bin/env bash
&lt;/code>&lt;/pre>&lt;p>There are occasions where this may not be advisable (especially if you use the shebang for other languages), but for BASH &lt;em>generally&lt;/em> is better to use &lt;code>/usr/bin/env&lt;/code> for the shebang. See &lt;a href="https://unix.stackexchange.com/questions/29608/why-is-it-better-to-use-usr-bin-env-name-instead-of-path-to-name-as-my">this StackOverflow answer&lt;/a> for a broader discussion.&lt;/p>
&lt;p>Using &lt;code>/usr/bin/env&lt;/code> tells the OS to search for the specified executable in the user &lt;code>PATH&lt;/code> instead on a fixed location, this makes it more portable. &lt;code>env&lt;/code> is usually available at &lt;code>/usr/bin/env&lt;/code>, you can double check that with &lt;code>command -v env&lt;/code>.&lt;/p>
&lt;h2 id="catch-unset-variables">Catch unset variables&lt;/h2>
&lt;blockquote>
&lt;p>Treat unset variables and parameters other than the special parameters &amp;ldquo;@&amp;rdquo; and &amp;ldquo;*&amp;rdquo; as an error when performing parameter expansion.&lt;/p>
&lt;/blockquote>
&lt;p>From &lt;a href="https://man7.org/linux/man-pages/man1/bash.1.html">BASH manual&lt;/a>, under &lt;code>SHELL BUILTIN COMMANDS &amp;gt; set&lt;/code>.&lt;/p>
&lt;p>One of the most common issues is running a BASH script with an undefined variable. BASH does not complain &lt;strong>at all&lt;/strong> (and that&amp;rsquo;s how I nuked 6.5TB of hard drives with a missing variable - but that&amp;rsquo;s for another story) and execute your script as if an empty string was your value.&lt;/p>
&lt;p>Why wouldn&amp;rsquo;t you want to do that? Let&amp;rsquo;s look at our example script:&lt;/p>
&lt;pre tabindex="0">&lt;code>#!/usr/bin/env bash
folder=$1
rm -fr /home/$USER/$folder
&lt;/code>&lt;/pre>&lt;p>This simple script is supposed to delete user provided &lt;code>$folder&lt;/code> in user home directory. What do you think will happen if &lt;code>$1&lt;/code> (shell script first argument) is not provided? 😱 It will delete all your home folder. YAY.
This is bad because &lt;code>rm&lt;/code> is reasonably fast and unlinks the files, which means the OS may decide to write on the just created free space almost immediately; before you realize and hit CTRL+C (&lt;strong>don&amp;rsquo;t do that&lt;/strong>, read &lt;a href="https://unix.stackexchange.com/a/101247/111295">here&lt;/a>) a huge portion will have been unlinked already. Recovering them is painful.&lt;/p>
&lt;p>To prevent this you can use:&lt;/p>
&lt;pre tabindex="0">&lt;code>set -u
&lt;/code>&lt;/pre>&lt;p>or its longest form:&lt;/p>
&lt;pre tabindex="0">&lt;code>set -o nounset
&lt;/code>&lt;/pre>&lt;p>What to do with variables that you want to be empty, for example because you want to check for value? &lt;code>nounset&lt;/code> refers to &lt;strong>unset&lt;/strong> variables, which means any other value (even an empty one) satisfies this requirement.&lt;/p>
&lt;p>My usual case is checking for environment variable, which can be done easily with:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">if&lt;/span> &lt;span style="color:#f92672">[&lt;/span> -z &lt;span style="color:#e6db74">&amp;#34;&lt;/span>&lt;span style="color:#e6db74">${&lt;/span>ENVVAR&lt;span style="color:#66d9ef">:-&lt;/span>&lt;span style="color:#e6db74">}&lt;/span>&lt;span style="color:#e6db74">&amp;#34;&lt;/span> &lt;span style="color:#f92672">]&lt;/span>; &lt;span style="color:#66d9ef">then&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># here ENVVAR has non-zero length&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">else&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#75715e"># here ENVVAR has zero length but does not trigger nounset option&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#66d9ef">fi&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>(&lt;code>-z&lt;/code> is part of &lt;code>test&lt;/code> utility and checks if the supplied argument is a zero-length string)&lt;/p>
&lt;h2 id="exit-on-error">Exit on error&lt;/h2>
&lt;blockquote>
&lt;p>Exit immediately if a pipeline (which may consist of a single simple command), a list, or a compound command […], exits with a non-zero status.&lt;/p>
&lt;/blockquote>
&lt;p>From &lt;a href="https://man7.org/linux/man-pages/man1/bash.1.html">BASH manual&lt;/a>, under &lt;code>SHELL BUILTIN COMMANDS &amp;gt; set&lt;/code>.&lt;/p>
&lt;p>When an error occurs in a BASH script, the script does not exit immediately but continue running; this can create weird situations (think about changing to a different folder, or running a setup command).&lt;/p>
&lt;p>To prevent this use:&lt;/p>
&lt;pre tabindex="0">&lt;code>set -e
&lt;/code>&lt;/pre>&lt;p>or its longest form:&lt;/p>
&lt;pre tabindex="0">&lt;code>set -o errexit
&lt;/code>&lt;/pre>&lt;h2 id="use-the-rightmost-exit-value-for-a-pipeline">Use the rightmost exit value for a pipeline&lt;/h2>
&lt;blockquote>
&lt;p>If set, the return value of a pipeline is the value of the last (rightmost) command to exit with a non-zero status, or zero if all commands in the pipeline exit successfully.&lt;/p>
&lt;/blockquote>
&lt;p>From &lt;a href="https://man7.org/linux/man-pages/man1/bash.1.html">BASH manual&lt;/a>, under &lt;code>SHELL BUILTIN COMMANDS &amp;gt; set&lt;/code>.&lt;/p>
&lt;p>When running a command pipeline (i.e &lt;code>cat file | grep foo&lt;/code>) the exit status of the pipeline depends from the first command run. In this cat/grep case, if the file exist but does not contain &lt;code>foo&lt;/code>, &lt;code>cat&lt;/code> will exit with &lt;code>0&lt;/code> but &lt;code>grep&lt;/code> will exit with a non-zero exit code. Nevertheless the exit value for the entire expression would be &lt;code>0&lt;/code>.&lt;/p>
&lt;p>To prevent this use:&lt;/p>
&lt;pre tabindex="0">&lt;code>set -o pipefail
&lt;/code>&lt;/pre>&lt;p>Combining &lt;code>-e&lt;/code> and &lt;code>-o pipefail&lt;/code> makes impossible to have &amp;ldquo;failing&amp;rdquo; commands (not part of conditions), like in the case above, because &lt;code>-o pipefail&lt;/code> will make the pipeline exit with a non-zero code and &lt;code>-e&lt;/code> will immediately halt the script. To prevent this, when you want to continue even in case of error you can end the pipeline with &lt;code>|| true&lt;/code>; this instruction will run &lt;code>true&lt;/code>, a utility that &amp;ldquo;do nothing, successfully&amp;rdquo; and change the rightmost exit code to &lt;code>0&lt;/code>.&lt;/p>
&lt;p>Es:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-bash" data-lang="bash">&lt;span style="display:flex;">&lt;span>set -eo pipefail
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>cat file | grep foo &lt;span style="color:#f92672">||&lt;/span> true
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>echo &lt;span style="color:#e6db74">&amp;#34;continue&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Happy coding 😉&lt;/p></description></item><item><title>2ami</title><link>https://endorama.dev/2022/2ami/</link><pubDate>Sat, 17 Sep 2022 00:32:25 +0200</pubDate><guid>https://endorama.dev/2022/2ami/</guid><description>&lt;p>Some years ago I was learning Golang and researching a 2FA tool to use in the command line (that I wanted to integrate with ULauncher, a Linux alternative to Alfred). The only tool I found was saving all secrets in a plain text file and I really wanted to avoid that.&lt;/p>
&lt;p>So 2ami was born! 2ami is a small CLI tool that allows to generate 2FA tokens. Is a single binary you can run and uses your OS secure storage to store secrets. It&amp;rsquo;s simple but complete and being my companion for years now.&lt;/p>
&lt;p>I recently released 0.6.0, the first cross platform (Linux, macOS, Windows) build after fighting a lot with the CI (it needs CGO) and thanks to GitHub Actions (which I keep admiring for how powerful and effective they are).&lt;/p>
&lt;p>If you want to checkit out ➡ &lt;a href="https://lnkd.in/dWY-Y_6e">https://lnkd.in/dWY-Y_6e&lt;/a>&lt;/p></description></item><item><title>Golang divisions</title><link>https://endorama.dev/2022/golang-divisions/</link><pubDate>Thu, 15 Sep 2022 12:31:05 +0200</pubDate><guid>https://endorama.dev/2022/golang-divisions/</guid><description>&lt;p>At work today I read a piece of code in Golang that got me curious:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">maxQuerySize&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">500&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// metricDataQueries is user provided
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">sliceLength&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> len(&lt;span style="color:#a6e22e">metricDataQueries&lt;/span>)&lt;span style="color:#f92672">/&lt;/span>&lt;span style="color:#a6e22e">maxQuerySize&lt;/span> &lt;span style="color:#f92672">+&lt;/span> &lt;span style="color:#ae81ff">1&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">// what&amp;#39;s the value of sliceLength?
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#66d9ef">for&lt;/span> &lt;span style="color:#a6e22e">i&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">0&lt;/span>; &lt;span style="color:#a6e22e">i&lt;/span> &amp;lt; &lt;span style="color:#a6e22e">sliceLength&lt;/span>; &lt;span style="color:#a6e22e">i&lt;/span>&lt;span style="color:#f92672">++&lt;/span> {
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#a6e22e">whatever&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a simple integer division, but the result is then used as a parameter to the for loop.
Integer division has a drawback, that can create a bug in this case: the remainder, as &lt;code>float&lt;/code> in for loop declarations are supported only if no truncation happens when converting to &lt;code>int&lt;/code> (i.e. &lt;code>10.0&lt;/code> works, &lt;code>10.1&lt;/code> don&amp;rsquo;t).&lt;/p>
&lt;p>How does Go handle this? The answer is as simple as it gets: remainder is ignore and the result is the quotient. The reason why this happens is explained in Go specs, under &lt;a href="https://go.dev/ref/spec#Constant_expressions">Constant expressions&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>Any other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant. If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand&amp;rsquo;s kind that appears later in this list: integer, rune, floating-point, complex.&lt;/p>
&lt;/blockquote>
&lt;p>Which means that to have &amp;ldquo;true&amp;rdquo; division you need to convert &lt;em>at least one argument&lt;/em> to &lt;code>float&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;">&lt;code class="language-go" data-lang="go">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">a&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">10.0&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">b&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">c&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">10.0&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2.0&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">d&lt;/span> &lt;span style="color:#f92672">:=&lt;/span> &lt;span style="color:#ae81ff">10&lt;/span> &lt;span style="color:#f92672">/&lt;/span> &lt;span style="color:#ae81ff">2&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">reflect&lt;/span>.&lt;span style="color:#a6e22e">TypeOf&lt;/span>(&lt;span style="color:#a6e22e">a&lt;/span>)) &lt;span style="color:#75715e">// float
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">reflect&lt;/span>.&lt;span style="color:#a6e22e">TypeOf&lt;/span>(&lt;span style="color:#a6e22e">b&lt;/span>)) &lt;span style="color:#75715e">// float
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">reflect&lt;/span>.&lt;span style="color:#a6e22e">TypeOf&lt;/span>(&lt;span style="color:#a6e22e">c&lt;/span>)) &lt;span style="color:#75715e">// float
&lt;/span>&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#75715e">&lt;/span>&lt;span style="color:#a6e22e">fmt&lt;/span>.&lt;span style="color:#a6e22e">Println&lt;/span>(&lt;span style="color:#a6e22e">reflect&lt;/span>.&lt;span style="color:#a6e22e">TypeOf&lt;/span>(&lt;span style="color:#a6e22e">d&lt;/span>)) &lt;span style="color:#75715e">// int
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;a href="https://go.dev/play/p/UfjOH9rGIAi">go.dev/play&lt;/a>&lt;/p>
&lt;p>Doing integer division effectively &amp;ldquo;floors&amp;rdquo; the result of the operation. What if we want the ceil of it?&lt;/p>
&lt;p>Go &lt;code>math&lt;/code> package has &lt;a href="https://pkg.go.dev/math#Ceil">&lt;code>Ceil(x float64) float64&lt;/code>&lt;/a> for this, which implies doing a &amp;ldquo;true&amp;rdquo; division and applying &lt;code>Ceil&lt;/code> on the result.&lt;/p>
&lt;p>All of this was not really surprising, but it&amp;rsquo;s cool to see this being implemented in a very ergonomic way.&lt;/p>
&lt;p>Just a note: if you want to get a float value you must convert to &lt;code>float&lt;/code> all division arguments &lt;strong>before&lt;/strong> the division.&lt;/p>
&lt;p>Do this: &lt;code>float64(2) / float64(10) // 0.2&lt;/code> and &lt;strong>not&lt;/strong> this: &lt;code>a := float64(2 / 10) // 0&lt;/code>.&lt;/p></description></item><item><title>asdf, little awesome tool</title><link>https://endorama.dev/2022/asdf-little-awesome-tool/</link><pubDate>Wed, 17 Aug 2022 23:46:30 +0200</pubDate><guid>https://endorama.dev/2022/asdf-little-awesome-tool/</guid><description>&lt;p>Some years ago I discovered a little tool with a huge potential. I started using it and since then it proved useful in so many ways it&amp;rsquo;s now a mandatory tool in my toolset.&lt;/p>
&lt;p>This tool is &lt;a href="https://asdf-vm.com/">&lt;code>asdf&lt;/code>&lt;/a>. &lt;code>asdf&lt;/code> is a CLI tool version manager. You may already be using multiple ones (&lt;code>rbenv&lt;/code>, &lt;code>nvm&lt;/code>, &lt;code>goenv&lt;/code> to name a few) to manage versions of programming language tooling and accessories.&lt;/p>
&lt;p>Having multiple tools for this purposes creates a series of disadvantages:&lt;/p>
&lt;ul>
&lt;li>setting up your workstation is more painful (multiple tools to install, multiple versions to manage); this become more complex when you work in a team and want to provide a great onboarding experience&lt;/li>
&lt;li>adopting a new tool force you to first search for a dedicated version manager, then install the tool (you may go without one, but that&amp;rsquo;s going to bite your future self)&lt;/li>
&lt;li>how to share the same version in all environments, in particular dev and CI? Was that file &lt;code>.go-version&lt;/code>, &lt;code>.golang-version&lt;/code>? Each tool uses it&amp;rsquo;s own convention, sometimes they align but having a consistent experience is not in their DNA;&lt;/li>
&lt;li>what&amp;rsquo;s the command again? Each tool may have different CLI commands, making switching between them a pain; this may happen more easily if you&amp;rsquo;re in a team;&lt;/li>
&lt;li>remember the &lt;a href="https://www.reddit.com/r/programming/comments/p1j1c/tabs_vs_spaces_vs_both/">Good Old Times™ of “spaces VS tabs”&lt;/a>? Well, why start a 🔥 war around which tool to use?&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s summarise why &lt;code>asdf&lt;/code> is ground breaking:&lt;/p>
&lt;ul>
&lt;li>helps developer onboarding across a wide set of tool;&lt;/li>
&lt;li>helps team collaborating;&lt;/li>
&lt;li>removes useless discussion drive by preference;&lt;/li>
&lt;li>has a consistent UI.&lt;/li>
&lt;/ul>
&lt;p>Would that be enough? NO 😜&lt;/p>
&lt;p>It delivers more:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://asdf-vm.com/plugins/create.html">creating plugins&lt;/a> is &lt;strong>so easy&lt;/strong> you can just add whatever you want (I did it &lt;a href="https://github.com/endorama/asdf-desk">here&lt;/a>, &lt;a href="https://github.com/endorama/asdf-goreleaser">here&lt;/a>, &lt;a href="https://github.com/endorama/asdf-envchain">here&lt;/a> and &lt;a href="https://github.com/endorama/asdf-envchain">here&lt;/a>);&lt;/li>
&lt;li>creating plugins is most probably not needed because what you need is &lt;strong>already available&lt;/strong>; &lt;a href="https://github.com/asdf-community">&lt;code>asdf-community&lt;/code>&lt;/a> has 60 repositories (so around 60 plugins), just a &lt;code>asdf plugin-add&lt;/code> away;&lt;/li>
&lt;li>are we forgetting the CI? Hell no! Note that this only works if you are using GitHub Actions, but if that&amp;rsquo;s the case there are &lt;a href="https://github.com/asdf-vm/actions">actions ready to be used&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p class="tip">In case you don&amp;rsquo;t want to use the install action provided by the asdf community because it does not leverages other optimized setup actions, I developed an action that parses the &lt;code>.tool-versions&lt;/code> file extracting each tool version so they can then be reused in other actions. No &lt;code>asdf&lt;/code> needed!&lt;br>
This action is available &lt;a href="https://github.com/endorama/asdf-parse-tool-versions">here&lt;/a>.&lt;/p>
&lt;p>If you reached this line, &lt;strong>STOP&lt;/strong> right here and &lt;a href="https://asdf-vm.com/guide/getting-started.html">go install&lt;/a> &lt;code>asdf&lt;/code>.&lt;/p></description></item><item><title>Contact me</title><link>https://endorama.dev/contact-me/</link><pubDate>Wed, 17 Aug 2022 00:37:52 +0200</pubDate><guid>https://endorama.dev/contact-me/</guid><description>&lt;p>Before contacting me, please take note of the following:&lt;/p>
&lt;!-- raw HTML omitted -->
&lt;ul>
&lt;li>please be polite, I’ll always be polite to you;&lt;/li>
&lt;li>if you&amp;rsquo;re a recruiter I would prefer if you get in touch through &lt;a href="https://www.linkedin.com/in/edoardotenani/">LinkedIn&lt;/a>, where you can also see a more detailed profile.&lt;/li>
&lt;/ul>
&lt;h1 id="email-its2023endoramadev">Email: &lt;a href="mailto:its2023@endorama.dev">its2023@endorama.dev&lt;/a>&lt;/h1>
&lt;p>Email is the easiest way to get in touch with me. Expect a reply in a day or two.&lt;/p></description></item><item><title>About me</title><link>https://endorama.dev/about/</link><pubDate>Mon, 08 Aug 2022 19:37:52 +0200</pubDate><guid>https://endorama.dev/about/</guid><description>&lt;p>👋 Nice to meet you, I&amp;rsquo;m Edoardo!&lt;/p>
&lt;h1 id="disclaimer">Disclaimer&lt;/h1>
&lt;p>This is my personal website. The opinions expressed here are my own and not those of my employer.&lt;/p>
&lt;p>In addition, my opinion change in time as I meet new ideas, opinions and facts; I consider this healthy and necessary, the consequence of an open mind.&lt;/p>
&lt;p>This blog acts as a semi-permanent, point in time snapshot of my thoughs and opinions. As such, any thoughts and opinions expressed within my previous posts may not be the same, or even similar, to those I may hold today.&lt;/p>
&lt;h1 id="anyway">Anyway&amp;hellip;&lt;/h1>
&lt;p>I&amp;rsquo;m currently based in Italy.&lt;/p>
&lt;p>I started my professional life around 2010, I&amp;rsquo;m a self-taught software developer and sysadmin, Open source advocate and contributor, obsessed with creating value for others.&lt;/p>
&lt;p>See what I&amp;rsquo;m up to &lt;a href="https://endorama.dev/now/">now&lt;/a>.&lt;/p>
&lt;p>In my professional life, I&amp;rsquo;ve worn many hats:&lt;/p>
&lt;ul>
&lt;li>DevOps engineer at &lt;a href="https://www.arduino.cc/">Arduino&lt;/a>, the Open-Source electronic prototyping platform;&lt;/li>
&lt;li>Front-end, Back-end and Cloud developer at &lt;a href="https://www.vitsoe.com/">Vitsoe&lt;/a>, a British furniture manufacturing company, realising furniture designs of Dieter Rams;&lt;/li>
&lt;li>Front-end, Back-end and maker at &lt;a href="https://todo.to.it/">TODO&lt;/a>, an Italian interaction design studio;&lt;/li>
&lt;li>Front-end, Backend and SysAdmin at &lt;a href="https://dev.welaika.com/">weLaika&lt;/a>, an Italian web agency.&lt;/li>
&lt;/ul>
&lt;p>I started learning programming through online resources and volunteering in an association were we developed websites and did SysAdmin work. I got my first job as beta tester and SysAdmin in 2010 at &lt;a href="https://dev.welaika.com/">weLaika&lt;/a>. During all the years I learn a bunch of web frameworks, multiple programming languages and an infinite amount of tools, and I keep learning.&lt;/p>
&lt;p>I&amp;rsquo;m a Linux (mainly Ubuntu) user since ~2010 and currently using elementary OS as a daily driver.&lt;/p>
&lt;p>I&amp;rsquo;m married, enjoy hiking and volunteering. I&amp;rsquo;m passionate about the Open Source, internet culture and knowledge management systems. I absolutely love crossovers, where cultures, styles, whatever mix to create something new - original + beautiful == 👨‍🍳👌.&lt;/p></description></item><item><title>Hello World</title><link>https://endorama.dev/2022/hello-world/</link><pubDate>Mon, 08 Aug 2022 19:25:09 +0200</pubDate><guid>https://endorama.dev/2022/hello-world/</guid><description>&lt;p>All stories should start from somewhere! This is where the story for my website starts.&lt;/p></description></item><item><title/><link>https://endorama.dev/index-about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://endorama.dev/index-about/</guid><description>&lt;p>👋 Edoardo is a software engineer and maker from Italy. Self taught and curious, with more than 10 years of experience in the field. Come say Hi!&lt;/p></description></item></channel></rss>