<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Kartones Blog</title><link href="https://blog.kartones.net/" rel="alternate"></link><link href="https://blog.kartones.net/syndication.atom.xml" rel="self"></link><id>https://blog.kartones.net/</id><updated>2024-01-28T14:45:00+01:00</updated><subtitle>Be the change you want to see in this world</subtitle><entry><title>My macOS Cheatsheet</title><link href="https://blog.kartones.net/post/my-macos-cheatsheet/" rel="alternate"></link><published>2024-01-28T14:45:00+01:00</published><updated>2024-01-28T14:45:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2024-01-28:/post/my-macos-cheatsheet/</id><summary type="html">&lt;p&gt;I learn better by creating cheatsheets and summaries, so adding &lt;a href="https://blog.kartones.net/archives.html"&gt;to my list&lt;/a&gt;, I recently took an old macos cheatsheet I had with a few shortcuts, and I've been improving and expanding it. I wrote it while at Eventbrite, when I had to switch from Linux to Mac, and now …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I learn better by creating cheatsheets and summaries, so adding &lt;a href="https://blog.kartones.net/archives.html"&gt;to my list&lt;/a&gt;, I recently took an old macos cheatsheet I had with a few shortcuts, and I've been improving and expanding it. I wrote it while at Eventbrite, when I had to switch from Linux to Mac, and now I'm facing a very similar scenario at Spotify; I need to build/compile iOS binaries from time to tim, so instead of having two work laptops, I made the switch.&lt;/p&gt;
&lt;p&gt;It lives at &lt;a href="https://blog.kartones.net/page/macos-cheatsheet/"&gt;https://blog.kartones.net/page/macos-cheatsheet/&lt;/a&gt;, and the content is a mix of three kinds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shortcuts: Both operating system and application-specific&lt;/li&gt;
&lt;li&gt;Tips/Fixes/Tweaks&lt;/li&gt;
&lt;li&gt;Tools: To complement &lt;a href="https://blog.kartones.net/page/command-line-tools/"&gt;my command-line tools list&lt;/a&gt;, GUI tools, with the restriction of being either free or open-source [1].&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I must admit that, at the time of writing this post, the list of tools is pretty scarce, so I'm more than open to more suggestions. Just take into account that I'm searching for applications to either improving macos-usage in general, or development-specific.&lt;/p&gt;
&lt;p&gt;[1] I won't pay for any software I'm only going to use at work, and I already have licenses for required tools like IntelliJ.&lt;/p&gt;</content><category term="post"></category><category term="Systems-IT"></category><category term="Tools"></category></entry><entry><title>Debugging Typescript errors in Bazel</title><link href="https://blog.kartones.net/post/debugging-typescript-errors-in-bazel/" rel="alternate"></link><published>2024-01-21T17:05:00+01:00</published><updated>2024-01-21T17:05:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2024-01-21:/post/debugging-typescript-errors-in-bazel/</id><summary type="html">&lt;p&gt;Typescript errors are not always easy to debug. Add Bazel to the mix, and some errors can feel a nightmare to understand and resolve. Thankfully, there are two steps that will help a lot with triaging.&lt;/p&gt;
&lt;p&gt;Let's imagine we have the following action, which transpiles some typescript and then runs …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Typescript errors are not always easy to debug. Add Bazel to the mix, and some errors can feel a nightmare to understand and resolve. Thankfully, there are two steps that will help a lot with triaging.&lt;/p&gt;
&lt;p&gt;Let's imagine we have the following action, which transpiles some typescript and then runs it via NodeJS (you can find it at &lt;a href="https://github.com/Kartones/bazel-web-template"&gt;my Github bazel-web-template repo&lt;/a&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;bazel&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;src/ts:run_d
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This action calls a &lt;code&gt;js_binary&lt;/code&gt;, to a transpiled &lt;code&gt;d.mjs&lt;/code&gt; file, which imports from &lt;code&gt;c.mjs&lt;/code&gt;. I have on purpose modified the import to wrongly ask for &lt;code&gt;e.mjs&lt;/code&gt;, so when we run it, inside the Bazel execution output, we'll get the following error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;src/ts/d.mts&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,28&lt;span class="o"&gt;)&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;TS2307:&lt;span class="w"&gt; &lt;/span&gt;Cannot&lt;span class="w"&gt; &lt;/span&gt;find&lt;span class="w"&gt; &lt;/span&gt;module&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;its&lt;span class="w"&gt; &lt;/span&gt;corresponding&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;declarations.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now let's keep imagining that we have no clue where could that error come from; We just know that something is trying to import &lt;code&gt;e.mjs&lt;/code&gt; and fails to do so.&lt;/p&gt;
&lt;p&gt;The first step to ease debugging, is to tell Bazel to keep the sandbox when the execution ends, so we can inspect and replicate what it ran. For that, we can use the &lt;a href="https://bazel.build/docs/sandboxing#debugging-build-failures"&gt;sandbox_debug flag&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;bazel&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;src/ts:run_d&lt;span class="w"&gt; &lt;/span&gt;--sandbox_debug
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, the output will be more verbose, and we will notice that, around where it outputs the error, there will be something referring to a failure/error executing a command, followed by a long execution line. In this example, calling a &lt;code&gt;tsc&lt;/code&gt; wrapper script.&lt;/p&gt;
&lt;p&gt;Note: I've simplified a bit the paths [1] and added some new lines for legibility.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;linux-sandbox&lt;span class="w"&gt; &lt;/span&gt;failed:&lt;span class="w"&gt; &lt;/span&gt;error&lt;span class="w"&gt; &lt;/span&gt;executing&lt;span class="w"&gt; &lt;/span&gt;TsProject&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/execroot/_main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;env&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BAZEL_BINDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bazel-out/k8-fastbuild/bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
/bazel/install/linux-sandbox&lt;span class="w"&gt; &lt;/span&gt;-W&lt;span class="w"&gt; &lt;/span&gt;/tmp/&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/dev/shm&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/tmp/
&lt;span class="w"&gt; &lt;/span&gt;bazel-execroot/_main&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-working-directory&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/external/aspect_rules_js&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-source-roots/0&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/external/nodejs_linux_amd64&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-source-roots/1&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/stats.out&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-N&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/debug.out&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;bazel-out/k8-opt-exec/bin/external/npm_typescript/tsc.sh&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--project&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--outDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--rootDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--declarationDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--tsBuildInfoFile&lt;span class="w"&gt; &lt;/span&gt;src/ts/d.tsbuildinfo&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we re-execute in the command line that big command, we can replay very similarly what Bazel did: In our case, apparently some transpilation taking place at &lt;code&gt;src/ts&lt;/code&gt;. But not only that, we can also alter it, by adding for example the &lt;a href="https://www.typescriptlang.org/tsconfig#traceResolution"&gt;traceResolution flag&lt;/a&gt; so that &lt;code&gt;tsc&lt;/code&gt; emits more details.&lt;/p&gt;
&lt;p&gt;Let's add it to the end, for example as &lt;code&gt;--traceResolution &amp;gt;&amp;gt; out.txt&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/execroot/_main&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;env&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BAZEL_BINDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;bazel-out/k8-fastbuild/bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;TMPDIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
/bazel/install/linux-sandbox&lt;span class="w"&gt; &lt;/span&gt;-W&lt;span class="w"&gt; &lt;/span&gt;/tmp/&lt;span class="w"&gt; &lt;/span&gt;-t&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/dev/shm&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;-w&lt;span class="w"&gt; &lt;/span&gt;/tmp/
&lt;span class="w"&gt; &lt;/span&gt;bazel-execroot/_main&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/execroot&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-working-directory&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/external/aspect_rules_js&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-source-roots/0&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/external/nodejs_linux_amd64&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp/bazel-source-roots/1&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/_hermetic_tmp&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;/tmp&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/stats.out&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;-N&lt;span class="w"&gt; &lt;/span&gt;-D&lt;span class="w"&gt; &lt;/span&gt;/bazel/sandbox/linux-sandbox/226/debug.out&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="w"&gt; &lt;/span&gt;bazel-out/k8-opt-exec/bin/external/npm_typescript/tsc.sh&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--project&lt;span class="w"&gt; &lt;/span&gt;tsconfig.json&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--outDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--rootDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--declarationDir&lt;span class="w"&gt; &lt;/span&gt;src/ts&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;--tsBuildInfoFile&lt;span class="w"&gt; &lt;/span&gt;src/ts/d.tsbuildinfo
&lt;span class="w"&gt; &lt;/span&gt;--traceResolution&lt;span class="w"&gt; &lt;/span&gt;&amp;gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;out.txt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Executing that command, we will get a very detailed &lt;code&gt;tsc&lt;/code&gt; module resolution trace. I Highly recommended to output it to a file, as even with simple examples it is quite large!&lt;/p&gt;
&lt;p&gt;Analysing the trace resolution, we search for &lt;code&gt;e.mjs&lt;/code&gt; and something-something resolution...&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;========&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Resolving&lt;span class="w"&gt; &lt;/span&gt;module&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/d.mts'&lt;/span&gt;.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;========&lt;/span&gt;
Explicitly&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;module&lt;span class="w"&gt; &lt;/span&gt;resolution&lt;span class="w"&gt; &lt;/span&gt;kind:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'NodeNext'&lt;/span&gt;.
Resolving&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ESM&lt;span class="w"&gt; &lt;/span&gt;mode&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;conditions&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'import'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'types'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'node'&lt;/span&gt;.
Loading&lt;span class="w"&gt; &lt;/span&gt;module&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;folder,&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;candidate&lt;span class="w"&gt; &lt;/span&gt;module&lt;span class="w"&gt; &lt;/span&gt;location&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.mjs'&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;types:&lt;span class="w"&gt; &lt;/span&gt;TypeScript,&lt;span class="w"&gt; &lt;/span&gt;JavaScript,&lt;span class="w"&gt; &lt;/span&gt;Declaration,&lt;span class="w"&gt; &lt;/span&gt;JSON.
File&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;extension
&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;stripping&lt;span class="w"&gt; &lt;/span&gt;it.
File&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.mts'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;exist.
File&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.d.mts'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;exist.
File&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;exist.
Directory&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'/tmp//bazel-out/k8-fastbuild/bin/src/ts/e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;exist,&lt;span class="w"&gt; &lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;skipping&lt;span class="w"&gt; &lt;/span&gt;all&lt;span class="w"&gt; &lt;/span&gt;lookups&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;it.
&lt;span class="o"&gt;========&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Module&lt;span class="w"&gt; &lt;/span&gt;name&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'./e.mjs'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;resolved.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;========&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And there it is, we see that the resolution comes from &lt;code&gt;from [...] src/ts/d.mts&lt;/code&gt;. We can also see all the variants that it tried (TS/JS + type definitions), and if this was a potential package dependency, we would see it also crawl up, searching for &lt;code&gt;node_modules&lt;/code&gt; folders and looking inside them if present.&lt;/p&gt;
&lt;p&gt;As a summary, by enabling the sandbox persistence, and then re-playing the executed command with some extra flags, we can debug way better most Typescript errors.&lt;/p&gt;
&lt;p&gt;Before wrapping up this post, I also want to mention some more generic debugging advices:&lt;/p&gt;
&lt;p&gt;Checking &lt;code&gt;bazel-out&lt;/code&gt; to see which files you have can be very helpful. Just remember to run a &lt;code&gt;bazel clean&lt;/code&gt; if want to be certain that all files you're seeing are from your last execution, and not from past ones. Web rules tend to leave a lot of leftovers.&lt;/p&gt;
&lt;p&gt;Many times, the source of the error is file visibility-related: You are transpiling the correct files, but then either you're not exposing them at the intended target (e.g. via &lt;code&gt;data&lt;/code&gt; or as a dependency), or maybe your &lt;code&gt;tsconfig.json&lt;/code&gt; is not properly set and as an example module resolution is not correctly configured, or you're missing some &lt;code&gt;rootDir&lt;/code&gt;/&lt;code&gt;rootDirs&lt;/code&gt;. About &lt;code&gt;rootDirs&lt;/code&gt;, they are not that problematic in Bazel &lt;em&gt;unless&lt;/em&gt; you use yarn/pnpm/etc. workspaces.&lt;/p&gt;
&lt;p&gt;And finally, your mileage and preferences might vary, but in Bazel I prefer a classic approach of transpiling everything to Javascript, and then running binaries and tests against JS-only file sets, versus using &lt;code&gt;ts-node&lt;/code&gt;, &lt;code&gt;babel&lt;/code&gt; and similar under-the-hood transformations. Being able to check at each step if you have the proper files eases debugging and simplifies configuration a lot (specially during migrations).&lt;/p&gt;
&lt;p&gt;[1] : Sandbox paths including the action tend to be quite long; A few times, long enough to cause issues under Windows.&lt;/p&gt;</content><category term="post"></category><category term="Development"></category></entry><entry><title>My Generative AI and ML usages (as of 2023)</title><link href="https://blog.kartones.net/post/my-generative-ai-and-ml-usages-2023/" rel="alternate"></link><published>2023-12-30T11:30:00+01:00</published><updated>2023-12-30T11:30:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-12-30:/post/my-generative-ai-and-ml-usages-2023/</id><summary type="html">&lt;p&gt;As I was writing &lt;a href="https://blog.kartones.net/post/2023-wrap-up/"&gt;my 2023 wrap-up post&lt;/a&gt;, I noticed I have scattered around different posts some of the generative AI and machine learning scenarios that I'm actively using (the so-called "AI" term). It is a good idea to summarize them because my general advice from 2023 echoes what has …&lt;/p&gt;</summary><content type="html">&lt;p&gt;As I was writing &lt;a href="https://blog.kartones.net/post/2023-wrap-up/"&gt;my 2023 wrap-up post&lt;/a&gt;, I noticed I have scattered around different posts some of the generative AI and machine learning scenarios that I'm actively using (the so-called "AI" term). It is a good idea to summarize them because my general advice from 2023 echoes what has been said a lot during the year: &lt;strong&gt;You should experiment and test with AI technologies&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I will focus only on technologies/APIs, but an increasing number of tools are "AI-Assisted" now, from Spotify's radio DJ to Grammarly/Language Tool.&lt;/p&gt;
&lt;h3&gt;Github Copilot&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;I use it as an auto-complete and smart linting helper&lt;/strong&gt;. It allows me to switch between languages almost on the fly: JS/TS, Python, Go and Bash scripts, in my case.&lt;/p&gt;
&lt;p&gt;Depending on the task, it is either incredibly useful or a terrible mess for more serious &lt;strong&gt;code generation&lt;/strong&gt;: It took me less than 15 minutes to build some React-based animations and SCSS styling changes. On the other hand, even with the tricks of leaving open other tabs with related source files (as context), at times, Copilot struggles to understand what I want to build; even with 3 line long descriptive comments. But it is still worth a try, as it is a decent speed boost for non-complex tasks.&lt;/p&gt;
&lt;p&gt;A related task where it is proving excellent is &lt;strong&gt;writing tests for your code&lt;/strong&gt;. It probably depends on the language, but for Javascript, I keep the file containing the class under test open and write the first one or two tests. Then I just write the starting `it("blablabla"`` line, and it auto-completes it with a perfectly valid body based on the test description.&lt;/p&gt;
&lt;p&gt;The Github Copilot Chat feature is excellent, as it removes the need to ask GPT to roleplay a developer, etc., and can give back examples directly adapted to your programming language and even your open source code files. Try it if you haven't yet. &lt;strong&gt;For technical questions, it became my new "Stack Overflow"&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;ChatGPT&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Learning languages&lt;/strong&gt;: for English, I'd instead go to specialized sites, but for Swedish, it is a fabulous quick learning tool.&lt;/p&gt;
&lt;p&gt;It is &lt;strong&gt;my new Google + Wikipedia&lt;/strong&gt;.Knowing that it might not always be correct, as long as I ask it about topics that I know exist, the information has always been correct (I still fact-check everything, just in case). Or I directly got a &lt;code&gt;"up to my training data of xxxx, I don't know about that"&lt;/code&gt; [1].&lt;/p&gt;
&lt;p&gt;I also use it a lot for &lt;strong&gt;random, trivia-like questions&lt;/strong&gt;. My "trick" is to think if my question is available somewhere on the public internet in trusted places. I'm confident it will adequately answer all my nature, physics, and similar science questions, but beware of more niche answers. e.g. it was able to very precisely answer the question "at the end of the first Dune book, which of the characters are still alive?". But then, when asked something about the second book, it confused some facts with other volumes and hallucinated.&lt;/p&gt;
&lt;p&gt;I've also noticed it is prone to be too gentle, both here and in Copilot-related tasks. If you tell it it was wrong, it will try very hard to please you, often inverting the previous response, even if the new outcome is false/incorrect. This fact and some basic prompting skills are still required to some extent, but I think this is a matter of pure iteration and refinement until those details get solved.&lt;/p&gt;
&lt;p&gt;Another use is to &lt;strong&gt;"spice up" specific messages&lt;/strong&gt;: e.g. we hosted an internal hackathon at work, and I used it to "cheer up" all the announcements/updates I had to write. It might tend to overuse emojis, but you can also tell it not to use any. It is the same with making a sentence or paragraph more formal or more technical.&lt;/p&gt;
&lt;p&gt;My last usage, often API-driven, is to &lt;strong&gt;summarize content&lt;/strong&gt;: From &lt;a href="https://github.com/Kartones/python/blob/master/youtube-summarizer/youtube-summarize.py"&gt;Youtube videos&lt;/a&gt; to complete articles and PDFs, it is excellent at interpreting data, and, via API, you can set its temperature to 0, and then it will be less creative (aka, less prone to hallucinate). The resulting summary is sometimes repetitive and dull, but digesting a 45-minute video into a three-paragraph text is a great time-saving technique.&lt;/p&gt;
&lt;h3&gt;DALL-E 3&lt;/h3&gt;
&lt;p&gt;"In the past" (as if it were a decade ago 😉), I played with DALL-E and the first versions of Stable Diffusion. A nice toy, and an incredible helper for &lt;strong&gt;specific tasks like assisting in Dungeon Mastering RPG sessions&lt;/strong&gt; (although Midjourney is probably better for both player and NPC portraits), but it was either too prone to flaws and imperfections or too direct copying artist styles.&lt;/p&gt;
&lt;p&gt;But then, Microsoft quickly added DALL-3 to Bing as soon as OpenAI announced it, and wow, the results are way better now. The images have fewer artifacts/errors (but look carefully; they still do at times!), it understands the prompts way better [2], and the quality of the images can be outstanding with fewer attempts.&lt;/p&gt;
&lt;p&gt;As an example, with decent but far from advanced prompts, I was able to help a family member &lt;strong&gt;design a small poster&lt;/strong&gt;. It took us 30 minutes of back and forth through an instant messaging app, me generating the images and sending them, her replying with the best one, and other things to tweak/change. The results were quite good; we didn't need to buy any paid clipart or ask for professional help.&lt;/p&gt;
&lt;p&gt;I've also used the OpenAI DALL-E 3 API. There, my experience is that the results are prone to flaws (e.g., missing body parts happens a lot), so &lt;strong&gt;I would wait to fully automate image generation&lt;/strong&gt;, placing an intermediate human review and approval step in the workflow for now.&lt;/p&gt;
&lt;h3&gt;Closing Words&lt;/h3&gt;
&lt;p&gt;Usefulness aside, which is coming close to many science-fiction books regarding AI assistants in concept [3], what impresses me most is the mathematical and technical complexity. LLMs are a huge multi-dimensional Markov chain (oversimplification!), statistically queried, but the text2image diffusion models feel mindblowing to me; It feels magical that we have devised a way to teach computers to convert image patterns and fragments to series of numbers, and even more incredible if we factor that they learn from shapes ("a cat") and fragments ("with tiger stripes") to colors ("orange") and drawing styles that apply to the whole image ("pixel art", "coloring book"...).&lt;/p&gt;
&lt;p&gt;[1] Pseudo-offtopic note: ChatGPT 3.5 is now reporting to have training data until Jan 2022, so they have advanced from the original Sept 2021 mark.&lt;/p&gt;
&lt;p&gt;[2] DALL-E 3 overrides and enriches your prompts. Often, it is good, but if you want more fine-grained control, &lt;a href="https://platform.openai.com/docs/guides/images/prompting"&gt;you can force it to not re-write&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;[3] Execution is still in the early stages, but I have zero doubt we'll get to a perfectly working speech-driven "user interface." Whether it is a single assistant or multiple ones, I'll leave it to futurologists.&lt;/p&gt;</content><category term="post"></category><category term="Development"></category></entry><entry><title>2023 Wrap-up</title><link href="https://blog.kartones.net/post/2023-wrap-up/" rel="alternate"></link><published>2023-12-27T19:50:00+01:00</published><updated>2023-12-27T19:50:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-12-27:/post/2023-wrap-up/</id><summary type="html">&lt;p&gt;Wrapping up the year, it would be good to write a short summary.&lt;/p&gt;
&lt;p&gt;Lots of &lt;a href="https://bazel.build/"&gt;Bazel&lt;/a&gt; (and other build systems), tooling, and automation. Bazel is quite complex and surprisingly rough on some edges, but I think it is a very powerful tool.&lt;/p&gt;
&lt;p&gt;I went through (and survived) two rounds of …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Wrapping up the year, it would be good to write a short summary.&lt;/p&gt;
&lt;p&gt;Lots of &lt;a href="https://bazel.build/"&gt;Bazel&lt;/a&gt; (and other build systems), tooling, and automation. Bazel is quite complex and surprisingly rough on some edges, but I think it is a very powerful tool.&lt;/p&gt;
&lt;p&gt;I went through (and survived) two rounds of lay-offs at work this year; technically, 3, but the middle was focused on a single product.  I'm not going to dig into details or opinions, other than I'm quite unhappy with the tech industry as a whole.&lt;/p&gt;
&lt;p&gt;Read and experimented with some of the basic foundations of LLMs, plus code generation and text and image generation (mostly ChatGPT and DALL-E 3). Despite being too brute-force based, I think LLMs and the AI topic, in general, can significantly change tech. From frequently using GitHub Copilot, to almost daily using ChatGPT [1], and helping friends and family with image-related tasks, it has already changed quite a few things in my everyday life.&lt;/p&gt;
&lt;p&gt;Coded a decent amount of Javascript (but little Typescript) and Python (including Starlark, Bazel's flavor), and a bit of Go.&lt;/p&gt;
&lt;p&gt;I'm ramping up on book reading (tech and non-tech, fiction and non-fiction), but it's one area I could do way more.&lt;/p&gt;
&lt;p&gt;I also read a lot of articles, but I feel there is an information overload as of late. It takes a lot of work to keep up with everything, so I'm iterating on my sources, filtering criteria, and even time spent (e.g., reading a book instead).&lt;/p&gt;
&lt;p&gt;Fewer courses watched, but more talks: Strange Loop, GOTO, InfoQ, and miscellaneous individual talks. But I miss the networking part of a physical conference.&lt;/p&gt;
&lt;p&gt;I deleted my Twitter account. I don't want to be part of the crappy place that Elon is converting it into, so that party is over for me. For the moment, my only social presence is at Mastodon.&lt;/p&gt;
&lt;p&gt;[1] I'm sure they are fully aware, but Google's biggest concern should be the fact that chat conversations with ChatGPT are a much better user experience than a search box. Of course, it's not yet useful in all scenarios, but one way or another, we'll get there.&lt;/p&gt;</content><category term="post"></category><category term="Offtopic"></category></entry><entry><title>An Entity Component System in Javascript</title><link href="https://blog.kartones.net/post/ecs-in-javascript/" rel="alternate"></link><published>2023-12-21T11:50:00+01:00</published><updated>2023-12-21T11:50:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-12-21:/post/ecs-in-javascript/</id><summary type="html">&lt;p&gt;Taking advantage of my Christmas holidays, one topic I had pending exploring was the &lt;a href="https://en.wikipedia.org/wiki/Entity_component_system"&gt;Entity component system (ECS)&lt;/a&gt; architecture pattern. It is used primarily in video-games, but it is fascinating because of the radical departure from object inheritance, heavily leaning into object composition instead.&lt;/p&gt;
&lt;p&gt;One of the best ways to …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Taking advantage of my Christmas holidays, one topic I had pending exploring was the &lt;a href="https://en.wikipedia.org/wiki/Entity_component_system"&gt;Entity component system (ECS)&lt;/a&gt; architecture pattern. It is used primarily in video-games, but it is fascinating because of the radical departure from object inheritance, heavily leaning into object composition instead.&lt;/p&gt;
&lt;p&gt;One of the best ways to learn is to a) read and then b) experiment, so I went and built a minimal (but representative) vanilla Javascript ECS implementation. &lt;/p&gt;
&lt;p&gt;For the impatient, the code is available here: &lt;a href="https://github.com/Kartones/ecs"&gt;https://github.com/Kartones/ecs&lt;/a&gt;, and a basic HTML demo is here: &lt;a href="https://kartones.net/demos/028/"&gt;https://kartones.net/demos/028/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I'll remark on a few technical details, but if you want to see how everything fits, the code is compact and easy to read.&lt;/p&gt;
&lt;p&gt;I created three systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;InputSystem&lt;/code&gt;: To handle (keyboard) input. It interacts with &lt;code&gt;PositionComponent&lt;/code&gt; components, by modifying their velocity "vectors" and/or changing positions when the &lt;code&gt;Spacebar&lt;/code&gt; is pressed (resets all entities to new random positions).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MovementSystem&lt;/code&gt;: Updates &lt;code&gt;PositionComponent&lt;/code&gt; components movement logic: update position based on velocity, or stop if at a boundary (edge)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RenderSystem&lt;/code&gt;: Initializes the screen (an HTML canvas) and tells &lt;code&gt;RenderComponent&lt;/code&gt; components to render based on their &lt;code&gt;PositionComponent&lt;/code&gt; values.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you might have noticed, there are two components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;PositionComponent&lt;/code&gt;: Keeps &lt;code&gt;(x,y)&lt;/code&gt; and a velocity vector&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RenderComponent&lt;/code&gt;: Keeps the "sprite" data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally, a few managers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EntityManager&lt;/code&gt;: Entities do not exist as a class; they are mere identifiers. The sole purpose of &lt;code&gt;EntityManager&lt;/code&gt; is to generate the entity IDs and keep track of them&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ComponentManager&lt;/code&gt;: Keeps track of all components of each entity. When a system is going through an update sweep, queries this manager to ask for all components required; e.g., the &lt;code&gt;RenderSystem&lt;/code&gt; asks for &lt;code&gt;PositionComponent&lt;/code&gt; and &lt;code&gt;RenderComponent&lt;/code&gt;, ensuring that any entity has both (or will be skipped)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SystemManager&lt;/code&gt;: A simple list of all configured systems, plus an &lt;code&gt;update()&lt;/code&gt; method that, surprise, triggers an update cycle on all systems&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And that is mostly everything! At first, I tried to plug in &lt;a href="https://phaser.io/"&gt;Phaser&lt;/a&gt; for rendering graphics and handling input, but it is already an ECS! So, instead of doing weird code to access some of the systems, I discarded it and implemented a simple keyDown handler for the input and an HTML canvas for the renderer. But Phaser looks so nice and well-structured! I'll try it as a complete game engine in the future.&lt;/p&gt;</content><category term="post"></category><category term="Development"></category><category term="Game Dev"></category></entry><entry><title>The origins of LEGO Mindstorms</title><link href="https://blog.kartones.net/post/origins-of-lego-mindstorms/" rel="alternate"></link><published>2023-12-17T20:55:00+01:00</published><updated>2023-12-17T20:55:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-12-17:/post/origins-of-lego-mindstorms/</id><summary type="html">&lt;p&gt;I recently learned that around two years after announcing the &lt;a href="https://www.lego.com/en-gb/aboutus/news/2020/june/lego-mindstorms-robot-inventor"&gt;Mindstorms Robot Inventor&lt;/a&gt;, LEGO &lt;a href="https://www.theverge.com/2022/10/28/23428766/lego-discontinue-mindstorm-educational-robots"&gt;discontinued the Mindstorms brand&lt;/a&gt; in 2022.&lt;/p&gt;
&lt;p&gt;It is sad because &lt;a href="https://blog.kartones.net/post/two-decades-lego-mindstorms"&gt;I've always thought&lt;/a&gt; LEGO Mindstorms was a great way to build robots by focusing on the creative and programming aspects while removing the complex hardware involved …&lt;/p&gt;</summary><content type="html">&lt;p&gt;I recently learned that around two years after announcing the &lt;a href="https://www.lego.com/en-gb/aboutus/news/2020/june/lego-mindstorms-robot-inventor"&gt;Mindstorms Robot Inventor&lt;/a&gt;, LEGO &lt;a href="https://www.theverge.com/2022/10/28/23428766/lego-discontinue-mindstorm-educational-robots"&gt;discontinued the Mindstorms brand&lt;/a&gt; in 2022.&lt;/p&gt;
&lt;p&gt;It is sad because &lt;a href="https://blog.kartones.net/post/two-decades-lego-mindstorms"&gt;I've always thought&lt;/a&gt; LEGO Mindstorms was a great way to build robots by focusing on the creative and programming aspects while removing the complex hardware involved in a non-LEGO robot.&lt;/p&gt;
&lt;p&gt;Reading a bit about its history, it is surprising how old it is and its origin, so here are some highlights I found interesting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;It all began with the &lt;a href="https://en.wikipedia.org/wiki/Logo_(programming_language)"&gt;Logo programming language&lt;/a&gt;, which was created in 1967! I used the the Turtle Logo Pascal library (&lt;a href="https://cs.nyu.edu/~marateck/turtle.html"&gt;Turtle Graphics&lt;/a&gt;?) to draw figures and spirals in high school.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Lego_Mindstorms#Lego/Logo_and_the_Technic_Control_Center_(1985)"&gt;Technic Control Center&lt;/a&gt;, in 1985, was the first combination of LEGO with a programming language.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;"Logo Blocks" were the predecessors of "RCX Code" and later evolutions, a purely visual program control flow language, by adding and linking visual blocks that represented loops, conditionals, sensor values, and the like.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The first &lt;strong&gt;LEGO Mindstorms&lt;/strong&gt; product appeared in 1998 (the Robotics Invention System, aka "yellow brick"), but the name comes from a 1980 book, &lt;a href="https://en.wikipedia.org/wiki/Mindstorms_(book)"&gt;Mindstorms: Children, Computers, and Powerful Ideas&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The yellow brick was the first model commercialized to the public, but before it existed both a red brick and a grey brick:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture&gt;&lt;source srcset="https://images.kartones.net/posts/kartonesblog/lego_programmable_bricks.webp" type="image/webp"/&gt;&lt;img alt="grey, red and yellow LEGO programmable bricks (from Wikipedia)" height="576" loading="lazy" src="https://images.kartones.net/posts/kartonesblog/lego_programmable_bricks.jpg" width="741"/&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Additional Resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cs.uml.edu/~fredm/papers/magical-machines.pdf"&gt;To Mindstorms and Beyond: Evolution of a Construction Kit for Magical Machines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content><category term="post"></category><category term="Development"></category></entry><entry><title>Content quality is suffering (as of late 2023)</title><link href="https://blog.kartones.net/post/content-quality-is-suffering-2023/" rel="alternate"></link><published>2023-12-02T19:00:00+01:00</published><updated>2023-12-02T19:00:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-12-02:/post/content-quality-is-suffering-2023/</id><summary type="html">&lt;p&gt;Lately, I've come to understand why GPT models avoided digesting content from later than September 2021 [1]: The amount of artificially extended written articles and videos is staggering; probably there are also more fully AI-generated cases, but I wanted to focus on human-originated cases. For me, it is more than …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Lately, I've come to understand why GPT models avoided digesting content from later than September 2021 [1]: The amount of artificially extended written articles and videos is staggering; probably there are also more fully AI-generated cases, but I wanted to focus on human-originated cases. For me, it is more than just a matter of wasting extra time when reading because there are whole sentences or paragraphs devoid of content or repeating for the sixth time the same point with different words. The form is also of poorer quality, making it harder to understand (which fails one of the main principles of writing, to communicate something!).&lt;/p&gt;
&lt;p&gt;Let me mention two examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everybody knows that 95% of YouTube channel content nowadays is artificially extended to get extra displayed ad slots. As a specific example, a particular channel that some friends like watching seldom publishes videos shorter than 30 minutes, sometimes going as far as 90 minutes. The "short" ones are usually content that can be explained in less than 5 minutes. For the longer ones, at minimum, one could remove 1/3 of the length (interviews that get too pedantic, "in-depth reports" that iterate multiple times over each topic...). I did a test, and in a specific video (~25 mins), they mentioned one relevant term more than 20 times. This is why I experimented &lt;a href="https://github.com/Kartones/python/tree/master/youtube-summarizer"&gt;with building a YouTube summarizer&lt;/a&gt; using ChatGPT, and I use a fine-tuned variant if I check YT (which is rare).&lt;/li&gt;
&lt;li&gt;When reading content in English, I have some leeway regarding initially wrong-looking texts, because I am not an expert in the language, so it might be a wrong interpretation. But when I read Spanish articles, I am way stricter, and it is degrading a lot outside main media sites: Half a page of buzzwords, abusing bolding text on almost every sentence (probably because not even the author can find relevant information otherwise), with sentences often cut in half, breaking the natural structure and forcing you to do extra cognitive work (maybe to give the impression of a longer text?), at times with non-sense words suspiciously looking like other more appropriate ones (LLM mistake?). And worst of all, if you read the text many times, any reasonable human being will notice something wrong.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Everybody is free to write however they want. I have friends who run their texts through ChatGPT to make them sound more formal, and I've used it to generate more cheerful messages for Slack announcements, so it is not that I'm against their use; quite the opposite [2]. But people are forgetting the basics: &lt;strong&gt;make your content readable and understandable, and then, if you want, make it longer&lt;/strong&gt;. Some books could be perfectly summarized in less than a single page, so it is nothing new!&lt;/p&gt;
&lt;p&gt;For example, I currently use &lt;a href="https://grammarly.com/"&gt;Grammarly&lt;/a&gt; (premium) to proofread my blog posts and some emails. At work, I use &lt;a href="https://languagetool.org/"&gt;LanguageTool&lt;/a&gt;, which serves the same function [3]. They both have browser plugins and give you spelling corrections and suggestions about tones, false friends, partial or complete sentence rewordings for clarity... &lt;/p&gt;
&lt;p&gt;Nothing beats careful human proofreading, but if you don't have the time or desire (I often don't, and simply click publish once grammar check is ok), at least try using suitable tools to increase legibility. It would also be nice to avoid "dark patterns" in writing online content; the tools I mentioned will probably flag most of them.&lt;/p&gt;
&lt;p&gt;[1] Although I read somewhere that this might change soon. They probably devised an accurate enough way to detect artificial content to discard.&lt;/p&gt;
&lt;p&gt;[2] The summaries on &lt;a href="https://blog.kartones.net/"&gt;this blog's landing page&lt;/a&gt; are also made by GPT (but revised by me), and I use ChatGPT very often for both general questions and as a Swedish tutor/teacher. I sincerely think that LLMs are a technical revolution in many ways.&lt;/p&gt;
&lt;p&gt;[3] I'm considering switching to LanguageTool's premium plan, as they don't use your data for ML training. But if you are okay with that, Grammarly is excellent.&lt;/p&gt;</content><category term="post"></category><category term="Offtopic"></category></entry><entry><title>Tiny Markdown-Based Wiki</title><link href="https://blog.kartones.net/post/tiny-markdown-wiki/" rel="alternate"></link><published>2023-10-31T18:30:00+01:00</published><updated>2023-10-31T18:30:00+01:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-10-31:/post/tiny-markdown-wiki/</id><summary type="html">&lt;p&gt;&lt;a href="https://blog.kartones.net/post/note-taking-and-knowledge-base/"&gt;Earlier this year&lt;/a&gt;, I settled on using markdown files to keep my notes around different topics. For editing and while using my computer, I am happy with &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt;, but at times I want to quickly check something (maybe from my work computer or the phone). I could pay for the …&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://blog.kartones.net/post/note-taking-and-knowledge-base/"&gt;Earlier this year&lt;/a&gt;, I settled on using markdown files to keep my notes around different topics. For editing and while using my computer, I am happy with &lt;a href="https://obsidian.md/"&gt;Obsidian&lt;/a&gt;, but at times I want to quickly check something (maybe from my work computer or the phone). I could pay for the sync features of the tool, but as we're talking markdown files and a few media contents, I decided to build a simple web alternative.&lt;/p&gt;
&lt;p&gt;TL;DR is that you can check &lt;a href="https://github.com/Kartones/tiny-wiki"&gt;tiny-wiki in GitHub&lt;/a&gt;. It is a no-frameworks, HTML 5 + CSS + Javascript glorified markdown renderer.&lt;/p&gt;
&lt;p&gt;As a summary, it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fetches an &lt;code&gt;items.json&lt;/code&gt; file containing a list of markdown files. Folders are treated as sections/subsections (supports up to one section and one subsection, so two folder levels), and the file name is treated as the Document name (and &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; heading once rendered as HTML). The list of sections, subsections, and files is rendered at the left.&lt;/li&gt;
&lt;li&gt;When loading a page, it fetches the markdown file, and then renders it client-side. I did some basic benchmarks, and it is faster to fetch the smaller markdown and render it, rather than pre-generating all the HTML files (at least on a decent computer or phone).&lt;/li&gt;
&lt;li&gt;Also, when rendering a page, extracts the headings from the document and creates a navigable list at the right of the content&lt;/li&gt;
&lt;li&gt;Supports light/dark theme (saving to &lt;code&gt;localStorage&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The whole Javascript code is less than 200 lines, so straightforward to understand.&lt;/p&gt;
&lt;p&gt;It is mentioned in the README of the repo, but I used &lt;a href="https://marked.js.org/"&gt;Marked&lt;/a&gt; for transforming the markdowns (really fast, and very easy to extend/modify the tag renderers) and &lt;a href="https://picocss.com/docs/"&gt;Pico.css&lt;/a&gt; for the styling (I love it because without needing to add any classes already makes everything pretty). Be warned that the styling is probably not perfect. I built this and did some quick experiments, but that's all.&lt;/p&gt;
&lt;p&gt;🦇 Happy Halloween 🎃!&lt;/p&gt;</content><category term="post"></category><category term="Development"></category></entry><entry><title>On multi-tasking and high WIP</title><link href="https://blog.kartones.net/post/multitasking-and-high-wip/" rel="alternate"></link><published>2023-10-17T07:00:00+02:00</published><updated>2023-10-17T07:00:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-10-17:/post/multitasking-and-high-wip/</id><content type="html">&lt;p&gt;This video details why multi-tasking, or having a high work-in-progress (WIP), is ineffective. The author explains it simply and clearly, demonstrated in multiple common scenarios. And yet, it is still an endemic problem we face at work too often.&lt;/p&gt;
&lt;p&gt;&lt;iframe frameborder="0" height="379" src="https://www.youtube-nocookie.com/embed/3MxAUurD9gU" width="620"&gt;&lt;/iframe&gt;&lt;/p&gt;</content><category term="post"></category><category term="Offtopic"></category></entry><entry><title>Book Review: DOOM Guy</title><link href="https://blog.kartones.net/post/book-review-doom-guy-john-romero/" rel="alternate"></link><published>2023-10-15T18:15:00+02:00</published><updated>2023-10-15T18:15:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-10-15:/post/book-review-doom-guy-john-romero/</id><summary type="html">&lt;h3&gt;Review&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source srcset="https://images.kartones.net/posts/kartonesblog/book_doom_guy_john_romero.webp" type="image/webp"/&gt;&lt;img alt="DOOM Guy book cover" height="200" loading="lazy" src="https://images.kartones.net/posts/kartonesblog/book_doom_guy_john_romero.jpg" width="133"/&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;: &lt;a href="https://romero.com/shop/p/doomguy"&gt;DOOM Guy: Life in First Person&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Author(s)&lt;/strong&gt;: John Romero&lt;/p&gt;
&lt;p&gt;Let's begin with the end: I won't say that I did not enjoy reading this title because I did, all in all, like the 1980 to 1996 stories. But for someone who boasts multiple times about having an …&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Review&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source srcset="https://images.kartones.net/posts/kartonesblog/book_doom_guy_john_romero.webp" type="image/webp"/&gt;&lt;img alt="DOOM Guy book cover" height="200" loading="lazy" src="https://images.kartones.net/posts/kartonesblog/book_doom_guy_john_romero.jpg" width="133"/&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;: &lt;a href="https://romero.com/shop/p/doomguy"&gt;DOOM Guy: Life in First Person&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Author(s)&lt;/strong&gt;: John Romero&lt;/p&gt;
&lt;p&gt;Let's begin with the end: I won't say that I did not enjoy reading this title because I did, all in all, like the 1980 to 1996 stories. But for someone who boasts multiple times about having an extraordinary capacity to recall past events in absolute detail, it is interesting to see what the book does &lt;em&gt;not&lt;/em&gt; speak about. e.g., all we get from the Daikatana epoch is a series of founding and management issues and mistakes and a final &lt;em&gt;mea culpa&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So, I will focus on the positive things instead, of which there are also plenty.&lt;/p&gt;
&lt;p&gt;The contents of the book are laid out, according to my ebook reader, as follows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;10% early life (until early 80s)&lt;/li&gt;
&lt;li&gt;65% pre-id "dev life" &amp;amp; id Software (until 1996)&lt;/li&gt;
&lt;li&gt;15% Ion Storm &amp;amp; miscellaneous (until 2001)&lt;/li&gt;
&lt;li&gt;10% Up until 2022 (emphasis on his new DooM map packs)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that there is a majority of content related to Commander Keen, Wolfenstein 3D, DooM, and Quake; I am adding Daikatana to the last one, as it used the Quake &amp;amp; later on Quake II engines.&lt;/p&gt;
&lt;p&gt;This book represents an excellent tale of how, with effort and dedication, you can create great things, no matter where you come from. It might not be easy, it might take some time, but it is possible. We'll read about the first PC games and the abundant problems they posed, always limited by CPU speed, but also severely handicapped by primitive graphic cards. We'll also learn about the first steps of the &lt;em&gt;shareware&lt;/em&gt; distribution system, and why John Romero and John Carmack got a lot of money, founded id Software, and then became very wealthy in their early 20s.&lt;/p&gt;
&lt;p&gt;I grew up living the same PC evolution and playing the very same games they were creating, from Commander Keen and Wolfenstein 3D to DooM and DooM II, and Quake is my all-time favourite videogame, so I devour any article &lt;a href="https://blog.kartones.net/page/reviews/"&gt;or book&lt;/a&gt; that tells me more about them and how they were made. You won't be disappointed in that regard, there is plenty of internal information about building each title.&lt;/p&gt;
&lt;p&gt;Many other geeky details are also fascinating, like why coding both DooM tools and the game itself in NeXT computers, or many of the features that Romero added to each version of the map editors, or the rare tools they would build (&lt;code&gt;"Carmacizing the maps"&lt;/code&gt;, aka compressing) and insane work they'd all put (e.g., &lt;code&gt;"Michael Abrash had had to rewrite his highly optimized renderer eight times"&lt;/code&gt;). There are also notes of humour and funny stories here and there.&lt;/p&gt;
&lt;p&gt;The best part of the Ion Storm era is some good lessons that Romero states as some introspection-work outcome, primarily oriented towards managing people in a company, but a few are also related with being in the videogame production industry. And, of course, the whole thing is a cautionary tale about correctly picking co-founders (and deciding their share).&lt;/p&gt;
&lt;p&gt;The final chapters, post-Daikatana era, lose momentum and are salvageable only because of the Sigil DooM maps part.&lt;/p&gt;
&lt;p&gt;It was a decent read, which I wasn't sure what to expect and delivered in some areas, but did not on others. Just don't expect any shocking reveal regarding what you can already read about on the internet about id Software.&lt;/p&gt;</content><category term="post"></category><category term="Reviews"></category><category term="Gaming"></category></entry><entry><title>Reproducible Builds</title><link href="https://blog.kartones.net/post/reproducible-builds/" rel="alternate"></link><published>2023-09-24T21:00:00+02:00</published><updated>2023-09-24T21:00:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-09-24:/post/reproducible-builds/</id><summary type="html">&lt;p&gt;One topic, that I incorrectly though was always happening, was that compiling always generated the exact same output (the same bytes). &lt;strong&gt;This is not always the case&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;I know about compilation modes, optimization flags and the like, so we're not talking here about those basic scenarios. We're talking about cases …&lt;/p&gt;</summary><content type="html">&lt;p&gt;One topic, that I incorrectly though was always happening, was that compiling always generated the exact same output (the same bytes). &lt;strong&gt;This is not always the case&lt;/strong&gt;. &lt;/p&gt;
&lt;p&gt;I know about compilation modes, optimization flags and the like, so we're not talking here about those basic scenarios. We're talking about cases where running exactly the same flags, in the same environment, and against the same source code (let's say C++), generates a binary that works exactly the same, and yet has a different checksum (and differs in some bytes).&lt;/p&gt;
&lt;p&gt;There are two big issues with this lack of output determinism, or as it is better known, lack of reproducible builds:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Security&lt;/em&gt;: How can you ensure that a binary has not been tampered with, if its signature changes?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Build Caching&lt;/em&gt;: You will always get a cache miss if you always get a different binary &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My tiny research originated from discussing with a colleague why some Windows Visual C++ builds were not cached, when there were no apparent code changes [1] and the flags were not mutating.&lt;/p&gt;
&lt;p&gt;The first thing that he found was that indeed, &lt;strong&gt;Microsoft Visual C++ (MSVC)&lt;/strong&gt; is not deterministic by default. The following article will help you achieve it: &lt;a href="https://nikhilism.com/post/2020/windows-deterministic-builds/"&gt;https://nikhilism.com/post/2020/windows-deterministic-builds/&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Coincidentally, I read that &lt;strong&gt;Golang&lt;/strong&gt; since 1.21.0 has a perfectly reproducible build toolchain. It was explained in a &lt;a href="https://go.dev/blog/rebuild"&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At this point, I decided to check if there were more resources, and found that there is a &lt;a href="https://reproducible-builds.org/docs/"&gt;Reproducible Builds organization&lt;/a&gt;, with great general tips on what to look for in compilers, project files and the like, to achieve determinism.&lt;/p&gt;
&lt;p&gt;And I was quite surprised to find that &lt;strong&gt;Java&lt;/strong&gt; is also not deterministic by default, but with a twist: the bytecode is, but the &lt;code&gt;jar&lt;/code&gt; files aren't. So here are some articles around that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;generic reproducibility: &lt;a href="https://reproducible-builds.org/docs/jvm/"&gt;https://reproducible-builds.org/docs/jvm/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;with Maven: &lt;a href="https://maven.apache.org/guides/mini/guide-reproducible-builds.html"&gt;https://maven.apache.org/guides/mini/guide-reproducible-builds.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;with Gradle: &lt;a href="https://dzone.com/articles/reproducible-builds-in-java"&gt;https://dzone.com/articles/reproducible-builds-in-java&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As an extra note, these kind of non-deterministic behaviours can also happen on scripted languages like &lt;strong&gt;TypeScript&lt;/strong&gt;/&lt;strong&gt;JavaScript&lt;/strong&gt;, not when transpiling code, but when doing tree-shaking and/or bundling; See for example the multiple places where you can set flags to &lt;code&gt;deterministic&lt;/code&gt; in &lt;a href="https://webpack.js.org/configuration/optimization/"&gt;Webpack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;[1] Build avoidance is a different topic, so let's assume that there was &lt;em&gt;some&lt;/em&gt; reason to trigger a compilation, instead of skipping the whole step and reusing a previously compiled artifact.&lt;/p&gt;</content><category term="post"></category><category term="Development"></category></entry><entry><title>Book Review: At The Heart Of Management</title><link href="https://blog.kartones.net/post/book-review-at-the-heart-of-management/" rel="alternate"></link><published>2023-09-13T07:50:00+02:00</published><updated>2023-09-13T07:50:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-09-13:/post/book-review-at-the-heart-of-management/</id><summary type="html">&lt;p&gt;Note: Free evaluation copy provided by the author.&lt;/p&gt;
&lt;h3&gt;Review&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source srcset="https://images.kartones.net/posts/kartonesblog/book_at_the_heart_of_management.webp" type="image/webp"/&gt;&lt;img alt="At The Heart Of Management book cover" height="200" loading="lazy" src="https://images.kartones.net/posts/kartonesblog/book_at_the_heart_of_management.jpg" width="130"/&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;: &lt;a href="https://www.goodreads.com/book/show/128182590-at-the-heart-of-management"&gt;At The Heart Of Management&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Author&lt;/strong&gt;: Lino F. Ciceri&lt;/p&gt;
&lt;p&gt;Disclaimer: I'm not a manager. I've led small technical teams in the past, but not departments or even companies for which this book aims.&lt;/p&gt;
&lt;p&gt;After the remark, I'll begin by saying …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Note: Free evaluation copy provided by the author.&lt;/p&gt;
&lt;h3&gt;Review&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source srcset="https://images.kartones.net/posts/kartonesblog/book_at_the_heart_of_management.webp" type="image/webp"/&gt;&lt;img alt="At The Heart Of Management book cover" height="200" loading="lazy" src="https://images.kartones.net/posts/kartonesblog/book_at_the_heart_of_management.jpg" width="130"/&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Title&lt;/strong&gt;: &lt;a href="https://www.goodreads.com/book/show/128182590-at-the-heart-of-management"&gt;At The Heart Of Management&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Author&lt;/strong&gt;: Lino F. Ciceri&lt;/p&gt;
&lt;p&gt;Disclaimer: I'm not a manager. I've led small technical teams in the past, but not departments or even companies for which this book aims.&lt;/p&gt;
&lt;p&gt;After the remark, I'll begin by saying that the book is interesting. I don't know how some of the themes will apply at (or collide with) different companies, but at least some chapters try to be different. There is also an overall attempt to add science, or at least use it as inspiration, to measure and orchestrate metrics, procedures and processes. &lt;/p&gt;
&lt;p&gt;As I mentioned, this is a book about pure management: of people, of processes, of a company's mission, vision, and goals, of how to leverage research and innovation versus production, and even about ethics and behaviors, both internally and externally. But it aims to be different in a few topics, which I'll mention.&lt;/p&gt;
&lt;p&gt;Extrapolating electromagnetic fields as market views, thermodynamics as a form of viewing impediments to change, and organizations as molecular bodies "more than the sum of their member atoms" are a few examples, sometimes even going as far as providing formulas to measure success. I don't know how applicable they are; thus, I can't judge them, but at least it is a different view, and nature is quite clever so why not try to imitate it (same as we do in areas like robotics)?&lt;/p&gt;
&lt;p&gt;About the growth or decline of a business being always an exponential process, I am not so sure, as I've seen and know of quite a few companies that are stuck in the middle, not drowning but not going up much either, small and big, private and public ones. But my view is biased and limited, so that I can be wrong.  &lt;/p&gt;
&lt;p&gt;And the last point is that I appreciate the general theme of optimism in the book: aiming for "the common good", for "prioritizing helping others ahead of ourselves", for suggesting trying to avoid massive reductions in force/employee terminations, playing fair with the competition... All of this is admirable, but at least in my sector (tech companies), it is something that very rarely happens. I usually see companies prioritizing benefits (theirs and their investors') over anything else, employees included, as we can see from the 2022-2023 mass layoffs.&lt;/p&gt;
&lt;p&gt;There are of course general and traditional management bits of advice, a few of them even I had heard about. Cross-domain thinking and holistic views I think are becoming more widespread, but it is good to see them remarked. And a good list of questions to ask yourself or the company executives to reflect upon and see how to steer a company.&lt;/p&gt;
&lt;p&gt;Those previously mentioned uncommon topics are, I think, what the author means constitute the fundamental pillars of his "Timeless Management" framework. Using science and formulas and aiming to do some good, not only being selfish, in order to survive "forever". &lt;/p&gt;
&lt;p&gt;In conclusion, it is a curious (innovative maybe?) approach to the classical company management.&lt;/p&gt;</content><category term="post"></category><category term="Reviews"></category></entry><entry><title>Course Review: Public Speaking for Non-Native English Speakers (LinkedIn Learning)</title><link href="https://blog.kartones.net/post/course-review-public-speaking-for-non-native-english-speakers-linkedin-learning/" rel="alternate"></link><published>2023-09-03T20:40:00+02:00</published><updated>2023-09-03T20:40:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-09-03:/post/course-review-public-speaking-for-non-native-english-speakers-linkedin-learning/</id><summary type="html">&lt;p&gt;Course link: &lt;a href="https://www.linkedin.com/learning/public-speaking-for-non-native-english-speakers/"&gt;https://www.linkedin.com/learning/public-speaking-for-non-native-english-speakers/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's been a while since I've done some proper public speaking, except in internal team or product area circles, but now it is always in English. And I am both not a very talkative person, and not a native English speaker.&lt;/p&gt;
&lt;p&gt;This …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Course link: &lt;a href="https://www.linkedin.com/learning/public-speaking-for-non-native-english-speakers/"&gt;https://www.linkedin.com/learning/public-speaking-for-non-native-english-speakers/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's been a while since I've done some proper public speaking, except in internal team or product area circles, but now it is always in English. And I am both not a very talkative person, and not a native English speaker.&lt;/p&gt;
&lt;p&gt;This course of almost two hours of content felt different from most (which tend to just teach you how to speak more correct). It goes the other way around, explaining techniques to, while being yourself, being properly understood, becoming more confident, and tricks on how to pronounce English not exactly "better", but instead "more clear".&lt;/p&gt;
&lt;p&gt;I really liked the advices it gives, and the exercise files and additional content are among the most interesting ones I've seen in English-teaching related courses.&lt;/p&gt;</content><category term="post"></category><category term="Reviews"></category></entry><entry><title>Installing Windows 10 on the Asus ROG Ally RC71L</title><link href="https://blog.kartones.net/post/installing-windows-10-on-asus-rog-ally/" rel="alternate"></link><published>2023-08-26T21:30:00+02:00</published><updated>2023-08-26T21:30:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-08-26:/post/installing-windows-10-on-asus-rog-ally/</id><summary type="html">&lt;p&gt;The new &lt;a href="https://en.wikipedia.org/wiki/Asus_ROG_Ally"&gt;Asus ROG Ally&lt;/a&gt; might devour batteries, but it is a portable powerhouse. Featuring a full Windows 10 operating system, you can install any PC game, and most will run quite nicely. I don't own one, but a friend does, and he was tinkering with backing its data up …&lt;/p&gt;</summary><content type="html">&lt;p&gt;The new &lt;a href="https://en.wikipedia.org/wiki/Asus_ROG_Ally"&gt;Asus ROG Ally&lt;/a&gt; might devour batteries, but it is a portable powerhouse. Featuring a full Windows 10 operating system, you can install any PC game, and most will run quite nicely. I don't own one, but a friend does, and he was tinkering with backing its data up, so I asked him why not try installing Windows 10. He did, and the results are so good that he kindly sent me his notes and Powershell scripts so I could write this blog post and share a small tutorial on how to do it.&lt;/p&gt;
&lt;p&gt;First of all, I'll address the question of &lt;strong&gt;why?&lt;/strong&gt;. The answer is also quite direct (and simple): &lt;strong&gt;Because Windows 10 consumes way fewer resources than Windows 11&lt;/strong&gt;. This might sound weird when, internally, Windows 10 is still Windows &lt;code&gt;10.0.xxx&lt;/code&gt;; it's not even &lt;code&gt;10.1&lt;/code&gt;, so you might think that it is a big service pack... and while there are some noticeable UI changes, it mostly feels like a bloated Win10. But one thing it does is contain a lot more official Microsoft bloatware and extra services: More apps and services to try to sell you Office 365, pure Ads, Bing search bars... 
Windows 10 is suitable for gaming, even more so now that Windows 7 has reached end-of-life (Win7 was perfect for a PC gaming system). You can tweak and turn off most annoyances and get it to run reasonably fast. My gaming rig feels blazing fast because it's fine-tuned only for games.&lt;/p&gt;
&lt;p&gt;Before we go into the tutorial, in case you want to know what you're gaining and some numbers of the actual improvements, here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some games, like Dark Souls 1, went from needing 20W to deliver 60 FPS to now sustaining those 60 FPS just with 10W&lt;/li&gt;
&lt;li&gt;Some Ryujinx titles went from not maintaining 60 FPS at 15W to stable 60 FPS at 10W&lt;/li&gt;
&lt;li&gt;Other games, like Elden Ring, still require the performance mode, but you can notice a significant increase in FPS (or bump up the graphic settings)&lt;/li&gt;
&lt;li&gt;Diablo IV can be played with medium settings at 15W (this one he didn't measured before, so more for informative purposes)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So we're talking about &lt;strong&gt;a 33% to 50% improvement&lt;/strong&gt;, either in battery life or performance, depending on what you seek. It is a significant boost.&lt;/p&gt;
&lt;p&gt;So, let's get on with the steps:&lt;/p&gt;
&lt;h3&gt;Pre-requisites&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Backup the device's HDD contents you want to preserve. &lt;strong&gt;Everything will be wiped out&lt;/strong&gt;. For example, perform a backup of the installed Windows 11 drivers (e.g., via &lt;a href="http://drivermagician.com/"&gt;Driver Magician&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;You need &lt;a href="https://www.xda-developers.com/best-asus-rog-ally-docks/"&gt;a dock&lt;/a&gt; with USB and ethernet adapters. Mouse and keyboard are also highly recommended&lt;/li&gt;
&lt;li&gt;Download the official Windows 11 drivers available at &lt;a href="https://rog.asus.com/gaming-handhelds/rog-ally/rog-ally-2023/helpdesk_download/"&gt;https://rog.asus.com/gaming-handhelds/rog-ally/rog-ally-2023/helpdesk_download/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A USB drive loaded with a &lt;strong&gt;Windows 10 21H1&lt;/strong&gt; ISO, setup with e.g. &lt;a href="https://rufus.ie/en/"&gt;Rufus&lt;/a&gt;. Note the specific Windows 10 version, more details later&lt;/li&gt;
&lt;li&gt;Disable Secure Boot in the device BIOS&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Steps&lt;/h3&gt;
&lt;p&gt;With the device turned off and &lt;strong&gt;offline&lt;/strong&gt; (don't yet connect the ethernet cable to the dock)&lt;/p&gt;
&lt;h4&gt;Setting up the BIOS&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Connect the USB ready with the Windows 10 image  &lt;/li&gt;
&lt;li&gt;Access the BIOS by holding &lt;code&gt;volume -&lt;/code&gt; and turning on the device. Once in the BIOS you'll notice a vibration and can let go of the button press&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;boot&lt;/code&gt; -&amp;gt; &lt;code&gt;secureboot&lt;/code&gt; and disable it&lt;/li&gt;
&lt;li&gt;At the boot sequence section, choose the USB drive as the first boot device&lt;/li&gt;
&lt;li&gt;Save changes &amp;amp; exit BIOS&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Installing Windows&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;The device will reboot and begin the installation&lt;/li&gt;
&lt;li&gt;At Windows setup, choose &lt;code&gt;custom install&lt;/code&gt; and delete all device HDD partitions (100% free space, in a single empty partition)&lt;/li&gt;
&lt;li&gt;Proceed then as usual with the setup&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Installing the drivers&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Drivers will be installed manually, by going to Windows Device Manager&lt;/li&gt;
&lt;li&gt;For each unknown device, first choose to install the driver from a location, and point to the folder containing the exported drivers from the original install (via Driver Magician or a similar tool)&lt;/li&gt;
&lt;li&gt;Some devices will still be unknown. Now install the downloaded drivers from the Asus website&lt;/li&gt;
&lt;li&gt;Reboot the machine&lt;/li&gt;
&lt;li&gt;Now, we &lt;strong&gt;must disable Windows automatic updates&lt;/strong&gt;. Else, it might automatically upgrade you to Windows 11 and screw up this whole process. But we still need some drivers contained in Windows Update, so we need to do a single update sweep. [1]&lt;/li&gt;
&lt;li&gt;Run these 3 scripts from an elevated (Administrator) prompt:&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/Kartones/fde023c0e6af12d4f9977f068835c755"&gt;Win 10 Disable Automatic App Updates.bat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/Kartones/83f75d55521744186f62ec76e9da93a3"&gt;Win 10 Disable Automatic Windows Updates.bat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gist.github.com/Kartones/5682924eceb6fbf85b7eea324ac0c0eb"&gt;Win 10 Disable Forced Reboot After Update.bat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;After running the Powershell script, you can check that updates are "company managed" instead of "automatic" in the Update settings&lt;/li&gt;
&lt;li&gt;Now we can plug in the ethernet cable, and click on &lt;code&gt;search for updates&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Ensure in the list of updates that will appear that the "network adapter" additional update is selected, and check it/include it if was not selected&lt;/li&gt;
&lt;li&gt;Proceed to install this updates list, but do not press again the update button&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Installing Asus Crate&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Download the software from &lt;a href="https://www.asus.com/supportonly/armoury%20crate/helpdesk_download/"&gt;https://www.asus.com/supportonly/armoury%20crate/helpdesk_download/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Choose the &lt;code&gt;lite&lt;/code&gt; version, so it downloads and installs only the required components that it will auto-detect during the setup&lt;/li&gt;
&lt;li&gt;After installing, in the &lt;code&gt;personalization&lt;/code&gt; panel, &lt;code&gt;task bar&lt;/code&gt;, configure the tactile keyboard to activate when there's no physical keyboard connected, or else the on-screen keyboard won't work properly&lt;/li&gt;
&lt;li&gt;Once the setup and keyboard config are done, reboot Windows, and you're ready to install your games. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Congratulations, now you have a nice Windows 10 powered Asus ROG Ally!&lt;/p&gt;
&lt;h4&gt;Extra steps&lt;/h4&gt;
&lt;p&gt;At this point, you can remove the USB Dock if you want.&lt;/p&gt;
&lt;p&gt;You should remove all unwanted applications and services. You can do it from the &lt;code&gt;Apps&lt;/code&gt; section in Windows settings, or via Powershell scripts.&lt;/p&gt;
&lt;p&gt;&lt;br/&gt;&lt;/p&gt;
&lt;p&gt;[1] We use the Windows 10 &lt;code&gt;22H1&lt;/code&gt; version image to be able to fetch updates without it installing any Windows 11 upgrade tool, at least on the first update run.&lt;/p&gt;</content><category term="post"></category><category term="Systems-IT"></category><category term="Gaming"></category></entry><entry><title>Course Review: ChatGPT Prompt Engineering for Developers (DeepLearning.AI)</title><link href="https://blog.kartones.net/post/course-review-cahtgpt-prompt-engineering-for-developers-deeplearningai/" rel="alternate"></link><published>2023-08-16T21:10:00+02:00</published><updated>2023-08-16T21:10:00+02:00</updated><author><name>Kartones</name></author><id>tag:blog.kartones.net,2023-08-16:/post/course-review-cahtgpt-prompt-engineering-for-developers-deeplearningai/</id><summary type="html">&lt;p&gt;Course link: &lt;a href="https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/"&gt;https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A brief but quite interesting course to either learn or improve basic prompt engineering skills when using ChatGPT models. During its one hour of duration, we will understand how we can guide the LLMs to provide better solutions, control their randomness, format …&lt;/p&gt;</summary><content type="html">&lt;p&gt;Course link: &lt;a href="https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/"&gt;https://www.deeplearning.ai/short-courses/chatgpt-prompt-engineering-for-developers/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A brief but quite interesting course to either learn or improve basic prompt engineering skills when using ChatGPT models. During its one hour of duration, we will understand how we can guide the LLMs to provide better solutions, control their randomness, format the output, and along the way see quite a few features that might not be so obvious or well known.&lt;/p&gt;
&lt;p&gt;Free (for now at least), with an integrated Jupyter notebook environment to reproduce the examples explained in the six lessons, and clear and concise. Totally recommended.&lt;/p&gt;</content><category term="post"></category><category term="Reviews"></category></entry></feed>