<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:marianposaceanu.com,2005:/feed</id>
  <link rel="alternate" type="text/html" href="https://marianposaceanu.com"/>
  <link rel="self" type="application/atom+xml" href="https://marianposaceanu.com/feed"/>
  <title>blog of Marian Posăceanu - rubyist most of the time</title>
  <updated>2026-03-30T09:08:04Z</updated>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/46</id>
    <published>2026-03-29T23:20:11Z</published>
    <updated>2026-03-30T09:08:04Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/semantic-search-in-rails-using-sqlite-vec-kamal-and-docker"/>
    <title>Semantic search in Rails using sqlite-vec, Kamal and Docker</title>
    <content type="html">&lt;p&gt;I’ve been reading a lot about search lately: kNN, BM25, RRF and related ideas, both within Elasticsearch and in isolation. With the advent of LLMs, especially open-weight models, plus modern hardware, it feels like a good time to approach some of this in a pragmatic way.&lt;/p&gt;

&lt;p&gt;On the blog I&amp;#39;m already using a form of BM25 via SQLite’s FTS5 &lt;a target="_blank" href="https://www.sqlite.org/fts5.html"&gt;extension &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which enables full text search, similar to the &lt;a target="_blank" href="https://github.com/Casecommons/pg_search"&gt;pg_search &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; gem which uses &lt;a target="_blank" href="https://www.postgresql.org/docs/current/textsearch.html"&gt;textsearch &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; on Postgres. &lt;/p&gt;

&lt;p&gt;What I wanted to do next was to experiment with embeddings and enable a form of semantic search too. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to try it out, it’s live at &lt;a target="_blank" href="https://marianposaceanu.com/semantic-search"&gt;/semantic-search &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;While searching around, I came across &lt;a target="_blank" href="https://alexgarcia.xyz"&gt;Alex Garcia&amp;#39;s &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; sqlite-vec￼. He describes it as “a no-dependency SQLite extension written entirely in C that runs everywhere”. That was appealing in itself since I did not want to introduce a separate client/server vector database for a small Rails app. The tradeoff is that it is brute-force search only, with “no ANN indexes (yet!)”, but at my scale that felt perfectly reasonable.&lt;/p&gt;

&lt;p&gt;The next step was to use a model from Hugging Face, create embeddings for all my articles then enable kNN search by using a local Docker container on my server.&lt;/p&gt;

&lt;h4&gt;Benchmarks&lt;/h4&gt;

&lt;p&gt;I tested: &lt;a target="_blank" href="https://huggingface.co/google/embeddinggemma-300m"&gt;EmbeddingGemma &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://huggingface.co/intfloat/multilingual-e5-large"&gt;Multilingual-E5-large &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and &lt;a target="_blank" href="https://huggingface.co/intfloat/multilingual-e5-base"&gt;*E5-base &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. As a benchmark, I used my own articles and evaluated the results manually with a sample size of one: myself.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------------------------------------------------------------------+
| MEMORY RATIO MAP (max ratio = 2.07x; lower is better)                    |
+--------------------------------------------------------------------------+
| embeddinggemma-300m / e5-base      1.16x  [###########.........]         |
| e5-large / e5-base                 2.07x  [####################]         |
| e5-large / embeddinggemma-300m     1.78x  [#################...]         |
|                                                                          |
| e5-base --x1.16--&amp;gt; embeddinggemma-300m --x1.78--&amp;gt; e5-large               |
| e5-base ------------------x2.07------------------&amp;gt; e5-large              |
+--------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Memory wise, *E5-large eats the most RSS memory (~4GB), E5-base is the lightest and EmbeddingGemma is right in the middle (~2GB). When it comes to actual results, Gemma returned similar results as *E5-large, at least according to my empirical results tested on my data. &lt;/p&gt;

&lt;p&gt;I ran some benchmarks on latency on my MBP M1 Pro. CPU run, cold cache (the model was loaded into memory on each run), one process: model load + one encode:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------------------------------------------------------------------+
| EMBEDDING MODEL BENCHMARKS (lower is better)                             |
+--------------------------------------------------------------------------+
| intfloat/multilingual-e5-base (768d)                                      |
|   mem:  1.95 GB  [############............]                              |
|   time: ~6.47s   [#####################...]                              |
|--------------------------------------------------------------------------|
| google/embeddinggemma-300m (768d)                                        |
|   mem:  2.26 GB  [##############..........]                              |
|   time: ~6.24s   [####################....]                              |
|--------------------------------------------------------------------------|
| intfloat/multilingual-e5-large (1024d)                                    |
|   mem:  4.03 GB  [########################]                              |
|   time: ~7.37s   [########################]                              |
+--------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I settled on Gemma because it was much lighter than *E5-large while giving similarly good results on my own data.&lt;/p&gt;

&lt;h4&gt;Rails implementation&lt;/h4&gt;

&lt;p&gt;In order to use this in my Rails blog, I decided the best approach would be to have a separate Docker image for a Gemma tiny service with two API endpoints: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;+--------------------------------------------------------------------------+&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;EMBEDDING&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;API&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;FLOW&lt;/span&gt;&lt;span class="w"&gt;                                                       &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;+--------------------------------------------------------------------------+&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;GET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/health&lt;/span&gt;&lt;span class="w"&gt;                                                              &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                                                      &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;v&lt;/span&gt;&lt;span class="w"&gt;                                                                      &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;OK&lt;/span&gt;&lt;span class="w"&gt;                                                                   &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                                                          &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/embed&lt;/span&gt;&lt;span class="w"&gt;                                                              &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"query"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text to embed"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;                                             &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;                                                                      &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;v&lt;/span&gt;&lt;span class="w"&gt;                                                                      &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;OK&lt;/span&gt;&lt;span class="w"&gt;                                                                   &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;                                                                        &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"embedding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;-0.02&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;                                       &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"dimensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                                                     &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"google/embeddinggemma-300m"&lt;/span&gt;&lt;span class="w"&gt;                                  &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;                                                                        &lt;/span&gt;&lt;span class="err"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;+--------------------------------------------------------------------------+&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This runs within the Docker kamal virtual network so it will be accessible by the Rails app. My VM has 6GB of RAM and two Ampere vCPUs i.e. RAM wise, it should fit with some headroom for the Rails app too.&lt;/p&gt;

&lt;p&gt;The current performance on the VM looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;When    Article ID  Operation   Source  Status  Duration    Dims    Bytes   Model   Error
2026-03-29 23:30:03 46  upsert  callback    success 3368ms  768 8819    google/embeddinggemma-300m  -
2026-03-29 23:28:12 46  upsert  callback    success 3428ms  768 8819    google/embeddinggemma-300m  -
...
etc.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------------------------------------------------------------------+
| EMBEDDING LATENCY HISTOGRAM (ms)                                         |
+--------------------------------------------------------------------------+
|   0-999    | [##......................]  2                               |
| 1000-1999  | [##################......] 18                               |
| 2000-2999  | [##########..............] 10                               |
| 3000-3999  | [###############.........] 15                               |
| 4000-4999  | [####....................]  4                               |
| 5000-6999  | [#.......................]  1                               |
+--------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The median response time for the API is ~2726 ms, RAM: ~2.5GB in use by both the embedding service and two Puma workers. Given this is a really low-spec VM, I&amp;#39;m quite happy with the experiment so far given there&amp;#39;s no cache at all in the hot path.&lt;/p&gt;

&lt;p&gt;On the app side, the implementation is fairly simple: add the &amp;quot;sqlite-vec&amp;quot; gem, a route for testing the kNN search &lt;a target="_blank" href="https://marianposaceanu.com/semantic-search"&gt;/semantic-search &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and a section in the admin panel where I can trigger the creation of embeddings for my articles.&lt;/p&gt;

&lt;p&gt;This is what the Rails flow looks like as an ASCII diagram:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Rails app                     Embedding service
(SemanticSearch + sqlite-vec) (embedding-inference:8765)
---------------------------   ----------------------------
INDEX / UPSERT FLOW
Admin regen
        |
        v
ArticleEmbedding#upsert_for(article)
        |
        +--&amp;gt; available? --no--&amp;gt; trace(skip/error); return false
        |
        +--&amp;gt; PassageBuilder.build_article(article)
        |
        +---------------------------&amp;gt; POST /embed {"query":"passage: ..."}
                                     |
                                     v
                         200 {"embedding":[...],"dimensions":768,"model":"..."}
        &amp;lt;----------------------------+
        |
        +--&amp;gt; validate dims == 768
        +--&amp;gt; delete old row + insert new row in article_embeddings
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And this is what happens when you click search on &lt;a target="_blank" href="https://marianposaceanu.com/semantic-search"&gt;/semantic-search &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /semantic-search?query=...
        |
        v
Searcher.search(query)
        |
        +--&amp;gt; PassageBuilder.build_query(query)
        +---------------------------&amp;gt; POST /embed {"query":"query: ..."}
        &amp;lt;---------------------------- 200 {"embedding":[...],"dimensions":768}
        +--&amp;gt; sqlite-vec KNN MATCH [query_vector], k = limit
        +--&amp;gt; join published articles, order by distance ASC
        v
Render ranked semantic results
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;The embedding service&lt;/h4&gt;

&lt;p&gt;The full source code for the embedding service can be found in my repo: &lt;a target="_blank" href="https://github.com/marianposaceanu/mp-com-embeddings"&gt;mp-com-embeddings &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It runs on Python/Torch and uses the sentence_transformers library, small excerpt from the POC:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;token&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;SentenceTransformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;local_files_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is still an early experiment, but it already feels like a practical complement to FTS5 rather than a replacement for it. &lt;/p&gt;

&lt;p&gt;BM25 remains great for exact terms and titles, while embeddings help when the wording drifts but the intent stays the same. Next up is a home-brewed RRF implementation, plus taking performance seriously this time.&lt;/p&gt;

&lt;h4&gt;Notes&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm"&gt;kNN &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - k-nearest neighbors, a way of finding the closest vectors to a given query embedding.&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.elastic.co/blog/practical-bm25-part-2-the-bm25-algorithm-and-its-variables"&gt;BM25 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - Best Matching 25, a ranking function commonly used in keyword-based full-text search.&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.elastic.co/docs/reference/elasticsearch/rest-apis/reciprocal-rank-fusion"&gt;RRF &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - Reciprocal Rank Fusion, a simple way to combine multiple ranked result sets, often used in hybrid search setups.&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/45</id>
    <published>2026-03-22T19:38:10Z</published>
    <updated>2026-03-25T07:34:28Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/kamal-rails-deployments-and-rega-turntables"/>
    <title>Kamal, Rails deployments, and Rega turntables</title>
    <content type="html">&lt;p&gt;Lately I&amp;#39;ve started to put back into a working state my turntable. It&amp;#39;s been disconnected from my main audio chain for about five years. The TT is a Rega &lt;a target="_blank" href="https://www.tonepublications.com/review/first-us-review-the-rega-rp3/"&gt;RP3 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; with a bog-standard Rega Carbon cartridge. Why now one might ask? It wasn&amp;#39;t my top priority and streaming Spotify to the &lt;a target="_blank" href="https://wiimhome.com/wiimmini/overview"&gt;WiiM Mini &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; then via a DAC directly into my NAD power amplifier was good enough.&lt;/p&gt;

&lt;p&gt;Yet, I started missing my records. I mean I still have them, I can still enjoy their tactility, art and smell, but not their sound. I missed the limits imposed by listening to vinyl: you have to carefully pick your album and then commit to listening to the whole thing, more or less. &lt;/p&gt;

&lt;p&gt;Anyway - after some cabling around, I managed to make it work and even better: improve its specs. It went from 33 1/3 rpm +0,57% to just +0,07%. The records sound great and I can now enjoy them in their full glory whilst following the rite of listening just like in Murakami&amp;#39;s &lt;a target="_blank" href="https://www.thebubble.org.uk/culture/music/haruki-murakami-and-the-literary-world-of-music/"&gt;books &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, what&amp;#39;s the connection with Kamal and Rails ? I&amp;#39;ve been using my blog to test out various Ruby/Rails changes, practically there&amp;#39;s no need to have a full CMS for something which you can write using HTML and a sprinkle of CSS and JavaScript. &lt;/p&gt;

&lt;p&gt;The crux of my blog is to experiment and enjoy programming in a restricted way. It&amp;#39;s a blog at the end of the day so it does need a CMS that means CRUD resources, feeds, SEO, partials, caching, public routing, etc. all within a dynamic language like Ruby and aided by Rails. &lt;/p&gt;

&lt;p&gt;As any Rails app it does need to be deployed somewhere and somehow. Back in the day I was using Capistrano to deploy to VMs from Linode, Hetzner et el. Then there was a large portion where I deployed to the Heroku free tier behind Cloudflare, but once that was gone I went back to VMs.&lt;/p&gt;

&lt;p&gt;Capistrano is considered &amp;quot;old school&amp;quot;, non-idempotent, etc. Nowadays, it still works but Rails now comes with a Docker file and direct support for Kamal: the Docker based deploy of choice for Rails apps (if you want to use it).&lt;/p&gt;

&lt;p&gt;The Rails app is quite small and the initial version of Kamal required a docker registry to be able to deploy to a VM. This wasn&amp;#39;t a big deal as you can create a private docker registry for free in many places including docker itself (this is what I used). Yet for a small app this was annoying for me since I now require an account which is permanently linked to my deploys.&lt;/p&gt;

&lt;p&gt;Thankfully as Kamal evolved the support for a local registry was added and this fixed the issue. What happens now is that Kamal starts a local registry container on your machine and forwards the remote server to it over SSH.&lt;/p&gt;

&lt;p&gt;This works pretty great in practice: my deploys are not dependent on an account anymore, however there is a small caveat: tunnelling via SSH can be slow. I have my app in an always free Oracle instance which means that on a fresh deploy when it has to download the 130MB+ of compressed docker image it is quite slow as it doesn&amp;#39;t go over 300KB/s. Small price to pay in my case, but keep this in mind if you want to bootstrap a start-up.&lt;/p&gt;

&lt;p&gt;This is probably a good mid-point to share my optimized &lt;a target="_blank" href="https://gist.github.com/marianposaceanu/a4be78582d9e0e6698f70ece225e0478"&gt;dockerfile &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax = docker/dockerfile:1&lt;/span&gt;
ARG &lt;span class="nv"&gt;RUBY_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4.0.1
FROM ruby:&lt;span class="nv"&gt;$RUBY_VERSION&lt;/span&gt;&lt;span class="nt"&gt;-slim&lt;/span&gt; AS base
WORKDIR /rails
ENV &lt;span class="nv"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt; &lt;span class="nv"&gt;BUNDLE_WITHOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"development:test"&lt;/span&gt; &lt;span class="nv"&gt;BUNDLE_DEPLOYMENT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;
RUN gem update &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--no-document&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; gem &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-N&lt;/span&gt; bundler

FROM base AS build

RUN apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; build-essential pkg-config libyaml-dev libsqlite3-dev &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists /var/cache/apt/archives

COPY &lt;span class="nt"&gt;--link&lt;/span&gt; Gemfile Gemfile.lock ./

ENV &lt;span class="nv"&gt;BUNDLE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bundle"&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bundle/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;BUNDLE_JOBS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4 &lt;span class="nv"&gt;BUNDLE_RETRY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3
RUN bundle &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.bundle/ &lt;span class="nv"&gt;$BUNDLE_PATH&lt;/span&gt;/ruby/&lt;span class="k"&gt;*&lt;/span&gt;/cache &lt;span class="nv"&gt;$BUNDLE_PATH&lt;/span&gt;/ruby/&lt;span class="k"&gt;*&lt;/span&gt;/bundler/gems/&lt;span class="k"&gt;*&lt;/span&gt;/.git

COPY &lt;span class="nt"&gt;--link&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;

RUN &lt;span class="nb"&gt;chmod&lt;/span&gt; +x bin/&lt;span class="k"&gt;*&lt;/span&gt;
RUN &lt;span class="nv"&gt;SECRET_KEY_BASE_DUMMY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 ./bin/rails assets:precompile

FROM base AS runtime

RUN apt-get update &lt;span class="nt"&gt;-qq&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libsqlite3-0 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists /var/cache/apt/archives

ENV &lt;span class="nv"&gt;BUNDLE_PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bundle"&lt;/span&gt; &lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/usr/local/bundle/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build /usr/local/bundle /usr/local/bundle
COPY &lt;span class="nt"&gt;--from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;build /rails /rails

RUN useradd rails &lt;span class="nt"&gt;--create-home&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; /bin/bash &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /data /rails/db /rails/tmp /rails/log /rails/public &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; rails:rails /rails/db /rails/tmp /rails/log /rails/public /data
USER rails:rails

ENV &lt;span class="nv"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sqlite3:///data/production.sqlite3"&lt;/span&gt; &lt;span class="nv"&gt;RUBY_YJIT_ENABLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1"&lt;/span&gt;

ENTRYPOINT &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/rails/bin/docker-entrypoint"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;

VOLUME /data
EXPOSE 80
CMD &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"bundle"&lt;/span&gt;, &lt;span class="s2"&gt;"exec"&lt;/span&gt;, &lt;span class="s2"&gt;"puma"&lt;/span&gt;, &lt;span class="s2"&gt;"-C"&lt;/span&gt;, &lt;span class="s2"&gt;"config/puma.rb"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have tried to reduce its size so that initial hit will be smaller plus I like to optimize things i.e. I still think the whole image at over 130MB is a bit too much.&lt;/p&gt;

&lt;p&gt;Going back to my Rega turntable, its cartridge the Rega Carbon it&amp;#39;s actually an Audio-Technica &lt;a target="_blank" href="https://phono-cartridge-reviews.co.uk/reviews/audio-technica-at3600l-at91/"&gt;AT3600L &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, a moving magnet one - which is decent but also the minimum when it comes to sound quality. I do want to upgrade it to something like an Ortofon or a &lt;a target="_blank" href="https://www.avstore.ro/doze-pick-up/nagaoka-mp-150/"&gt;Nagaoka MP-150/300 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and whilst they are pricier, the issue is that I have to adjust them properly after installing them.&lt;/p&gt;

&lt;p&gt;Rega recently created the &lt;a target="_blank" href="https://www.rega.co.uk/accessories/carbon-pro-upgrade-stylus"&gt;Carbon Pro &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which is a direct upgrade to the Carbon but without removing the whole cartridge just the needle itself and its housing. This is straightforward as it doesn&amp;#39;t require for me to go crazy and try to adjust a new cartridge from scratch.&lt;/p&gt;

&lt;p&gt;Similarly, Kamal requires docker and the simplest way on macOS is via Docker Desktop which is fine but also kind of an annoyance i.e. I want to get rid of it without dropping Kamal.&lt;/p&gt;

&lt;p&gt;Just as a refresher this is how Kamal works more or less:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local machine
  kamal CLI
    |-- uses Docker/Buildx to build images
    |-- may build locally, multi-arch, or with a remote builder depending on config
    |-- uses SSH to run Docker commands on servers
    |-- can run/use a local registry at localhost:5555

Remote servers
  Docker Engine
    |-- pulls image from registry
    |-- runs app/proxy/accessory containers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I can replace most of Docker Desktop by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;docker docker-buildx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But this does not give me a running Docker daemon here is where Colima jumps in, as it provides:&lt;br&gt;
- a Linux VM with Docker Engine (dockerd)&lt;br&gt;
- a Docker context/socket (docker context use colima)&lt;br&gt;
- runtime for Buildx builder containers&lt;br&gt;
- runtime for local registry container (localhost:5555 flow)&lt;/p&gt;

&lt;p&gt;A nuance to note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On some Colima setups, buildx may need to be installed or exposed as a Docker CLI plugin before docker buildx works properly. Also, if your local-registry setup needs special Docker daemon settings, such as insecure-registries or registry mirrors, Colima supports that too through its Docker configuration. In my case the simple setup was enough, but it is worth checking the Colima FAQ if your machine behaves differently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After I fully removed Docker Desktop from its own UI (this felt quite nice), all I had to do was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;colima start &lt;span class="nt"&gt;--arch&lt;/span&gt; aarch64 &lt;span class="nt"&gt;--vm-type&lt;/span&gt; vz &lt;span class="nt"&gt;--mount-type&lt;/span&gt; virtiofs &lt;span class="nt"&gt;--cpus&lt;/span&gt; 1 &lt;span class="nt"&gt;--memory&lt;/span&gt; 1
docker context use colima
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then verify that everything works correctly:  &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;colima status
docker context &lt;span class="nb"&gt;ls
&lt;/span&gt;docker context show
docker info
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; hello-world
docker buildx &lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And finally just ran &lt;code&gt;kamal deploy&lt;/code&gt; and it worked: fast deploys without Docker Desktop. This is just awesome and it finally feels like good old Capistrano without the drawbacks.&lt;/p&gt;

&lt;p&gt;Just like the Carbon Pro the Colima adds a container runtime without breaking Kamal whilst removing the icky initial Docker Desktop mammoth.&lt;/p&gt;

&lt;h4&gt;Credits&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://colima.run/docs/faq"&gt;Colima FQA &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://kamal-deploy.org/docs/configuration/docker-registry/"&gt;Kamal with local registry &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.oracle.com/cloud/free/"&gt;Oracle Free tier &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/capistrano/capistrano"&gt;Capistrano &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/2</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-03-08T15:52:54Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-zygohistomorphic-prepromorphisms"/>
    <title>On zygohistomorphic prepromorphisms</title>
    <content type="html">&lt;p&gt;There are words in programming that look like jokes. &lt;em&gt;Zygohistomorphic prepromorphism&lt;/em&gt; is one of them.&lt;/p&gt;

&lt;p&gt;The first time you encounter it, you assume one of two things must have happened. Either someone lost a bet, or category theorists were left unsupervised for too long. Both explanations feel plausible.&lt;/p&gt;

&lt;p&gt;And yet the term survived. It appears in libraries. It appears on the Haskell wiki. People occasionally implement it, usually after an evening spent exploring recursion schemes and slowly realizing that the rabbit hole goes deeper than expected.&lt;/p&gt;

&lt;p&gt;What is surprising is that behind the intimidating name there is actually a relatively simple idea. The difficulty is not the concept itself, but the number of layers you have to peel away before it becomes visible.&lt;/p&gt;

&lt;h4&gt;Recursion, but with memory&lt;/h4&gt;

&lt;p&gt;Most recursive programs share a common shape. You start with some structure, reduce it step by step, and eventually collapse it into a value. Lists become numbers, trees become summaries, syntax structures become evaluations.&lt;/p&gt;

&lt;p&gt;In the functional programming world this pattern has a precise name: a &lt;strong&gt;catamorphism&lt;/strong&gt;. In more familiar terms, it is simply a fold.&lt;/p&gt;

&lt;p&gt;A fold walks over a structure and accumulates a result as it goes. It works for lists, trees, and many other recursive data types. The details change depending on the structure, but the pattern remains the same: descend into the structure, combine results on the way back up.&lt;/p&gt;

&lt;p&gt;For many problems this is perfectly sufficient. But not all recursive computations are so cooperative.&lt;/p&gt;

&lt;p&gt;Sometimes the recursive step needs more information than just the results of its children. It might need access to intermediate computations that happened earlier in the traversal. Or it might need to look at the structure in a slightly different form before the recursion even begins. Once those requirements appear, the simple fold starts to feel a little too simple.&lt;/p&gt;

&lt;h4&gt;Adding just one more layer&lt;/h4&gt;

&lt;p&gt;Programmers have a well-known habit when something almost works. They add one more layer.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;histomorphism&lt;/strong&gt; extends a fold by allowing the recursion to remember its own history. Instead of seeing only the immediate results of recursive calls, the computation can also access a record of previously computed values. A &lt;strong&gt;zygomorphism&lt;/strong&gt; allows two recursive computations to run together. One traversal produces information that the other one can consume, effectively letting two analyses cooperate as they walk the same structure. A &lt;strong&gt;prepromorphism&lt;/strong&gt; adds another capability: the ability to transform the structure before recursion proceeds. The data is reshaped first, and only then does the recursive process begin.&lt;/p&gt;

&lt;p&gt;Individually, each of these ideas solves a small limitation of a plain fold. They are incremental extensions that add a bit more context to the recursion. Combine them, however, and the name becomes a small monster:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;zygo + histo + prepro + morphism&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which gives us the formidable &lt;strong&gt;zygohistomorphic prepromorphism&lt;/strong&gt;. It sounds like something a doctor might diagnose. In practice, it is simply a recursion scheme that carries additional context while traversing a structure.&lt;/p&gt;

&lt;h4&gt;Why anyone would do this&lt;/h4&gt;

&lt;p&gt;At this point a reasonable question usually appears. Why not just write the recursion directly? Most of the time, that is exactly what you should do.&lt;/p&gt;

&lt;p&gt;Recursion schemes are not about saving keystrokes. They are about separating concerns. Instead of intertwining traversal logic with the computation itself, they isolate the mechanics of walking a structure from the operations performed on that structure. Traversal becomes reusable. Computation becomes composable.&lt;/p&gt;

&lt;p&gt;This separation becomes valuable when you begin running multiple analyses over the same recursive data structure, or when different transformations need to cooperate during traversal. In those cases the abstraction stops looking excessive and starts looking convenient.&lt;/p&gt;

&lt;p&gt;Until you reach that point, though, the name alone can make it feel like something that escaped from a biology textbook.&lt;/p&gt;

&lt;h4&gt;The cost of names&lt;/h4&gt;

&lt;p&gt;Names matter more than we often admit. Some abstractions fail because they are genuinely too complex. Others fail because their names discourage anyone from getting close enough to understand them. Zygohistomorphic prepromorphism suffers a little from both.&lt;/p&gt;

&lt;p&gt;The name compresses an entire lineage of ideas: zygomorphisms, histomorphisms, prepromorphisms. Each term has its own precise meaning within the taxonomy of recursion schemes. Combined, however, they form something that reads less like a programming concept and more like an academic prank. Which is unfortunate.&lt;/p&gt;

&lt;p&gt;Because the underlying idea is actually quite gentle. Sometimes recursion needs memory. Sometimes it needs cooperation between multiple computations. Sometimes it needs a transformation before the recursion even starts. That is all the abstraction is trying to express.&lt;/p&gt;

&lt;h4&gt;A small observation&lt;/h4&gt;

&lt;p&gt;Programming languages accumulate complexity in two distinct ways. One kind of complexity comes from the problems we are trying to solve. Real-world systems are messy, and the abstractions we build inevitably reflect that messiness.&lt;/p&gt;

&lt;p&gt;The other kind of complexity comes from the vocabulary we invent while trying to describe those abstractions. Occasionally the vocabulary grows faster than the underlying ideas. Zygohistomorphic prepromorphisms sit right at that boundary. They are both an elegant generalization of recursive structure traversal and a reminder that abstraction, if left unattended, tends to produce very long names.&lt;/p&gt;

&lt;p&gt;Perhaps that is fitting. After all, the idea itself is not particularly complicated. Only the word is.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/44</id>
    <published>2025-07-06T14:40:06Z</published>
    <updated>2025-07-06T14:43:50Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/the-analogue-programmer-in-the-land-of-llms"/>
    <title>The analogue programmer in the land of LLMs</title>
    <content type="html">&lt;p&gt;It&amp;#39;s been a while since I&amp;#39;ve posted, for obvious reasons a lot has changed in the realm of programming.&lt;/p&gt;

&lt;p&gt;LLMs, &amp;quot;agentic&amp;quot; coding et al. I&amp;#39;ve started using them almost from day one, when they were pretty bad and they could barely generate some readable text in their images.&lt;/p&gt;

&lt;p&gt;When it comes to coding, being in my late 30s, I&amp;#39;m clearly what I&amp;#39;d call an &amp;quot;old-school&amp;quot; programmer: I&amp;#39;m using vim with a couple of plug-ins, a fast terminal emulator, minimalistic Docker, brew on MacOS i.e. nothing fancy, not even using IDEs.&lt;/p&gt;

&lt;p&gt;I like my dot-files, I love the idea of slowly improving them, it&amp;#39;s a nice hobby that keeps me anchored and reminds me to keep learning new things.   This is just to a bit of context in order to make my bias clear when it comes to LLMs and using them.&lt;/p&gt;

&lt;p&gt;My main gripe with using LLMs for coding is that they incur a cognitive debt and no, I&amp;#39;m not quoting all the studies around this problem (nothing wrong with them), this purely from my empirical point of view.&lt;/p&gt;

&lt;p&gt;For example, I was cleaning up my cloud storage, which is a mess of lots of tiny files moved from different cloud storages: Google Drive to Dropbox to iCloud. Over the years this generated a lot of duplicate files i.e. bad syncs, the infamous &lt;code&gt;file_name { 2}.jpg&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt; As  I usually do, I apply the brute force approach and started cleaning them up one by one - this got tedious fast so I was ok, hello generic-LLM, please write a Ruby script to remove these kind of duplicates. The generated code looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env ruby&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'optparse'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'fileutils'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'find'&lt;/span&gt;

&lt;span class="c1"&gt;# CLI options&lt;/span&gt;
&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="no"&gt;OptionParser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;banner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Usage: ruby dedup.rb [options] /path/to/dir"&lt;/span&gt;

  &lt;span class="n"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--delete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Delete duplicate files"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse!&lt;/span&gt;

&lt;span class="c1"&gt;# Directory check&lt;/span&gt;
&lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="no"&gt;Dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exist?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Please provide a valid directory path."&lt;/span&gt;
  &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Helper to detect duplicates in "filename 2.ext" style&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;find_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files_by_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;duplicates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="n"&gt;files_by_dir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^(.*?)( \d+)(\.[^.]+)$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;

      &lt;span class="n"&gt;original_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="n"&gt;duplicate_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;

      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;duplicates&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_name&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duplicate_name&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;duplicates&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Helper to format size nicely&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;human_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;units&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'B'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'KB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'MB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'GB'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'TB'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'0 B'&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt;
  &lt;span class="s2"&gt;"%.2f %s"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_f&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;units&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Recursively collect all files by directory&lt;/span&gt;
&lt;span class="n"&gt;files_by_dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;h&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;next&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;files_by_dir&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Find duplicates&lt;/span&gt;
&lt;span class="n"&gt;duplicate_pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;find_duplicates&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;files_by_dir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Track total size&lt;/span&gt;
&lt;span class="n"&gt;total_duplicate_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

&lt;span class="c1"&gt;# Compare sizes and act&lt;/span&gt;
&lt;span class="n"&gt;duplicate_pairs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;orig_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orig_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;total_duplicate_size&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;

    &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Match: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;orig_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;=&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;human_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:delete&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"  Deleted: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;dup_path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Final total&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Total duplicate size: &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;human_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_duplicate_size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&amp;#39;s pretty meh from many points of views, for example as I love ActiveSupport, the generated method &lt;code&gt;human_size(bytes)&lt;/code&gt; is just horrid. I could have generated this using standard *nix tools + some bash, but I went with Ruby as it is much more familiar and easier to grok for me.&lt;/p&gt;

&lt;p&gt;The script was generated in around 15.minutes ? a couple of promptly iterations - now, if I were to write this from scratch, knowing myself - this would had been a small gem and I&amp;#39;d probably would have spend some time getting the recursive directory checks right.&lt;/p&gt;

&lt;p&gt;At the end of the day - I created something useful for myself, which was rather easy to check since it was &amp;quot;coded&amp;quot; in Ruby. If I were to write this from scratch this experience would have been completely different.&lt;/p&gt;

&lt;p&gt;There&amp;#39;s also a cognitive dissonance about this: why didn&amp;#39;t I just write it myself ? it would have been fun, I just wanted to save some time, but at what cost?&lt;/p&gt;

&lt;p&gt;This is why the whole LLM human assisted code generation is not a full boon - it&amp;#39;s like cheating your brain, not learning anything, and it also leaves you in a state of dissonance. &lt;/p&gt;

&lt;p&gt;Are LLMs going away ? No. I will stop using them ? No. I just need to fix my cognitive dissonance when it comes to their use and in the process, avoid rotting my brain, after all, people will start calling me soon enough an &amp;quot;analogue programmer&amp;quot;.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/43</id>
    <published>2025-07-06T08:38:30Z</published>
    <updated>2025-07-06T08:38:30Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/moving-from-iterm2-to-kitty-for-simplicity-and-performance"/>
    <title>Moving from iTerm2 to Kitty for simplicity and performance</title>
    <content type="html">&lt;p&gt;I love iTerm2, but it can be sluggish sometimes on my old late 2016 MacBook Pro. Plus, many of its features I never use. I had tried Alacritty before, but making it look and act like iTerm2 was a no-go. This time, I tried Kitty, and within two hours of work, it looked and behaved just like my iTerm2 configuration. This exploration was fueled by a &lt;a target="_blank" href="https://mitchellh.com/writing/ghostty-devlog-006"&gt;performance comparison &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; I came across that piqued my interest.&lt;/p&gt;

&lt;p&gt;To really put Kitty to the test, I used my older MacBook Pro, deliberately looking for CPU usage spikes via fan noise and Activity Monitor. My &amp;quot;benchmark&amp;quot; was simple: opening an 800-line Rails schema and scrolling through it using Vim&amp;#39;s &lt;code&gt;Shift + ]&lt;/code&gt;. Unlike iTerm2, which often caused my fans to ramp up, Kitty handled the same tasks with significantly less strain on the CPU.&lt;/p&gt;

&lt;p&gt;I quickly realized that syntax highlighting was a major bottleneck. To address this, I switched to &lt;a target="_blank" href="https://github.com/sheerun/vim-polyglot"&gt;vim-polyglot &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; for faster Ruby syntax highlighting. The improvement was modest but noticeable. In a more unrealistic benchmark where I scrolled through the file quickly, latency was significantly reduced, and no more dropped frames were noticeable.&lt;/p&gt;

&lt;h4&gt;Configuration tweaks&lt;/h4&gt;

&lt;p&gt;To further optimize Kitty, I made several configuration tweaks:&lt;br&gt;
- Integrated it into my &lt;a target="_blank" href="https://github.com/marianposaceanu/dot-files/tree/master?tab=readme-ov-file#add-a-new-submodule"&gt;dot-files as a git submodule &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;br&gt;
- Set the &lt;code&gt;text_composition_strategy&lt;/code&gt; to &lt;code&gt;legacy&lt;/code&gt; to emulate the thin strokes from iTerm2. You can find the setting &lt;a target="_blank" href="https://sw.kovidgoyal.net/kitty/conf/#opt-kitty.text_composition_strategy"&gt;here &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;br&gt;
- Used the Monokai Pro theme from &lt;a target="_blank" href="https://github.com/dexpota/kitty-themes"&gt;kitty-themes &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; for a familiar and visually appealing look.&lt;br&gt;
- Kitty leveraging vector CPU instructions, &lt;a target="_blank" href="https://github.com/kovidgoyal/kitty/issues/7005"&gt;enhancing its overall performance &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;My Kitty configuration explained&lt;/h4&gt;

&lt;p&gt;Here are some specific Kitty configurations I used and what they do:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim:fileencoding=utf-8:foldmethod=marker
# Ensures that the configuration file is encoded in UTF-8 and uses marker-based folding.

include ./theme.conf
# Includes an external theme configuration file for consistent theming.
font_family Berkeley Mono Variable
# Sets the font to Berkeley Mono Variable.
font_size 16.0
# Sets the font size to 16.0 points.
text_composition_strategy legacy
# Uses the legacy strategy for text composition, emulating thin strokes similar to iTerm2.

enable_audio_bell no
# Disables the audio bell to prevent sound alerts.
visual_bell_duration 0.0
# Sets the visual bell duration to zero, effectively disabling it.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Comparing the results: &lt;a target="_blank" href="https://raw.githubusercontent.com/marianposaceanu/dot-files/master/assets/comparison.jpg"&gt;iterm2 vs kitty &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If Kitty performs well under these stressful and unrealistic contexts, it will certainly be even faster on my M1 Pro MacBook Pro. Plus, the switch was a fun and rewarding project. Kitty not only matches the appearance and functionality of iTerm2 but also offers a performance boost that makes my old MacBook Pro feel more responsive and efficient.&lt;/p&gt;

&lt;p&gt;Overall, I highly recommend giving Kitty a try if you&amp;#39;re looking for a terminal emulator that combines simplicity and performance.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/42</id>
    <published>2025-07-06T08:37:13Z</published>
    <updated>2025-07-06T08:37:13Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/ruby-meets-lisp-unveiling-alien-tech-at-friendly-rb-s-lightning-talk"/>
    <title>Ruby meets LISP: Unveiling alien tech at Friendly.rb's lightning talk</title>
    <content type="html">&lt;p&gt;Recently at the &lt;a target="_blank" href="https://friendlyrb.com"&gt;Friendly.rb &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; conference in Romania, I explored an experimental blend of Ruby and LISP, particularly through Clojure. This journey was more about embracing the spirit of experimentation intrinsic to Ruby, rather than direct comparison.&lt;/p&gt;

&lt;p&gt;A big shoutout to &lt;a target="_blank" href="https://twitter.com/jcsrb"&gt;Jakob &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, whose suggestion that I do a lightning talk was a catalyst for my participation. His encouragement led me to share my explorations with fellow Ruby enthusiasts.&lt;/p&gt;

&lt;p&gt;One of my personal &lt;a target="_blank" href="https://twitter.com/dakull/status/1706975839324094922"&gt;highlights &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; at the conference was &lt;a target="_blank" href="https://twitter.com/NaijeriaToweett"&gt;Naijeria Toweett&amp;#39;s &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; talk, &amp;quot;React-ing to Rails: Why Ruby on Rails is the Best Stop for Web Developers.&amp;quot; Her transition into tech as a mother and a problem solver was not only inspiring but also infused with humor and warmth, bringing a delightful human touch to the technical world. For those curious to experience her engaging storytelling and insightful journey with Ruby on Rails, Naijeria&amp;#39;s full presentation is available &lt;a target="_blank" href="https://youtu.be/SasYDQwtdr0?si=u1-JlNmtCGMrYDTJ"&gt;online &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In my presentation, I delved into Ruby&amp;#39;s playful side by contrasting its syntax with LISP&amp;#39;s features. A pivotal aspect of my talk was utilizing JRuby to integrate Ruby on Rails with Clojure, demonstrating Ruby&amp;#39;s creative possibilities. This integration owes much to &lt;a target="_blank" href="https://twitter.com/headius"&gt;Charles Oliver Nutter &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, the maintainer of JRuby. His work in maintaining this crucial bridge between Ruby and Java ecosystems has been indispensable for the Ruby community. You can watch my talk &lt;a target="_blank" href="https://www.youtube.com/watch?v=eOmF9SnIG5A&amp;amp;t=1s"&gt;here &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;And here it is: &lt;code&gt;app/views/articles/index.html.clj&lt;/code&gt; in all its glory:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(html
  [:main
    [:section {:id "ruby-lisp-alien-tech"}
      [:h2 "Ruby &amp;amp; the LISP Alien Tech"]
      [:p "When we talk about the world of programming languages, two stand out in their unique ways: Ruby, "
          "with its elegant syntax and Rails powerhouse, and LISP, the ancient language known for its "
          "code-as-data philosophy. The connection? Both embody the spirit of making the impossible possible. "
          "Join us as we delve into the mysteries of Ruby and LISP and uncover the alien tech within."]
      [:img {:src "https://www.lisperati.com/lisplogo_warning2_256.png", :alt "LISP Alien Tech Illustration"}]]

    [:section {:id "s-expressions"}
      [:h2 "What are S-expressions?"]
      [:p "S-expressions, or symbolic expressions, are a fundamental part of the LISP (LISt Processing) "
          "programming language. They are used to represent both data and code. An S-expression can be an "
          "atom or a list. At its core, an S-expression is a way to represent nested list data in a uniform "
          "manner."]
      [:figure
        [:img {:src "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVUA", :alt "S-expression diagram"}]
        [:figcaption "A visual representation of S-expressions."]]]])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The focus was on Clojure&amp;#39;s Hiccup library to dynamically render HTML in a Rails application. This required creating a custom ActionView::Template handler in Rails to process Clojure&amp;#39;s code. More than a technical exercise, this project was about exploring the contrasts between OOP and functional paradigms, particularly in Clojure. It was purely an exploration, having no practical purpose other than demonstrating Ruby&amp;#39;s flexibility and encouraging paradigmatic curiosity.&lt;/p&gt;

&lt;p&gt;As I’m already revving up for next year’s Friendly.rb conference, it’s not just the anticipation that’s buzzing — it&amp;#39;s the reminder of how electrifying the Ruby community truly is. These events are like the ultimate sandbox for grown-up tech enthusiasts: a place to share, learn, experiment, and sometimes, just revel in the camaraderie of like-minded tinkerers. &lt;/p&gt;

&lt;p&gt;Bonus: if you&amp;#39;re already marking your calendar and planning your tech adventures, you should know that &lt;a target="_blank" href="https://www.tickettailor.com/events/friendlyrb/1031276"&gt;tickets &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; are already on sale.&lt;/p&gt;

&lt;p&gt;I extend my kudos to the Friendly.rb team - &lt;a target="_blank" href="https://twitter.com/adrianthedev"&gt;Adrian &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://twitter.com/lucianghinda"&gt;Lucian &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://twitter.com/jcsrb"&gt;Jakob &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://twitter.com/_alexmarinescu"&gt;Alex &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, and &lt;a target="_blank" href="https://twitter.com/stefanbc"&gt;Stefan &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - for creating a platform where learning and community can thrive.&lt;/p&gt;

&lt;h3&gt;Resources&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Source code repository: &lt;a target="_blank" href="https://github.com/marianposaceanu/weird_ruby"&gt;Weird Ruby on GitHub &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Learn about Clojure: &lt;a target="_blank" href="https://clojure.org/"&gt;Clojure Official Site &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Hiccup library: &lt;a target="_blank" href="https://github.com/weavejester/hiccup"&gt;Hiccup on GitHub &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;For Clojure &amp;amp; Leiningen: &lt;a target="_blank" href="https://leiningen.org/"&gt;Leiningen &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;About JRuby: &lt;a target="_blank" href="https://www.jruby.org/"&gt;JRuby Official Site &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/41</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/harmonizing-projects-navigating-the-graph-of-estimation-and-detail"/>
    <title>Harmonizing Projects: Navigating the Graph of Estimation and Detail</title>
    <content type="html">&lt;p&gt;As someone who finds solace in the rich nuances of sound, I often draw parallels to the world of development. Ponder this: Just as the quality of music affects the depths of my appreciation, the broad overview of a project offers a satisfying clarity. Yet, the devil, as they say, is in the details.&lt;/p&gt;

&lt;p&gt;In much the same way I lose myself in the symphonies of sound, developers often journey through the intricate web of a project, visualized as a vast graph. At the heart of this graph lies the core objective or goal, the resonating note that sets the tone. As we journey outwards, moving towards the peripheries, the graph branches out, much like the intricate harmonies and melodies in a musical piece.&lt;/p&gt;

&lt;p&gt;And then, there are the leaf nodes, the finer details. Just as the subtlest notes in music might escape the casual listener, these granular elements of a project often elude our initial estimations. The further we delve into these leaves, the more we&amp;#39;re confronted with the unexpected — the unforeseen challenges, the complexities that weren&amp;#39;t apparent from a distance. It&amp;#39;s akin to the ambient noise that permeates through my open-back headphones, often distracting and always unpredictable.&lt;/p&gt;

&lt;p&gt;In the past months, I&amp;#39;ve found myself gravitating towards noise-cancelling headphones, a testament to the need for focused immersion. Similarly, as developers, our approach to project estimation needs that metaphorical noise-cancellation. Recognizing the uncertainties that lurk in the granular tasks, adjusting our approach, and continuously recalibrating our strategies becomes paramount. After all, in both music and development, it&amp;#39;s about harmonizing the broad strokes with the intricate details, creating a masterpiece that resonates with purpose.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/40</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/don-t-stress-use-the-right-gear-for-the-job"/>
    <title>Don’t stress, use the right gear for the job</title>
    <content type="html">&lt;p&gt;Whilst I consider myself an audiophile, I also like to think I’m a pragmatist i.e. best of both worlds: don’t go over the top with snake oil gear but at the same time recognise that sound quality plays a pivotal role on how I listen and enjoy music.&lt;/p&gt;

&lt;p&gt;As developers, we use various ways to relax, in my case music plays a vital role - it decouples me from the “now”, triggers the creation of happy chems in the brain and finally re-syncs my contorted perception towards “beauty can exist in the world whatever the circumstances might be”.&lt;/p&gt;

&lt;p&gt;In the last couple of months, a new requirement has risen: noise cancelling headphones, as the surrounding noise was having a negative effect on my productivity. I mostly use open back headphones, actually all of them are open back so they let outside noise easily in.&lt;/p&gt;

&lt;p&gt;I addressed the finding of the perfect pair of noise cancelling headphones by also factoring in their sound quality. After some weeks a candidate emerged: Sony WH-1000XM4, the fourth iteration of their NC cans - the reviews and online consensus revealed top of the notch NC and more than OK sound quality.&lt;/p&gt;

&lt;p&gt;To digress a bit, my reference cans are the old timers from Sennheiser: the venerable HD600s released in 1997 with their odd coating. People affectionately call them “kitchen”-cans. Luckily, I got the original looking ones from ‘97. The updated design looks in-sync with the modern times. Before them I tried other headphones: I went from Grados (SR225e) to Audeze (LCD-2f), which I still own but rarely use, also listened to a myriad of other models. &lt;/p&gt;

&lt;p&gt;For me, the 600s timber, especially in the vocals department, doesn’t have any competition. They’re just magic if you will, just like the classic BBC monitors, nowadays best incarnated as the Harbeth P3ESR - they’re not perfect speakers by any means, but their timbre and reproduction of vocals are just magic.&lt;/p&gt;

&lt;p&gt;Back to the Sony’s - bought them, the NC feature worked like a charm but I had a major disappointment in the sound quality department - out of the box they come tuned with a huge bass bump which bleeds into the mids and makes everything sound… well, muddy. &lt;/p&gt;

&lt;p&gt;I spent again a week researching different EQ settings, various fixes to make them sound better. Finally, I used &lt;a target="_blank" href="https://rogueamoeba.com/soundsource/"&gt;SoundSource &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; coupled with Apple’s default AUNBandEQ (parametric EQ) to apply fixes I’ve found &lt;a target="_blank" href="https://www.reddit.com/r/oratory1990/comments/kek3sn/sony_wh1000xm4_eq_preset_check/"&gt;online &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. The results - quite impressive, the sound was much better, but it wasn’t perfect, most likely because there are many factors that influence each individual frequency perception like the shape of the ear, the length of the ear canal, the inherent subjectivity of the brain, etc. &lt;/p&gt;

&lt;p&gt;This was a failed experiment. I truly believe you can EQ your headphones to fit your ears; the issue is the amount of energy you have to put into this and there’s always seemed to be that one track where a cymbal just sounds off or too edgy.&lt;/p&gt;

&lt;p&gt;Therefore, I caved. I just set the “Vocal” profile in their DSP, which doesn’t sound horrible, and went on with my business, ignoring the issue for some time but still having a feeling that they aren’t good enough somehow.&lt;/p&gt;

&lt;p&gt;Then it downed on me: I got them for a specific role - noise cancellation i.e. not to trick my brain that I’m listening to magic. When thinking in that specific structure, the dissent went away, because when I wanted magic, I could always switch to my 600s coupled with the Chord Mojo or the Burson Soloist brute and a &lt;a target="_blank" href="https://ifi-audio.com/products/micro-itube2/"&gt;tube buffer &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, all mixed with a quiet night: just sit back and enjoy the music. &lt;/p&gt;

&lt;p&gt;As in programming and many other fields: use the right gear for the job and appreciate things for what they excel at.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/39</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/hierarchy-of-perceptions"/>
    <title>Hierarchy of perceptions</title>
    <content type="html">&lt;p&gt;I was thinking of the old conundrum of how we perceive the objective world or reality - it’s always through our limited sensory organs. Then is has to be processed: some small latency in the brain, maybe some evolutionary heuristics, our genetic background, etc. after all this we get a tainted fragment the world.&lt;/p&gt;

&lt;p&gt;We know these facts: we aren’t getting a full picture, i.e., our perception is limited. We can have levels of perception which depend on various factors, some of which are internal (currently researched and refined in the fields of psychology &amp;amp; neuroscience) and others external. As a species, we have cultivated them, I’d reckon, starting with the first human tribes.&lt;/p&gt;

&lt;p&gt;Society is both a necessity and a natural extension of the late humans. The interesting detail is society&amp;#39;s effects on our perceptions or reality. I keep using perception at plural because I’m making the case there’s a multiplicity of them (maybe I’m channelling Deleuze here). What I want to add on top of the plurality of perception is its hierarchical property in relation to the immovable real world.&lt;/p&gt;

&lt;p&gt;Why do I believe we can switch the perception of the world? Is self-evident, but this property forces it to move under our radars. We box ourselves into a certain instance of reality through a specific perception. &lt;/p&gt;

&lt;p&gt;Let’s say, age wise, we are in our thirties and our carrier takes center stage - this becomes the instance of how we perceive the world. Thus, our prima perception is bound to this context and the other instances take a second place. Other instances? we can think of items that ooze meaning in our lives, e.g. our loved ones, passions, hobbies, etc.&lt;/p&gt;

&lt;p&gt;This creates a hierarchy of perceptions which, if we’re not aware of it, we can designate as the most important the “wrong” instance of reality. These hierarchies are subjective, but to what degree? What if we get stuck in a perception bubble and we forget or ignore our health? - in the future we might experience the perception of being ill to a higher degree and, sadly, get stuck in it longer or for the rest of our life.&lt;/p&gt;

&lt;p&gt;We can sketch this out at a macro and micro-level. The hierarchy of perceptions guides our choices to a certain extent in different periods of our lives (macro). But at any point there’s a multiplicity of perceptions of the same position of reality which unified unto infinity would give us a full picture.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/38</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/tips-macos-save-screenshots-as-jpeg-in-a-different-location"/>
    <title>Tips: macOS save screenshots as JPEG in a different location</title>
    <content type="html">&lt;p&gt;I&amp;#39;m not really sure why taking screenshots in macOS defaults to PNG file format. I just googled this and the &amp;quot;answer&amp;quot; was:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MacOS captures screenshots using PNG as the default image format, which provides lossless compression and preserves image quality while creating relatively small files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;OK - I can understand somewhat the logic behind this - &amp;quot;we&amp;#39;re a premium brand, even the screenshots should be premium, jokes aside, whilst I agree with the part about the format being lossless I&amp;#39;m surely (and don&amp;#39;t call me Shirley!) that the part about small files is non-sense. &lt;/p&gt;

&lt;p&gt;PNGs, especially when using the &amp;quot;Retina&amp;quot;-mode HiDPI, create large file as the format default compression engine is DEFLATE which is OK but the problem is not the algorithm itself, the issue is that is has to deal with a large set of data (lossless compression i.e. not one pixel can be lost). When using a 4K display in 1080p mode - that translates to really big files (usually a couple of megs). &lt;/p&gt;

&lt;p&gt;Why is this an issue for me? I keep all my screenshots and sync them in iCloud. It be rather useful to have them - assign dates to images over large periods of time and you can reconstruct a bit of the past e.g. &amp;quot;What was I doing that day? Oh, I was making a meme about moths&amp;quot;. &lt;/p&gt;

&lt;p&gt;But I digress, I always forget how the command to change the default image format to jpeg and as far as I know still there&amp;#39;s is no global UI way to set it - therefore here goes, the &lt;em&gt;magic&lt;/em&gt; commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;defaults write com.apple.screencapture type jpg
killall SystemUIServer
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The first line is easy to decipher as for the second one the only question is what SystemUIServer is - well, according to its man page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SystemUIServer(8)            System Manager's Manual           SystemUIServer(8)

NAME
     SystemUIServer – Presents status items in the upper right of menu bar

DESCRIPTION
     SystemUIServer is a system service which presents status items in your menu
     bar.

     There are no options for SystemUIServer, and users should not run
     SystemUIServer manually.

macOS                             July 4, 2018                             macOS
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The cherry on the top? How to set the location of your newly saved JPEG screenshots: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Command + Shift + 5&lt;/code&gt; =&amp;gt; there&amp;#39;s an UI at the bottom where you can click &amp;quot;Options&amp;quot; and select the new folder where the screenshots will be saved.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/37</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/interesting-links-is-logging-a-code-smell"/>
    <title>Interesting links - Is logging a "code smell" ?</title>
    <content type="html">&lt;p&gt;I was reading this &lt;a target="_blank" href="https://dave.autonoma.ca/blog/2022/01/08/logging-code-smell/"&gt;article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; it raises quite an unexpected question - &amp;quot;Can we consider logging as a code smell&amp;quot;?&lt;/p&gt;

&lt;p&gt;Clearly - the answer is more nuanced than that and whilst I do enjoy the &lt;code&gt;event bus&lt;/code&gt; approach from the article - that seems like overkill for most applications.&lt;/p&gt;

&lt;p&gt;It&amp;#39;s interesting that Aspect Oriented Programming is mentioned but not in a positive light and I resonate with that to a certain extent - especially in Java but in Ruby world this is quite easy to implement - actually no one even calls AOP (most likely because is not in the pure definition of it) it&amp;#39;s just neat &lt;em&gt;little&lt;/em&gt; feature of &lt;code&gt;ActiveSupport&lt;/code&gt; : more precisely &lt;code&gt;ActiveSupport::Notifications&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To me that&amp;#39;s quite similar to the implementation in the original article - it just comes as under the &amp;quot;instrumentation API for Ruby&amp;quot; moniker.&lt;/p&gt;

&lt;p&gt;A simple example would look like:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instrument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'some_action_that_requires_instrumentation'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;run_the_action&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In some other part of our logic - preferably in config file we can subscribe to those events:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Notifications&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;monotonic_subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'render'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

  &lt;span class="c1"&gt;# According to the docs we get a handy set of meta information:&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# name         =&amp;gt; String, name of the event (such as 'render' from above)&lt;/span&gt;
  &lt;span class="c1"&gt;# start          =&amp;gt; Monotonic time, when the instrumented block started execution&lt;/span&gt;
  &lt;span class="c1"&gt;# finish        =&amp;gt; Monotonic time, when the instrumented block ended execution&lt;/span&gt;
  &lt;span class="c1"&gt;# id              =&amp;gt; String, unique ID for the instrumenter that fired the event&lt;/span&gt;
  &lt;span class="c1"&gt;# payload    =&amp;gt; Hash, the payload&lt;/span&gt;

  &lt;span class="c1"&gt;# here we could implement our logging and instrumentation (Librato &amp;amp; friends)&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Credits&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://dave.autonoma.ca/blog/2022/01/08/logging-code-smell"&gt;Original article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://api.rubyonrails.org/classes/ActiveSupport/Notifications.html"&gt;ActiveSupport docs &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/36</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/multiple-data-stores-and-eventual-consistency-using-micro-services"/>
    <title>Multiple data stores and eventual consistency using micro-services</title>
    <content type="html">&lt;p&gt;There are cases where there isn&amp;#39;t a single source of truth when it comes to the data that a fleet of micro-services consume. The data is thus sparse across multiple storage solutions, each designed to solve a specific problem using a sub-sets of it. This can be either mirrored via syncing to a specific format (think for example of a graph database) or a set of specific features that exist only in another store (think of a relational database with some custom metadata) all derived from an &amp;quot;M:N&amp;quot; data store (this can be anything from a relational database, a document one or even a set of resources exposed as an API, all mixed together).&lt;/p&gt;

&lt;p&gt;The question in this case is how to achieve eventual data consistency across all the data stores keeping in mind that the single source of truth is physically sparse across multiple data stores and third party APIs each with their own set of shortcomings. Moreover let&amp;#39;s assume that consolidating them into one data store is not an option - in this specific case a strategy within a set of constraints is required in order to achieve an adequate level of consistency which can guarantee the correct resolution of end-user specific use-cases.&lt;/p&gt;

&lt;p&gt;First it becomes clear that the criteria for each data store consistency needs to be properly defined - how fresh must the data be? does the access requires bi-directional access? read and writes or it&amp;#39;s just a form of read-replica within a specific type of data store to optimally solve a problem e.g. moving the relations between users into a graph database in order to easily make connections between them and generate insights that can then be easily consumed through a micro-service by various clients (mobile, web apps, etc.)&lt;/p&gt;

&lt;p&gt;The simplest case is read-only mode without any or minimal metadata, also it&amp;#39;s important if the metadata is encapsulated or it leaks into other micro-services business logic. If we&amp;#39;re talking about the former then defining the rules for how fresh the data is dictated by its consumers.&lt;/p&gt;

&lt;p&gt;The next one is when we have to deal with bi-directional consistency i.e. a client updates something in our data store and we need to sync that back with all the sparse single source of truth data stores. Now, this becomes quite problematic, because we have a dependency tree with side-effects leaking on each leaf. Existing criteria dictated by the consumers will thus require syncing the data to lowest acceptable interval, otherwise we will break the consistency contract we have with our clients. This becomes quite complex with metadata that it&amp;#39;s shared  between multiple micro-services.&lt;/p&gt;

&lt;p&gt;What about metadata? this is now a full fledged cross cutting concern that becomes dissonant with itself as it needs to be synced in multiple places and via different protocols (think about having similar metadata in a graph db. and a relational one). Optimally the metadata, since it&amp;#39;s cross cutting across multiple micro-services it should reside in one data store but let&amp;#39;s say this is not an option. &lt;/p&gt;

&lt;p&gt;This quickly transforms into an aching architectural issue - the solution resides not in a shared data store but within a shared mechanism of keeping track of these changes. One solution could emerge in the form of queues with ack/retry capability backed by persistence (something à la RabbitMQ, Kafka, etc.). It is now self-evident that this adds another complication: how do we deal with persistent events in the queues that are now inconsistent due to how much time they resided (a simple example would be a set of unacked events that after some time &amp;quot;T&amp;quot; become invalid as the record/object they have a reference to doesn&amp;#39;t exist anymore) as a result: this will require another mechanism to drop those event from the queues or add another high level abstraction leak as the clients will have to deal with the issues of out of date data.&lt;/p&gt;

&lt;p&gt;Eventual consistency in a complex system of sparse single source of truth data store with an added complication of cross cutting metadata within a fleet of micro-services is quite an interesting problem to solve - it its most basic form we deal with a graph of (inter)dependencies that should govern the orchestration of both the micro-services and the syncing of the various data stores.&lt;/p&gt;

&lt;p&gt;Finally the solution would be emergent of the graph of (inter)dependencies in the form of an attached mechanism which abstracts away the sparse single source of truth into just one that stays within the confines of its consumers, thus delivering the expected business value on all fronts. &lt;/p&gt;

&lt;h4&gt;More information:&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://en.wikipedia.org/wiki/Eventual_consistency"&gt;Eventual consistency &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://en.wikipedia.org/wiki/Single_source_of_truth"&gt;Single source of truth &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/35</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/keep-your-rails-logs-free-of-unwanted-noise"/>
    <title>Keep your Rails logs free of unwanted noise</title>
    <content type="html">&lt;p&gt;I like to use &lt;a target="_blank" href="http://rollbar.com"&gt;Rollbar &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&amp;#39;s default plan for small apps or prototypes that are wip. Getting production exceptions in my inbox can be quite useful but most of them are actually 404s - sure there are a multitude of ways for filtering out the noise (&lt;a target="_blank" href="https://www.digitalocean.com/community/tutorials/how-to-protect-an-nginx-server-with-fail2ban-on-ubuntu-14-04"&gt;Fail2Ban &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, hand written IPTables &lt;a target="_blank" href="https://www.linode.com/docs/security/firewalls/control-network-traffic-with-iptables"&gt;rules &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, etc.)&lt;/p&gt;

&lt;p&gt;The main idea here is to filter as much of the Rails&amp;#39;s logs noise with simple enough changes — security is a completely different topic (though some of the tools do touch on that realm too).&lt;/p&gt;

&lt;h4&gt;nginx&lt;/h4&gt;

&lt;p&gt;First thing I&amp;#39;ve observed was that my app was getting scanned via the IP and not the domain name — so first the fix comes as a configuration change in nginx to enable responses only via the app&amp;#39;s host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
  listen 80;
  server_name {the-ip-address};
  return 404;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#39;m not really sure whether I should use 404 or 403 — Not Found sounds correct since one should not access the app via its IP but Forbidden is somewhat equally suitable — the resource is here but you cannot access it via the server&amp;#39;s IP.&lt;/p&gt;

&lt;p&gt;This single configuration change reduced the logging noise by about 200 daily faux-requests — which is quite nice.&lt;/p&gt;

&lt;h4&gt;Rack::Attack&lt;/h4&gt;

&lt;p&gt;Next stop: automatic scans for WordPress vulnerabilities — lots and lots of requests for &lt;code&gt;wp-admin&lt;/code&gt; and &lt;code&gt;wp-login&lt;/code&gt; — just ghastly. &lt;/p&gt;

&lt;p&gt;How can we fix this? We get down on the application level i.e. Rack.&lt;/p&gt;

&lt;p&gt;The idea is simple: put a Rack middleware in front of the chain that can just drop all those pesky WP scans: &lt;a target="_blank" href="https://github.com/kickstarter/rack-attack"&gt;Rack::Attack &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Installation is simple: add the gem to your Gemfile, create a config file like &lt;code&gt;/config/initialisers/rack_attact.rb&lt;/code&gt; and add:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# After 1 blocked requests in 10 minutes, block all requests from that IP for 1 day.&lt;/span&gt;
&lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Attack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blocklist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fail2ban pentesters'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# `filter` returns truthy value if request fails, or if it's from a previously banned IP so the request is blocked&lt;/span&gt;
  &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Attack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Fail2Ban&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;"pentesters-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;maxretry: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;findtime: &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="ss"&gt;bantime: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;day&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# The count for the IP is incremented if the return value is truthy&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp-admin'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'wp-login'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Any requests to &lt;code&gt;/{wp-admin | wp-login}&lt;/code&gt; will translate into an instant one-day ban for that IP. Clearly you could do this using Fail2Ban but &lt;code&gt;Rack::Attack&lt;/code&gt; comes with other &lt;a target="_blank" href="https://github.com/kickstarter/rack-attack#usage"&gt;niceties too &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and it ticks the &lt;em&gt;simple&lt;/em&gt; checkbox whilst being more portable than Fail2Ban.&lt;/p&gt;

&lt;p&gt;The final step is to actually use this middleware by prepending it in config.ru:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../config/environment'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="kp"&gt;__FILE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Attack&lt;/span&gt;
&lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="no"&gt;YourApp&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why in config.ru and not in the config files of Rails? Mostly because of semantics — it&amp;#39;s not related to the business logic of the app and it makes it explicit that&amp;#39;s the first Rack middleware a request going through nginx will hit.&lt;/p&gt;

&lt;p&gt;After all of this is set-up — a simple request to &amp;#39;/wp-admin&amp;#39; will result in a 403 with the Forbidden message and a one-day ban.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;before: &lt;strong&gt;220 to 240&lt;/strong&gt; - 404 exceptions/day&lt;br&gt;
   after: &lt;strong&gt;10 or fewer&lt;/strong&gt; - 404 exceptions/day&lt;/p&gt;

&lt;p&gt;and this without completely turning off 404 exceptions.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/34</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/the-perils-of-writing-request-specs-using-concurrent-ruby-under-the-jvm"/>
    <title>The perils of writing request specs using concurrent-ruby under the JVM</title>
    <content type="html">&lt;p&gt;When I write an API, though I&amp;#39;m not a hard core TDD practitioner, I do like writing specs - especially requests specs that test the whole stack. &lt;/p&gt;

&lt;p&gt;Adding them into an API is fast and yields quite good results compared to an app with an UI where you have to use chrome-cli or phantomjs just to get near of that level but at the cost of painfully slow execution time (yes, even if you optimise them to hell and back and get a runtime of five minutes - they&amp;#39;re still slow in my book).&lt;/p&gt;

&lt;p&gt;Anyhow, for some context - I&amp;#39;ve been writing Ruby APIs for quite some time but using the classic CRuby VM/interpreter - this time we switched to JRuby (thanks to &lt;a target="_blank" href="https://andreimaxim.ro"&gt;Max &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;) since we needed to render extremely quick JSON responses (i.e. 202 status codes) - so everything from ActiveRecord CRUD operations to processing business logic is done using &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby"&gt;concurrent-ruby &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; with &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/blob/master/doc/future.md"&gt;Futures &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/blob/master/doc/thread_pools.md"&gt;ThreadPools &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; (which in JRuby they use &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent/executor/thread_pool_executor.rb#L10:L15"&gt;Java&amp;#39;s native implementation &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - awesome stuff since one can play with &lt;a target="_blank" href="https://en.wikipedia.org/wiki/Software_transactional_memory"&gt;STM &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and all that good stuff - if required).&lt;/p&gt;

&lt;p&gt;Next stop: the core of the issue: running request specs with all this parallelism in the background breaks most of our specs because RSpec is not aware of anything which runs in parallel and the inherent non-deterministic nature of running code in parallel.&lt;/p&gt;

&lt;p&gt;The initial fix for this was to add a helper that basically forces a shutdown of the ThreadPool which will block until it&amp;#39;s done - this &amp;quot;ensured&amp;quot; that all the parallel tasks (like creating a record) would finish before we got to the expect part in the specs. All nice and rosy except it did not work as expected.&lt;/p&gt;

&lt;p&gt;This was the initial implementation btw. :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait_for_thread_pool!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sleep_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;thread_pool_executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;
  &lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thread_pool_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executor&lt;/span&gt;

  &lt;span class="c1"&gt;# shutdown the pool and wait as long as it takes&lt;/span&gt;
  &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;
  &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_termination&lt;/span&gt;

  &lt;span class="c1"&gt;# we want a fresh thread pool for the next test&lt;/span&gt;
  &lt;span class="n"&gt;thread_pool_executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:initialize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is quite self-explanatory - it issues a ThreadPool shutdown which waits for all the threads to finish their work - the problem? In 10% of the cases using RSpec random spec execution it failed.&lt;/p&gt;

&lt;p&gt;For the time being - the next best fix was to monkey patch the library (well, to be more precise our adapter for it) in order to run everything sequentially - especially keeping in mind that concurrent-ruby is an externally tested library and the issue should be an odd interaction between RSpec, &lt;a target="_blank" href="https://github.com/DatabaseCleaner/database_cleaner"&gt;DatabaseCleaner &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and concurrent-ruby. &lt;/p&gt;

&lt;p&gt;In any case until I can start digging into all those dependencies and see what the actual issue was this fixed the issue:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# run everything sequantial for now&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disable_parallelism!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;context: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:context_a&lt;/span&gt;
    &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;executor: &lt;/span&gt;&lt;span class="kp"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="ss"&gt;:context_b&lt;/span&gt;
    &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_observer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;observer&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;opts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@block_to_call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;block&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;
        &lt;span class="n"&gt;proc_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@block_to_call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
        &lt;span class="vi"&gt;@observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;proc_response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I usually avoid monkey patching stuff like this - but then again this is also where monkey patching can give you some breathing time and keep you sane whilst the long process of debugging why:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shutdown&lt;/span&gt;
&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait_for_termination&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;it&amp;#39;s not doing its job properly can continue as an async (pun intended) task. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NB&lt;/strong&gt;: please read the Updates section below on how this was fixed - to completely avoid the monkey-patching we can use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;disable_parallelism!&lt;/span&gt;
  &lt;span class="no"&gt;CustomExecutor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ImmediateExecutor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Regarding specs/coverage - the next step is to improve code-coverage in the unit-tests realm as to be sure all the small parts are working correctly.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;JRuby + concurrent-ruby is a boon - there are some drawbacks as pointed above and many more which I&amp;#39;ll detail in future posts but it really pays off when you really need quick responses. En plus, concurrent-ruby literally levels up JRuby to a place where it can compete with Elixir, well Erlang&amp;#39;s concurrency model (yes it has &lt;a target="_blank" href="http://ruby-concurrency.github.io/concurrent-ruby/Concurrent/Actor.html"&gt;Actors &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; but they&amp;#39;re in the edge branch for now). Its abstractions are top notch and they remove a lot of pain from dealing with Threads with some minor caveats.&lt;/p&gt;

&lt;h4&gt;Updates&lt;/h4&gt;

&lt;p&gt;After the &lt;code&gt;r/ruby&lt;/code&gt; &lt;a target="_blank" href="https://www.reddit.com/r/ruby/comments/6w6hr0/the_perils_of_writing_request_specs_using/"&gt;discussion &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; some updates are required:&lt;/p&gt;

&lt;p&gt;A fellow redditor - &lt;code&gt;i_know_sherman&lt;/code&gt; suggested concurrent-ruby&amp;#39;s &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/blob/master/lib/concurrent/executor/immediate_executor.rb"&gt;ImmediateExecutor &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which is a special executor that basically runs everything sequentially:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An executor service which runs all operations on the current thread, blocking as necessary. Operations are performed in the order they are received and no two operations can be performed simultaneously.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A small caveat here: it works correctly except when using Futures with the option &lt;code&gt;dup_on_deref&lt;/code&gt; set to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Another suggestion from &lt;code&gt;moomaka&lt;/code&gt; - this time for keeping parallelism on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Problem may be that sometimes the tasks in the queue needs to queue other tasks on the same executor and those tasks are being rejected. May be able to fix this by setting the :fallback_policy of the executor to :caller_runs which will then run the rejected tasks immediately in the caller thread allowing everything to complete.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This feature was implemented in concurrent-ruby by &lt;code&gt;jrochkind&lt;/code&gt; &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/issues/192"&gt;here &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; i.e. &lt;a target="_blank" href="https://github.com/ruby-concurrency/concurrent-ruby/commit/8bea918f0d949c3a13d74864efaa3c53c3cbc6ad"&gt;Improve behaviour when posting to a shutdown thread pool &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/33</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/jruby-rvm-and-vim-walk-into-a-performance-bar"/>
    <title>JRuby, RVM and Vim walk into a (performance) bar</title>
    <content type="html">&lt;p&gt;I&amp;#39;m not sure why RVM is still my default Ruby version manager yet the issue at hand should be reproducible on rbenv too. &lt;/p&gt;

&lt;p&gt;Let&amp;#39;s add a bit of context to this: we have an API written in Ruby and running on JRuby i.e. via JVM which implies slow start-up times compared to CRuby (yes, even with &lt;code&gt;export JRUBY_OPTS=&amp;#39;--dev&amp;#39;&lt;/code&gt;). I use Vim 8.0.502 which of course comes by default bundled with Tim Pope&amp;#39;s &lt;a target="_blank" href="https://github.com/vim-ruby/vim-ruby"&gt;vim-ruby &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The problem: since I&amp;#39;ve switched to JRuby I started to notice a slowdown whilst working in Vim with .rb files. I did not pay too much attention to it at first (read almost for a month) but today it really started to bug me. I had no idea what was causing the slowdown so I tried Vim&amp;#39;s trusty command-line option:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vim --startuptime profile-data app/controllers/application_controller.rb
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;which yielded (edited for brevity):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;104.316  002.877  002.877: sourcing /usr/local/share/vim/vim80/syntax/ruby.vim
1228.691  1123.486  1123.486: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim
1229.935  000.686  000.686: sourcing /usr/local/share/vim/vim80/indent/ruby.vim
{ edited }
1273.618  043.838: opening buffers
1274.471  000.853: BufEnter autocommands
1274.475  000.004: editing files in windows
1274.924  000.449: VimEnter autocommands
1274.932  000.008: before starting main loop
1299.500  024.568: first screen update
1299.501  000.001: --- VIM STARTED ---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Over one second to open a simple Ruby file? auch!&lt;/p&gt;

&lt;p&gt;The relevant line in that file is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1228.691  1123.486  1123.486: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Why are we spending one second in &lt;code&gt;ruby.vim&lt;/code&gt; ? the answer is in &lt;a target="_blank" href="https://github.com/vim-ruby/vim-ruby/blob/master/ftplugin/ruby.vim#L70:L80"&gt;this function &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which gets executed before opening a .rb file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function! s:query_path(root) abort
  let code = "print $:.join %q{,}"
  if &amp;amp;shell =~# 'sh' &amp;amp;&amp;amp; empty(&amp;amp;shellxquote)
    let prefix = 'env PATH='.shellescape($PATH).' '
  else
    let prefix = ''
  endif
  if &amp;amp;shellxquote == "'"
    let path_check = prefix.'ruby --disable-gems -e "' . code . '"'
  else
    let path_check = prefix."ruby --disable-gems -e '" . code . "'"
  endif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: &lt;code&gt;ftplugin&lt;/code&gt; stands for filetype and not FTP - so yes, this logic is required. &lt;/p&gt;

&lt;p&gt;After all of this I did one more test: using &lt;code&gt;top&lt;/code&gt; in another tmux window I could actually see the Java process running after Vim therefore the issue was trivial: &lt;code&gt;vim-ruby&lt;/code&gt; was using JRuby.&lt;/p&gt;

&lt;p&gt;The logical next step: instruct Vim to use CRuby if RVM with JRuby is detected. After &lt;a target="_blank" href="https://github.com/vim-ruby/vim-ruby/issues/33#issuecomment-1004831"&gt;some &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; &lt;a target="_blank" href="https://plus.google.com/114112334290393746697/posts/foWqKczaGff"&gt;googling &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; around the solution was an appendix to &lt;code&gt;.vimrc&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if !empty(matchstr($MY_RUBY_HOME, 'jruby'))
  let g:ruby_path = '/usr/bin/ruby'
endif
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Basically if RVM has set-up JRuby already instruct &lt;code&gt;vim-ruby&lt;/code&gt; via its helpful &lt;code&gt;ruby_path&lt;/code&gt; config to use the system&amp;#39;s default Ruby (on macOS that being - CRuby version 2.0.0p648)&lt;/p&gt;

&lt;p&gt;Results after the fix:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;104.643  002.876  002.876: sourcing /usr/local/share/vim/vim80/syntax/ruby.vim
106.938  001.369  001.369: sourcing /usr/local/share/vim/vim80/ftplugin/ruby.vim
108.062  000.622  000.622: sourcing /usr/local/share/vim/vim80/indent/ruby.vim
{ edited }
152.700  044.957: opening buffers
153.389  000.689: BufEnter autocommands
153.394  000.005: editing files in windows
154.086  000.692: VimEnter autocommands
154.092  000.006: before starting main loop
178.172  024.080: first screen update
178.174  000.002: --- VIM STARTED ---
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;~200ms =&amp;gt; much better.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/32</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/how-to-reduce-the-size-of-your-vms-macos-update"/>
    <title>How to reduce the size of your VMs (macOS Update)</title>
    <content type="html">&lt;p&gt;​&lt;br&gt;
Lately I&amp;#39;m also running macOS virtualized for testing various apps. After some heavy usage the same problem popped up: the .vmdk uses way too much space on my quite small 256GB SSD. Currently is at around 70GB.&lt;/p&gt;

&lt;p&gt;The process is similar to what I did on Windows and Linux i.e. write a null file that is erased at the end then use the command-line tools to compact the .vmdk and reclaim space (the example is specific for VMWare Fusion).&lt;/p&gt;

&lt;p&gt;Without further ado: &lt;/p&gt;

&lt;h4&gt;Step one&lt;/h4&gt;

&lt;p&gt;Fill up all  the empty space with 0x00&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat /dev/zero &amp;gt; zeroed_empy_space; rm zeroed_empy_space
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In case you&amp;#39;re wondering what &lt;code&gt;/dev/zero&lt;/code&gt; does:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;/dev/zero&lt;/code&gt; is a special file in Unix-like operating systems that provides as many null characters (ASCII NUL, 0x00) as are read from it. One of the typical uses is to provide a character stream for initializing data storage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;Step two&lt;/h4&gt;

&lt;p&gt;VMWare Fusion is installed by default in &lt;code&gt;/Applications/VMware Fusion.app&lt;/code&gt; so you just have to run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cd ‘/Applications/VMware Fusion.app/Contents/Library/‘ 
$ vmware-vdiskmanager -k your_vm_name.vmdk
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and wait until it finishes - please note that you need at least the .vmdk file size free space on disk or this operation will fail.&lt;/p&gt;

&lt;h3&gt;Results&lt;/h3&gt;

&lt;p&gt;Original vmdk size: 70GB&lt;br&gt;
After zeroing free space and clean-up: 40GB&lt;/p&gt;

&lt;h4&gt;Credits:&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://communities.vmware.com/thread/455978?start=0&amp;amp;tstart=0"&gt;vmware support #1 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://kb.vmware.com/selfservice/microsites/search.do?language=en_US&amp;amp;cmd=displayKC&amp;amp;externalId=1003897"&gt;vmware support #2 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/31</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/programming-archaeology-phps-dollar-sigil"/>
    <title>Programming archaeology: PHP's dollar sigil</title>
    <content type="html">&lt;p&gt;I started out with Java as my professional language and before I discovered Ruby, PHP was my main programming language of choice for new projects. This was probably six to seven years ago or more. I had some pretty good times with CodeIgniter [0] and various libraries like HHMVC [1]. &lt;/p&gt;

&lt;p&gt;In any case, I digress - even back then the most striking feature of its syntax was the dollar sign prepended to variables which made me think the aesthetics of the language were sub-par or at least annoying. Clearly it did not stop me for writing lots of apps in PHP since it was more of a small annoyance, if you will, something debatable mostly for scholastic purposes.&lt;/p&gt;

&lt;p&gt;Recently the question popped into my mind: &amp;quot;What&amp;#39;s the history of the dollar sign? and why is there.&amp;quot; - so like every good question I started to Google my way into some answers:&lt;/p&gt;

&lt;p&gt;First influence - Perl:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Because PHP was based on Perl which used $, though the symbols Perl used were meaningful and plenty used to indicate the data type, ( such as @ used to indicate an array ) PHP just has $.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;via &lt;a href="http://stackoverflow.com/a/3073818"&gt;http://stackoverflow.com/a/3073818&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Perl might have this from shell tools like awk et al. hence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Having a symbol to denote variables makes string interpolation simple and clear. Shell, Perl and PHP grew out of the need for quick and easy string manipulation&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;via &lt;a href="http://stackoverflow.com/a/3596504"&gt;http://stackoverflow.com/a/3596504&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then Quora provided a very nice anecdote, collating the above results even more:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;PHP was originally implemented as a set of Perl scripts written by Rasmus to manage his website -- &amp;quot;PHP&amp;quot; stood for &amp;quot;Personal Homepage&amp;quot;. A number of other syntactical elements in PHP are reminiscent of Perl as well.&lt;/p&gt;

&lt;p&gt;Perl probably picked up this from awk and sh, which likely picked it up from BASIC, which predates Unix by about 5-10 years.&lt;/p&gt;

&lt;p&gt;Bit of trivia: the $ (or @ or % in Perl 5) is referred to as a sigil.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;via &lt;a href="https://www.quora.com/Why-we-use-dollar-symbol-before-variables-in-PHP"&gt;https://www.quora.com/Why-we-use-dollar-symbol-before-variables-in-PHP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From this we find out that $, @ and % are actually called sigils [3] in computer programming. Next stop was to see what these sigils indicated in Perl:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Scalar values are always named with &amp;#39;$&amp;#39;, even when referring to a scalar that is part of an array or a hash. The &amp;#39;$&amp;#39; symbol works semantically like the English word &amp;quot;the&amp;quot; in that it indicates a single value is expected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  $days               # the simple scalar value "days"
  $days[28]           # the 29th element of array @days
  $days{'Feb'}        # the 'Feb' value from hash %days
  $#days              # the last index of array @days
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Entire arrays (and slices of arrays and hashes) are denoted by &amp;#39;@&amp;#39;, which works much as the word &amp;quot;these&amp;quot; or &amp;quot;those&amp;quot; does in English, in that it indicates multiple values are expected.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  @days               # ($days[0], $days[1],... $days[n])
  @days[3,4,5]        # same as ($days[3],$days[4],$days[5])
  @days{'a','c'}      # same as ($days{'a'},$days{'c'})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Entire hashes are denoted by &amp;#39;%&amp;#39;:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  %days               # (key1, val1, key2, val2 ...)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;via &lt;a href="http://search.cpan.org/%7Eshay/perl-5.24.1/pod/perldata.pod#Variable_names"&gt;http://search.cpan.org/~shay/perl-5.24.1/pod/perldata.pod#Variable_names&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Very interesting and they remind me of Ruby&amp;#39;s shorthand or general delimited input:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="sx"&gt;%w(foo bar)&lt;/span&gt; &lt;span class="c1"&gt;# =&amp;gt; ["foo", "bar"]&lt;/span&gt;
&lt;span class="c1"&gt;# %r() is another way to write a regular expression.&lt;/span&gt;
&lt;span class="c1"&gt;# %q() is another way to write a single-quoted string (and can be multi-line, which is useful)&lt;/span&gt;
&lt;span class="c1"&gt;# %Q() gives a double-quoted string&lt;/span&gt;
&lt;span class="c1"&gt;# %x() is a shell command&lt;/span&gt;
&lt;span class="c1"&gt;# %i() gives an array of symbols (Ruby &amp;gt;= 2.0.0)&lt;/span&gt;
&lt;span class="c1"&gt;# %s() turns foo into a symbol (:foo)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Conclusion? the dollar sign for variables in PHP is actually a vestigial sigil since compared to Perl there aren&amp;#39;t any for arrays and hashes. &lt;/p&gt;

&lt;p&gt;[0] - &lt;a href="https://codeigniter.com"&gt;https://codeigniter.com&lt;/a&gt;&lt;br&gt;
[1] - &lt;a href="https://github.com/jenssegers/codeigniter-hmvc-modules"&gt;https://github.com/jenssegers/codeigniter-hmvc-modules&lt;/a&gt;&lt;br&gt;
[3] - &lt;a href="https://en.wikipedia.org/wiki/Sigil_(computer_programming)"&gt;https://en.wikipedia.org/wiki/Sigil_(computer_programming)&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/30</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/macos-sierra-upgrade-from-a-developers-perspective"/>
    <title>macOS Sierra upgrade from a developer's perspective</title>
    <content type="html">&lt;p&gt;Most developers (myself included) usually wait for the &lt;code&gt;point one&lt;/code&gt; release of the next major macOS upgrade. This is mostly in order so that all the related tools/applications maintainers will have time to upgrade and iron out their bugs/issues with the &lt;em&gt;new&lt;/em&gt; OS.&lt;/p&gt;

&lt;p&gt;Since I use my Mac mostly as a glorified *nix replacement, the GUI applications are scarce (notably: &lt;a target="_blank" href="https://www.iterm2.com/downloads.html"&gt;iTerm &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://paw.cloud/"&gt;Paw &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="http://postgresapp.com/"&gt;Postgres.app &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, Xcode and a couple of small utilities) i.e. the bulk of my development is dependent on &lt;a target="_blank" href="http://brew.sh"&gt;Homebrew &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; working correctly + the good old &lt;a target="_blank" href="https://rvm.io/"&gt;RVM &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All of these work flawlessly under Sierra, one just has to remember to update them beforehand via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ brew update
$ rvm get stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Afterwards a clean output from &lt;code&gt;brew doctor&lt;/code&gt; should fix whatever potential issues remain.&lt;/p&gt;

&lt;p&gt;The main issues after the OS upgrade were, ironically, not the apps/utilities but the crushingly slow default values for &lt;code&gt;​KeyRepeat&lt;/code&gt;, a pink external EIZO display, the Notes.app crashing like nuts and finally some minor performance tweaks (mostly for aesthetics)&lt;/p&gt;

&lt;h4&gt;Fixing slow key repeat&lt;/h4&gt;

&lt;p&gt;If you use vim in a terminal you&amp;#39;ll quickly realize that the navigation is back to &lt;em&gt;slow as heck&lt;/em&gt;. That&amp;#39;s due to the &lt;code&gt;​KeyRepeat&lt;/code&gt; values. In El Capitan this could be fixed easily via Karabiner but for 10.12 their developers have a wip Karabiner-Elements which it&amp;#39;s useless at this point (at least for me as it tells that I should edit JSON files).&lt;/p&gt;

&lt;p&gt;Well, no thank you good sirs: if I&amp;#39;m going to edit config files, I&amp;#39;d rather do it directly at the OS level. &lt;/p&gt;

&lt;p&gt;After some testing around I got back the fast and snappy vim scroll/navigation via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ defaults write NSGlobalDomain KeyRepeat -int 1
$ defaults write NSGlobalDomain InitialKeyRepeat -int 12
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: these can be reset back to their default via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ​defaults delete NSGlobalDomain KeyRepeat
$ defaults delete NSGlobalDomain InitialKeyRepeat
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In order for them to take effect you can just log out or restart your Mac.&lt;/p&gt;

&lt;h4&gt;Fixing my pink external EIZO display&lt;/h4&gt;

&lt;p&gt;This was rather easy but tedious: it basically boils down to repeating the same steps again from Mathew&amp;#39;s &lt;a target="_blank" href="http://www.mathewinkson.com/2013/03/force-rgb-mode-in-mac-os-x-to-fix-the-picture-quality-of-an-external-monitor"&gt;article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; i.e.: &lt;/p&gt;

&lt;p&gt;Disable macOS&amp;#39;s SIP by booting into recovery mode (using &lt;code&gt;Command-R&lt;/code&gt; whilst rebooting your mac and then running &lt;code&gt;csrutil disable&lt;/code&gt; in the terminal).&lt;/p&gt;

&lt;p&gt;Download the linked Ruby script and run it via &lt;code&gt;ruby patch-edid.rb&lt;/code&gt;. It will generate a directory.&lt;/p&gt;

&lt;p&gt;Move the directory into (back-up the old one if it exists):&lt;br&gt;
 &lt;code&gt;/System/Library/Displays/Contents/Resources/Overrides&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Then you can restart again into recovery mode and enable back SIP by running &lt;code&gt;csrutil enable&lt;/code&gt; into the terminal.&lt;/p&gt;

&lt;p&gt;After all this: my external LCD was crystal clear and NOT pink anymore.&lt;/p&gt;

&lt;h4&gt;Notes.app crashing and minor performance tweaks&lt;/h4&gt;

&lt;p&gt;The first one is simple: just disable/enable the iCloud Notes syncing. &lt;/p&gt;

&lt;p&gt;Regarding the second one you can tick &lt;em&gt;reduce transparency and motion&lt;/em&gt; in the Accessibility section in Settings (this should offload the GPU if you have an older Mac with Intel graphics and as aesthetics and UX go, I&amp;#39;m not a fan of over-the-top animations and transparency).&lt;/p&gt;

&lt;h4&gt;Other issues&lt;/h4&gt;

&lt;p&gt;&lt;em&gt;Wi-Fi not working&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rachel pointed out her Wi-Fi wasn&amp;#39;t working anymore. The fix: after posting the issue on Apple forums someone suggested &lt;a target="_blank" href="http://www.cydiageeks.com/fix-slow-wi-fi-issues-macos-sierra-upgrade.html"&gt;this article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; that finally fixed it.&lt;/p&gt;

&lt;p&gt;I also had a small problem with it but it quickly went away by turning the Wi-Fi off and then back on. In any case hopefully this will ease the upgrade for someone.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Bonus: right after the upgrade prepare for 100% CPU usage on all logical cores for ~ one hour or less, the culprit will be &lt;code&gt;​photoanalysisd&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you’ve just installed Mac OS Sierra and now see photoanalysisd sucking 100 to 200% CPU power, this process is doing some kind of face detection + object / image recognition / indexing on your Photos library. via &lt;a target="_blank" href="https://blog.yimingliu.com/2016/09/20/check-progress-of-photoanalysisd/"&gt;check progress of photoanalysisd &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the end the upgrade wasn&amp;#39;t painless e.g. El Capitan went smoothly compared but at least I removed an extra dependency - I no longer need to have Karabiner running in the background and somehow it feels a tad faster even whilst the memory pressure is now lower.&lt;/p&gt;

&lt;h4&gt;Credits&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="​https://pqrs.org/osx/karabiner"&gt;karabiner &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.mathewinkson.com/2013/03/force-rgb-mode-in-mac-os-x-to-fix-the-picture-quality-of-an-external-monitor"&gt;Force RGB mode in Mac OS X &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/mathiasbynens/dotfiles/issues/687#issuecomment-249026654"&gt;Validate KeyRepeat values for Sierra &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="​
https://coderwall.com/p/jzuuzg/osx-set-fast-keyboard-repeat-rate"&gt;Reset KeyRepeat &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/29</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/using-rvm-for-quite-some-time-you-should-clean-it-up-a-bit"/>
    <title>Using RVM for quite some time? You should clean it up a bit</title>
    <content type="html">&lt;p&gt;I can&amp;#39;t even remember the last time I ran &lt;code&gt;rvm implode&lt;/code&gt;, I&amp;#39;d reckon I&amp;#39;ve been using RVM for about three years. &lt;/p&gt;

&lt;p&gt;Sure: I always update it via &lt;code&gt;rvm get stable&lt;/code&gt; but I never really clean up old Rubys or gems, I only run &lt;code&gt;bundle clean --force&lt;/code&gt; in each repository from time to time since I don&amp;#39;t always use &lt;code&gt;bundle exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In any case, time to see how much space it eats up on macOS via &lt;code&gt;rvm disk-usage all&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Downloaded Archives Usage: 1.3M
  Repositories Usage: 0B
  Extracted Source Code Usage: 5.2M
  Log Files Usage: 28K
  Packages Usage: 0B
  Rubies Usage: 298M
  Gemsets Usage: 11G
  Wrappers Usage: 212K
  Temporary Files Usage: 0B
  Other Files Usage: 5.6M
  Total Disk Usage: 11G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Eleven gigabytes&lt;/strong&gt; of gems, sources, caches and various paraphernalia, wow, that&amp;#39;s something. Let&amp;#39;s try to clean this up a bit:&lt;/p&gt;

&lt;h4&gt;Step one&lt;/h4&gt;

&lt;p&gt;Always update RVM to its latest stable release:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rvm get stable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Step two&lt;/h4&gt;

&lt;p&gt;Share the gem cache with all Ruby versions (note this might introduce some subtle bugs if you like to edit your gems like me for debug purposes):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rvm gemset globalcache enable
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Step three  (attention destructive)&lt;/h4&gt;

&lt;p&gt;Run this simple script by &lt;a target="_blank" href="https://gist.github.com/kugaevsky/5830602"&gt;kugaevsky &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; to further clean up your gems.&lt;/p&gt;

&lt;p&gt;Note:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This process could be too destructive sometimes and you may need to run &amp;#39;bundle&amp;#39; command from your project directory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;Step four&lt;/h4&gt;

&lt;p&gt;Let RVM clean-up after itself:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;rvm cleanup&lt;/code&gt; Lets you remove stale source folders / archives and other&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rvm cleanup all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: this will take some time to complete.&lt;/p&gt;

&lt;h4&gt;Results&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rvm 
  " .etc
  Total Disk Usage: 9.0G
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not bad (let&amp;#39;s keep in mind that I did not remove any gemsets or Ruby installs and for my main projects I didn&amp;#39;t need to run bundle after running kugaevsky&amp;#39;s script).&lt;/p&gt;

&lt;p&gt;Btw. if you&amp;#39;re using a VM for development you can try &lt;a target="_blank" href="http://marianposaceanu.com/articles/how-to-reduce-the-size-of-your-vms"&gt;this &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; afterwards to further reduce its size.&lt;/p&gt;

&lt;h4&gt;Credits:&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://coderwall.com/p/x3z2ag/cleanup-rvm"&gt;Cleanup RVM &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://gist.github.com/kugaevsky/5830602"&gt;kugaevsky&amp;#39;s cleanup_all_gemsets.sh &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/28</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/buzzwords-and-manifestos"/>
    <title>Buzzwords and manifestos</title>
    <content type="html">&lt;p&gt;Do we really need them ? I mean sometimes I ask myself what’s the point of all the so-called ways to get things done or tons of guides for getting you to be a “better” you, like NLP, the secret of life and all that stuff.&lt;/p&gt;

&lt;p&gt;To quote Nassim N. Taleb:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“I went to a happiness conference: researchers looked very unhappy” - Nassim N. Taleb&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This really strikes me as big problem of modern society, maybe is something related to herd-mentality, we want to be stupid and not believe or motivate ourselves by the sheer power of our will. We suffer from a disease: the laziness of the will, gone is the will to power (or any other philosophical system that puts you in control of yourself, like existentialism for a change), nowadays we have a new direction: will to brainwash, will to not have a will. Is really sad and I find myself in the same situation too from time to time, yet solving half of the problem is realizing that you have it.&lt;/p&gt;

&lt;p&gt;I think therefore I might be wrong: let’s just say, for the sake of the argument, that all of these systems are in fact useful. They might guide to a better path, yet the problem is that it is not your path, someone else created it to fit as many of &lt;em&gt;you’s&lt;/em&gt; as possible; generalizing and having a reductionist view of reality makes it inauthentic for the individual. I’m not saying that everything else that’s not ours is irrelevant or not useful, no, what I mean is that by choosing what we like from the outer world and mashing all of that in something we can use that makes sense to us is far more effective than blindly fallowing some program and in the end realizing that nothing had changed.&lt;/p&gt;

&lt;p&gt;So let’s drop the buzzwords or fallowing blindly someone else system without taking it  through a critical hell ride in our minds.&lt;/p&gt;

&lt;p&gt;Good stuff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.themadtolive.com/wp-content/uploads/2010/11/A-Kick-In-the-Pants.pdf"&gt;A Kick In the Pants &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - a great pdf&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.themadtolive.com"&gt;via &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - themadtolive.com&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/27</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/the-frontend-backend-debacle"/>
    <title>The frontend backend debacle</title>
    <content type="html">&lt;p&gt;I’m really starting to get fed up with this useless separation. (&lt;em&gt;Warning&lt;/em&gt;: strong personal opinions, based on arguments fallowing).&lt;/p&gt;

&lt;p&gt;Ok, it’s not &lt;em&gt;useless&lt;/em&gt;, I get the idea in a big team, separation of concerns, better productivity and all that. That’s good and I’m all for that, makes things faster (in the big picture at least). Anyway, what I don’t get is why people that do programming, need to bracket themselves into this kind of name spacing.&lt;/p&gt;

&lt;p&gt;Whether you’re programming in JavaScript, php, ruby or java or even LOL-code in the end you are still a programmer. If you think that calling yourself a &lt;em&gt;frontend _, _backend&lt;/em&gt; or &lt;em&gt;middle-end&lt;/em&gt;  developer makes you somehow better, I’ve got news for you: it doesn’t&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It just shows that you can’t actually handle programming in a true sense”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’m not saying being a js developer is easy, no way! Actually starting to learn the concepts behind js made me a better programmer and helped me getting into more advanced stuff like ruby or clojure. Yes, we have the DOM model and all that stuff, but really, one can abstract that using a library and still write advanced UI’s with little or no performance problems. And don’t get me started on html and css, anyone can learn that, if you can write a hello world java program then, my friend, css and html is a piece of cake.&lt;/p&gt;

&lt;p&gt;Baseline ? A true programmer should be able to code in almost any language. If in your team you work as a &lt;em&gt;backender&lt;/em&gt; or &lt;em&gt;frontender&lt;/em&gt;, that’s ok, but that doesn’t mean you should not know css, html or js or vice-versa, other programming languages and concepts.&lt;/p&gt;

&lt;p&gt;You must know the basics and stay up to date, yes it’s not easy, so deal with it and lets stop hiding behind &lt;em&gt;name spaces&lt;/em&gt; ok ?&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/26</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/making-rails-asset-pipeline-faster"/>
    <title>Making Rails asset pipeline faster</title>
    <content type="html">&lt;p&gt;We all know that for a rather large Rails app where the front-end is quite stuffed, the asset pre-compilation and more importantly - development mode changes are far from instantaneous.&lt;/p&gt;

&lt;p&gt;The front-end we&amp;#39;re referring to is the kind that makes heavy use of sass, sass libraries (Compass, Bourbon, etc.) and transpiled JavaScript languages (like the old CoffeeScript, ES6, etc.)&lt;/p&gt;

&lt;h4&gt;Replacing Ruby Sass with libsass&lt;/h4&gt;

&lt;p&gt;On the Sass front for quite some time there&amp;#39;s a pure C/C++ implementation of the Sass CSS precompiler: &lt;a target="_blank" href="https://github.com/sass/libsass"&gt;libsass &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. Clearly, being implemented in C/C++ this means it should run orders of magnitude faster than the Ruby implementation.&lt;/p&gt;

&lt;p&gt;All we need is a gem that uses it: enter &lt;a target="_blank" href="https://github.com/sass/sassc-rails"&gt;sassc-rails &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which is a drop-in replacement for the &lt;code&gt;sass-rails&lt;/code&gt; gem. &lt;/p&gt;

&lt;p&gt;How fast is &lt;code&gt;sassc-rails&lt;/code&gt; compared to the vanilla gem in the real world? (benchmarks straight from their repo)&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Using sassc-rails
[1] pry(main)&amp;gt; Benchmark.bm { |bm| bm.report { Rails.application.assets["application.css"] } }
       user     system      total        real
   1.720000   0.170000   1.890000 (  1.936867)

# Using sass-rails
 [1] pry(main)&amp;gt; Benchmark.bm { |bm| bm.report { Rails.application.assets["application.css"] } }
       user     system      total        real
  7.820000   0.250000   8.070000 (  8.106347)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not really orders of magnitude faster but then again a 4.5x is not bad at all. &lt;/p&gt;

&lt;p&gt;The only issue with this is that it doesn&amp;#39;t fully work with the usual sass gems like Compass and more i.e. before using it in production make sure it can actually compile your scss files without issues.&lt;/p&gt;

&lt;p&gt;To help with that you can check the &lt;a target="_blank" href="http://sass-compatibility.github.io/"&gt;Sass Compatibility &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; website and run your app with the gem installed in a different branch to test things out.&lt;/p&gt;

&lt;h4&gt;Compass intermission&lt;/h4&gt;

&lt;p&gt;What grinds my gears is that Compass doesn&amp;#39;t work at all with &lt;code&gt;sassc-rails&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/sass/sassc-rails/issues/19"&gt;sassc-rails GH issue &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/Compass/compass-rails/issues/242"&gt;compass-rails GH issue &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also as a side note Compass seems, well, kinda dead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/Compass/compass"&gt;last commit in master a year ago &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://compass-style.org/blog"&gt;last blog post two years ago &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://wecodetheweb.com/2015/05/20/is-compass-dead/"&gt;Is Compass dead? &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.devbridge.com/articles/increasing-sass-compiling-performance-or-when-every-second-counts"&gt;Increasing Sass Compiling Performance or &amp;quot;When Every Second Counts” &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I think I&amp;#39;m going to migrate my front-ends that still use Compass to &lt;a target="_blank" href="https://github.com/thoughtbot/bourbon"&gt;Bourbon &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and maybe &lt;a target="_blank" href="https://github.com/ai/autoprefixer-rails"&gt;autoprefixer-rails &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;Replacing the JS transpiling/minification/etc. with mini_racer&lt;/h4&gt;

&lt;p&gt;The problem with &lt;a target="_blank" href="https://github.com/cowboyd/therubyracer"&gt;therubyracer &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; is that it uses version &lt;code&gt;~&amp;gt; 3.16.14.0&lt;/code&gt; of &lt;a target="_blank" href="https://github.com/cowboyd/libv8"&gt;libv8 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; (&amp;quot;a gem for distributing the v8 runtime libraries and headers in both source and binary form&amp;quot;) whereas &lt;a target="_blank" href="https://github.com/discourse/mini_racer"&gt;mini_racer &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; uses &lt;code&gt;~&amp;gt; 5.1&lt;/code&gt; which as one might expect is quite faster than the former.&lt;/p&gt;

&lt;p&gt;Some quick benchmarks from their repository:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ ruby bench_uglify.rb

Benching with MiniRacer
MiniRacer minify discourse_app.js 13813.36ms
MiniRacer minify discourse_app_minified.js 18271.19ms
MiniRacer minify discourse_app.js twice (2 threads) 13587.21ms

Benching with therubyracer
MiniRacer minify discourse_app.js 151467.164ms
MiniRacer minify discourse_app_minified.js 158172.097ms
MiniRacer minify discourse_app.js twice (2 threads) - DOES NOT FINISH

Killed: 9
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here we are talking about  a 10x improvement i.e. really nice gains and thus an order of magnitude faster. On the compatibility side this seems to be a drop-in replacement i.e. so far in the apps I&amp;#39;ve added it in the Gemfile there were no issues whatsoever.&lt;/p&gt;

&lt;h4&gt;Alternative to mini-racer for best possible performance&lt;/h4&gt;

&lt;p&gt;Sprockets uses &lt;a target="_blank" href="https://github.com/rails/execjs"&gt;ExecJS &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; which “picks the best runtime available to evaluate your JavaScript” this means that if you don&amp;#39;t mind having an extra dependency installed you can just use &lt;a target="_blank" href="https://nodejs.org"&gt;Node.js 6.x &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; to get the &lt;em&gt;maximum&lt;/em&gt; performance.&lt;/p&gt;

&lt;p&gt;Since I deploy my assets by precompilling them locally I just installed Node.js 6.2.0 via brew &lt;code&gt;brew install node&lt;/code&gt; and ran &lt;code&gt;time rake assets:clobber assets:precompile&lt;/code&gt; on a 2K lines CoffeeScript JS file. Without further ado, here are the results:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;node.js 6.2.0&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rake assets:clobber assets:precompile 7.30s user 1.27s system 102% cpu 8.358 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;mini-racer with libv8 5.x&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rake assets:clobber assets:precompile 14.96s user 1.20s system 101% cpu 15.943 total
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#39;s another 2x speed-up! Also, I tried it with 10K lines of CoffeeScript - in this case mini-racer didn&amp;#39;t even finish.&lt;/p&gt;

&lt;p&gt;Kudos goes to &lt;a target="_blank" href="https://twitter.com/attilagyorffy"&gt;@attilagyorffy &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; for pointing this out.&lt;/p&gt;

&lt;h4&gt;Should I use these in production?&lt;/h4&gt;

&lt;p&gt;In a set-up where the assets are pre-compiled locally then uploaded to the actual VM this is not a big issue i.e. if something fails just revert to the original gems. &lt;/p&gt;

&lt;p&gt;Mini-racer looks rock solid so far whereas sassc-rails still has issues with some libraries i.e. not ready for production in all use-cases yet.&lt;/p&gt;

&lt;p&gt;If you don&amp;#39;t mind installing node.js and want maximum performance then by all means do it since it works flawlessly with ExecJs.&lt;/p&gt;

&lt;p&gt;In the optimal case where you can integrate both into your Gemfile you will get quite a nice boost during deploys and more importantly: during development (in apps with large sets of assets of course).&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/25</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/shedding-some-light-into-uuid-version-4-in-ruby-and-rails"/>
    <title>Shedding some light into UUID version 4 in Ruby and Rails</title>
    <content type="html">&lt;p&gt;Ruby&amp;#39;s standard library and Rails&amp;#39;s PostgreSQL adapter use by default version 4 UUIDs. This can be changed in Rails migrations via:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s1"&gt;'uuid_generate_v1()'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;param whereas Ruby&amp;#39;s stdlib only supports version 4.&lt;/p&gt;

&lt;p&gt;Another interesting difference is in the implementations:&lt;/p&gt;

&lt;h5&gt;In the Ruby&amp;#39;s stdlib the UUID method is found in SecureRandom:&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# SecureRandom.uuid generates a random v4 UUID (Universally Unique IDentifier).&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# The version 4 UUID is purely random (except the version).&lt;/span&gt;
  &lt;span class="c1"&gt;# It doesn't contain meaningful information such as MAC addresses, timestamps, etc.&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="c1"&gt;# See RFC 4122 for details of UUID.&lt;/span&gt;
  &lt;span class="c1"&gt;#&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;uuid&lt;/span&gt;
    &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"NnnnnN"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x0fff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mh"&gt;0x4000&lt;/span&gt;
    &lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x3fff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mh"&gt;0x8000&lt;/span&gt;
    &lt;span class="s2"&gt;"%08x-%04x-%04x-%04x-%04x%08x"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;ary&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a target="_blank" href="https://github.com/ruby/ruby/blob/trunk/lib/securerandom.rb#L229"&gt;source-code &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h5&gt;The Rails version uses &lt;code&gt;uuid-ossp&lt;/code&gt; postgres extension:&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="c1"&gt;# By default, this will use the +uuid_generate_v4()+ function from the&lt;/span&gt;
  &lt;span class="c1"&gt;# +uuid-ossp+ extension, which MUST be enabled on your database.&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;primary_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="ss"&gt;:primary_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;super&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="ss"&gt;:uuid&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:default&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:default&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'uuid_generate_v4()'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:primary_key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;
    &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a target="_blank" href="https://github.com/rails/rails/blob/4-2-stable/activerecord/lib/active_record/connection_adapters/postgresql/schema_definitions.rb#L108-L127"&gt;source-code &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;The only difference is how the actual numbers are being generated in each library - this is quite interesting to investigate - let&amp;#39;s start with the Ruby stdlib.&lt;/p&gt;

&lt;h5&gt;Ruby standard library implementation&lt;/h5&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;SecureRandom&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Random&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gen_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="k"&gt;defined?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@pid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vg"&gt;$$&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="vi"&gt;@pid&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
        &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clock_gettime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CLOCK_REALTIME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:nanosecond&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;ary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@pid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random_add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="vi"&gt;@pid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;OpenSSL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gen_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;ret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"No random device"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;unless&lt;/span&gt; &lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="no"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unexpected partial read from random device: only &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;ret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bytes"&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="n"&gt;ret&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;a target="_blank" href="https://github.com/ruby/ruby/blob/trunk/lib/securerandom.rb#L49-L74"&gt;source-code &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Is starts with &lt;code&gt;OpenSSL::Random&lt;/code&gt; which can be described as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenSSL cannot generate truly random numbers directly. The choices are to use a cryptographically secure PRNG with a good random seed (i.e. with OS harvested data from effectively random hardware events); or use a real hardware RNG. [1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If OpenSSL is not present it falls back to Ruby&amp;#39;s pseudo-random number generator implemented in &lt;code&gt;Random.raw_seed&lt;/code&gt; which uses a modified Mersenne Twister with a period of 2**19937-1.&lt;/p&gt;

&lt;h5&gt;uuid-ossp extension implementation&lt;/h5&gt;

&lt;p&gt;Looking at &lt;code&gt;uuid-ossp&lt;/code&gt; source-code we can find:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Datum&lt;/span&gt;
&lt;span class="n"&gt;uuid_generate_v4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PG_FUNCTION_ARGS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;uuid_generate_internal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID_MAKE_V4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;and the &lt;code&gt;uuid_generate_internal&lt;/code&gt; relevant part of the function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;         &lt;span class="sr"&gt;/* random uuid */&lt;/span&gt;
&lt;span class="ss"&gt;default:
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;#ifdef HAVE_UUID_E2FS&lt;/span&gt;
    &lt;span class="n"&gt;uuid_t&lt;/span&gt;      &lt;span class="n"&gt;uu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;uuid_generate_random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uu&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;uuid_unparse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strbuf&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;#endif&lt;/span&gt;
   &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As we can see there&amp;#39;s a call to &lt;code&gt;uuid_generate_random&lt;/code&gt; - what can this be?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;uuid_generate_random(3) - Linux man page &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Its description:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The uuid_generate_random function forces the use of the all-random UUID format, even if a high-quality random number generator (i.e., /dev/urandom) is not available, in which case a pseudo-random generator will be substituted. Note that the use of a pseudo-random generator may compromise the uniqueness of UUIDs generated in this fashion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Interesting to note that if &lt;code&gt;/dev/urandom&lt;/code&gt; is not available - again it falls back to PRNG - It seems there&amp;#39;s a nuance here or even a contradiction as &lt;code&gt;/dev/urandom&lt;/code&gt; uses csPRNG (cryptographically secure pseudorandom number generator) [3]&lt;/p&gt;

&lt;h5&gt;Conclusions&lt;/h5&gt;

&lt;p&gt;This started as a simple curiosity on what versions of UUID I could use - note that this is not a critique of truly-random vs PRGNs as the RFC clearly states:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The version 4 UUID is meant for generating UUIDs from truly-random or pseudo-random numbers. [0]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;which makes both implementations correct. &lt;/p&gt;

&lt;p&gt;It makes sense for the UUID method to be in SecureRandom in Ruby since it&amp;#39;s only the version 4 implementation and by default it uses &lt;code&gt;OpenSSL::Random&lt;/code&gt;. The only minor gripe I have with this is that there&amp;#39;s no UUID lib for general use in Ruby supporting all the versions [2].&lt;/p&gt;

&lt;p&gt;Regarding their implementations it should be noted that they differ and if the &lt;em&gt;quality of randomness&lt;/em&gt; is important one should further investigate (e.g. due to missing OpenSSL - Ruby will use the internal PRNG which at first look it appears quite solid as it uses for seeding &lt;code&gt;dev/urandom&lt;/code&gt; [6] but it might not be in the same class of csPRNG)&lt;/p&gt;

&lt;p&gt;For UUIDs both implementations should yield usable random UUIDs even if the library falls back to a non csPRNG algorithm. In the end there&amp;#39;s a &lt;em&gt;small gotcha&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Distributed applications generating UUIDs at a variety of hosts must be willing to rely on the random number source at all hosts. If this is not feasible, the namespace variant should be used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to &lt;em&gt;avoid&lt;/em&gt; UUID collisions [4].&lt;/p&gt;

&lt;p&gt;[0] - &lt;a target="_blank" href="http://www.ietf.org/rfc/rfc4122.txt"&gt;RFC-4122 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; &lt;br&gt;
[1] - Why OpenSSL can&amp;#39;t use /dev/random directly? &lt;a target="_blank" href="http://security.stackexchange.com/a/47882"&gt;question &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;br&gt;
[2] - I usually use &lt;a target="_blank" href="https://github.com/sporkmonger/uuidtools"&gt;uuidtools &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;br&gt;
[3] - Myths about &lt;a target="_blank" href="http://www.2uo.de/myths-about-urandom"&gt;/dev/urandom &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;br&gt;
[4] - &lt;a target="_blank" href="http://programmers.stackexchange.com/questions/130261/uuid-collisions"&gt;uuid-collisions &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;br&gt;
[5] - bonus read &lt;a target="_blank" href="http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf"&gt;GoodPracticeRNG &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;br&gt;
[6] - &lt;a target="_blank" href="https://github.com/ruby/ruby/blob/trunk/random.c#L458"&gt;source-code &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/24</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/the-half-life-of-a-programmer"/>
    <title>The half-life of a programmer</title>
    <content type="html">&lt;blockquote&gt;
&lt;p&gt;Half-life (t1/2) is the amount of time required for the amount of something to fall to half its initial value. The term is very commonly used in nuclear physics to describe how quickly unstable atoms undergo radioactive decay, but it is also used more generally for discussing any type of exponential decay.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had this almost random idea of when a programmer hits his or hers career &lt;em&gt;half-life&lt;/em&gt;. &lt;/p&gt;

&lt;h4&gt;Tangents&lt;/h4&gt;

&lt;p&gt;First a few points that need explaining: &lt;em&gt;almost random&lt;/em&gt; is in the sense that next year this time I&amp;#39;ll be hitting thirty (Faith No More &lt;a target="_blank" href="https://www.youtube.com/watch?v=U8b88US-6ts"&gt;track &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; is already playing in my head).&lt;/p&gt;

&lt;p&gt;The next point is how one should understand the word &amp;quot;career&amp;quot; in the context of the field I&amp;#39;m working in. The word itself makes me think of a reductionist fifties like view of how one works and/or deals with work. &lt;/p&gt;

&lt;p&gt;This begets the question &lt;em&gt;&amp;quot;What does &amp;#39;career&amp;#39; mean for a programmer?&amp;quot;&lt;/em&gt;  - tricky question and interesting tangent: methinks it refers to that period of time when one starts to work professionally (getting paid) to the point in time when one stops doing that.&lt;/p&gt;

&lt;p&gt;Let&amp;#39;s jump on another tangent: &lt;em&gt;&amp;quot;Why is the end of a programming career when one stops writing code?&amp;quot;&lt;/em&gt;  - clearly one could easily jump on a management position and still call oneself a programmer - that for me is not programming any more, maybe I&amp;#39;m a purist and yes I&amp;#39;m not fond of labels like &amp;quot;team leader&amp;quot;, &amp;quot;CTO&amp;quot;, &amp;quot;lead developer&amp;quot;, &amp;quot;senior developer&amp;quot; and all that paraphernalia that in the end is just utter non-sense.&lt;/p&gt;

&lt;h4&gt;Relation to age&lt;/h4&gt;

&lt;p&gt;If one looks around the &amp;quot;average&amp;quot; age or more likely the anecdotal age (yes this is not even proper empiric data) of developers in shiny start-ups is around twenty seven, I reckon. And it makes sense - they&amp;#39;re relatively fresh out of university programmers with a decent experience on that hi(y)ppie new programming language or paradigm.&lt;/p&gt;

&lt;p&gt;I don&amp;#39;t want to turn this into a rant, methinks I was also in that situation and it&amp;#39;s a sane approach as long as one doesn&amp;#39;t go into extremes (like using MongoDB, OrientDB, InfluxDB, etc. for a problem&amp;#39;s domain where a relational DB is eye melting clear).&lt;/p&gt;

&lt;p&gt;In any case, years pass, one gets experience and more importantly, experience at a higher level i.e. when one can much more easily understand the inner-workings of complex systems. The problem that stems out of this is that at some point work gets repetitive (if you let it) and one can easily get entrenched in a tool-chain.&lt;/p&gt;

&lt;h4&gt;The half-life of a programmer&lt;/h4&gt;

&lt;p&gt;I&amp;#39;m starting to feel this is the crux of the problem: a programmer hits half-life when he realizes that most of the work he does is repetitive and he or she gets entrenched in a tool-chain. Clearly, if one cannot overcome these slight issues then we have a problem that might lead to a non-programming career or an &lt;em&gt;illusory&lt;/em&gt; one: &amp;quot;team leader&amp;quot;, &amp;quot;CTO&amp;quot;, &amp;quot;lead developer&amp;quot;, &amp;quot;senior developer&amp;quot;, &amp;quot;consultant&amp;quot; etc.&lt;/p&gt;

&lt;p&gt;The issue with those terms is that some programmers start to take it way too seriously whilst completely ignoring what I like to call &lt;em&gt;referential humility&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Most of my heroes: the likes of &lt;a target="_blank" href="http://onestepback.org"&gt;Jim Weirich &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="http://www.sandimetz.com"&gt;Sandi Metz &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and more seem to be in for life (the career) and I find that very reassuring.&lt;/p&gt;

&lt;h4&gt;Referential humility&lt;/h4&gt;

&lt;p&gt;What does that mean in the end? - just a play of words from &lt;em&gt;referential integrity&lt;/em&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Referential integrity is a property of data which, when satisfied, requires every value of one attribute (column) of a relation (table) to exist as a value of another attribute in a different (or the same) relation (table).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words: there&amp;#39;s always something &amp;quot;new&amp;quot; or something we still don&amp;#39;t have a grasp of - hence the end of the road in a programmer&amp;#39;s career is virtually non-existent.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Once you hit this &lt;em&gt;half-life&lt;/em&gt; - choose wisely, work in the end is just another distraction from death, it&amp;#39;s in our best interest to make that distraction authentic.&lt;/p&gt;

&lt;p&gt;Vaguely related to the &lt;em&gt;distraction from death&lt;/em&gt; tangent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.youtube.com/watch?v=wHWbZmg2hzU"&gt;Friedrich Nietzsche via The School of Life &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.youtube.com/watch?v=D9JCwkx558o"&gt;Soren Kierkegaard via The School of Life &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.youtube.com/watch?v=IgbHzFb1hGw"&gt;The Soul of Software by Avdi Grimm &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/23</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/how-to-web-conference-random-thoughts-mostly-live---five-years-later"/>
    <title>How to Web conference, random thoughts, mostly live - five years later</title>
    <content type="html">&lt;p&gt;Almost five years since the last post about How to Web [0] - wow that&amp;#39;s some large slice of space-time continuum. I recall I almost started this blog for stuff like this, good memories [1]. &lt;/p&gt;

&lt;p&gt;Half a decade later,  what&amp;#39;s changed one might ask? Well, the scale looks bigger and so far the talks have been interesting at least the ones I got to see. &lt;/p&gt;

&lt;p&gt;Colette Balou [2] quick talk about being a decent human being i.e. not a total jackass when asking a potential person for advice.&lt;/p&gt;

&lt;p&gt;The idea behind Mendele [3] is quite interesting and Jan Reichelt&amp;#39;s talk explained it eloquently.&lt;/p&gt;

&lt;p&gt;Not bad for half a day.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; [0] &lt;a target="_blank" href="http://2015.howtoweb.co"&gt;2015 How To Web &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; [1] &lt;a target="_blank" href="http://marianposaceanu.com/articles/how-to-web-conference-random-thoughts-mostly-live"&gt;original article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; [2] &lt;a target="_blank" href="https://twitter.com/coletteballou"&gt;@coletteballou &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; [3] &lt;a target="_blank" href="https://www.mendeley.com"&gt;Mendeley &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/22</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/how-to-web-conference-random-thoughts-mostly-live"/>
    <title>How to Web Conference, random thoughts, mostly live</title>
    <content type="html">&lt;p&gt;Sadly, today I’ve arrived a bit late, where &amp;quot;a bit&amp;quot; means a lot; I’vevolatile and utopic place most of the time.&lt;/p&gt;

&lt;p&gt;In very few domains you can take an idea and with small or no resources (&lt;em&gt;money&lt;/em&gt;) and bring it alive and running. I mean, actually thinking about it, how can’t that be exciting ?! Having an idea and turning pure thoughts into something that can actually reach people and the real world... that’s just mind boggling.&lt;/p&gt;

&lt;p&gt;I’ve stumbled over a new concept: &amp;quot;life style business&amp;quot;, apparently you can have two kinds of businesses (assuming they will work): you can go big, invest a lot, get international and all that stuff or you can keep it small as a life-style, you live for your business you _burn _your thoughts making it better and better, I really like that idea, it’s inspiring.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Some heavily 37signals inspired ideas”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Also another interesting concept: &lt;em&gt;bootstrapping&lt;/em&gt;. More on that in another article (it’s problably going to be a Rails article)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;And last update on how to web&lt;/em&gt;: it was awesome and it’s not just about the conference it’s about the great people I’ve met or watched in full action, if they can do it then we can do it also, it’s all about passion and loving what you do and never taking yourself to seriously, self-irony from time to time its great, nobody is perfect, perfection is not the goal, accepting the imperfection and making the best of them is the key.&lt;/p&gt;

&lt;p&gt;All in all, great experience, kudos !&lt;/p&gt;

&lt;p&gt;Biography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://37signals.com/rework/"&gt;rework &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - this one is a must read&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/21</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/ruby-2-2-0-preview-1-quick-rails-benchmarks"/>
    <title>Ruby 2.2.0 Preview 1 quick Rails benchmarks</title>
    <content type="html">&lt;p&gt;Great news everyone! Ruby 2.2 preview 1 has been &lt;a target="_blank" href="https://www.ruby-lang.org/en/news/2014/09/18/ruby-2.2.0-preview1-released/"&gt;released &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;! I&amp;#39;m really curios about the &lt;a target="_blank" href="https://bugs.ruby-lang.org/issues/10137"&gt;Incremental GC &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and &lt;a target="_blank" href="https://bugs.ruby-lang.org/issues/9634"&gt;Symbol GC &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; so let&amp;#39;s run some quick Rails benchmarks on a normal Rails API app.&lt;/p&gt;

&lt;p&gt;First off let&amp;#39;s install the preview via RVM:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rvm install ruby-2.2.0-preview1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After fiddling around about five minutes trying to find a part of the application that doesn&amp;#39;t fail under the Preview I stopped at the simple &lt;code&gt;/profiles&lt;/code&gt; endpoint that just renders a JSON of all profiles, quite simple indeed. Using the trusty &lt;a target="_blank" href="https://github.com/wg/wrk"&gt;wrk &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; I fired up a quick bench:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t10 -c10 -d20s http://localhost:8080/profiles
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The results are as follows:&lt;/p&gt;

&lt;h4&gt;Ruby 2.1.2p95&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 20s test @ http://localhost:8080/profiles
  10 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   255.02ms   25.10ms 372.80ms   67.61%
    Req/Sec     3.21      0.70     5.00     71.13%
  771 requests in 20.01s, 4.40MB read
Requests/sec:     38.53
Transfer/sec:    225.31KB
------------------------------
50%,252 ms
90%,285 ms
99%,328 ms
99.999%,372 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Ruby 2.2.0preview1&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 20s test @ http://localhost:8080/profiles
  10 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   253.27ms   30.64ms 344.75ms   64.21%
    Req/Sec     3.34      0.70     5.00     89.63%
  786 requests in 20.02s, 4.49MB read
Requests/sec:     39.26
Transfer/sec:    229.60KB
------------------------------
50%,251 ms
90%,291 ms
99%,329 ms
99.999%,344 ms
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I&amp;#39;m not really sure that I should interpret them yet it seems that under the Preview we have a slight improvement but within margins of error. At this point I don&amp;#39;t think is the best benchmark for the Preview as we don&amp;#39;t use views thus Rails won&amp;#39;t bloat up the memory with Strings.&lt;/p&gt;

&lt;p&gt;On the memory usage side we have 65M vs 75M (Preview vs. 2.1) so in this scenario we clearly have a winner. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;note&lt;/strong&gt;: this was measured using OSX&amp;#39;s Activity Monitor after wrk finished the benchmark and it&amp;#39;s the average of the unicorn workers sizes.&lt;/p&gt;

&lt;h4&gt;Issues&lt;/h4&gt;

&lt;p&gt;Bundler and all the gems installed without issue but in some cases I got silent failures. The benchmarks were run on an actual working/production Rails 4.0.x app with around 25 gems. Nonetheless all of the gems installed and I could boot up Rails with unicorn and benchmark the simpler endpoints which is great.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;TBD - this is work in progress I will update it with more information&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/20</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/chunked-transfer-encoding-in-rails-streaming"/>
    <title>Chunked transfer encoding in Rails (streaming)</title>
    <content type="html">&lt;p&gt;Anyone that has written a little PHP knows what the &lt;code&gt;flush()&lt;/code&gt; family of functions do. The ideal usage scenario for using chunked transfer[0] is when we have something costly to render e.g. the first three most recent articles on a blog. Why ? one might ask.&lt;/p&gt;

&lt;p&gt;Is rather simple: in a normal request where the server responds with a &lt;code&gt;Content-Length&lt;/code&gt; header the browser will wait until the whole page comes down the wire then it goes loading the assets et al. &lt;/p&gt;

&lt;p&gt;Using the &lt;code&gt;Transfer-Encoding: chunked&lt;/code&gt; header, the server will send chunks of the rendered page back to the browser so in the case of Rails, it starts with the layout and sends out the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; part including assets like js and css. &lt;/p&gt;

&lt;p&gt;It&amp;#39;s clear how this helps the rendering of the page on the client side : get the first chunk containing the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; with assets, immediately start loading the assets while waiting for the rest of the response. Of course, browsers nowadays include lots of micro-optimizations that might already do something similar but still this remains a good practice.&lt;/p&gt;

&lt;p&gt;Implementation wise, you just need to add to your controller methods something like :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YourController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;
    &lt;span class="vi"&gt;@articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;most_recent&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;stream: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="c1"&gt;# other controller logic&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The latest version of Unicorn (4.x) comes by default[1] with support for chunked response. You can always add to your &lt;code&gt;unicorn_config.rb&lt;/code&gt; something like: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# :tcp_nopush This prevents partial TCP frames from being sent out&lt;/span&gt;
&lt;span class="c1"&gt;# :tcp_nodelay Disables Nagle’s algorithm on TCP sockets if true.&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"PORT"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_i&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;
&lt;span class="c1"&gt;# the ENV["PORT"] is a Heroku environment variable&lt;/span&gt;
&lt;span class="n"&gt;listen&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;tcp_nopush: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;tcp_nodelay: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We also have some quirks when using streaming in Rails because of the inversion of template rendering order[2]:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When streaming, rendering happens top-down instead of inside-out. Rails starts with the layout, and the template is rendered later, when its yield is reached .&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;tl;dr: use &lt;code&gt;provide&lt;/code&gt; instead of &lt;code&gt;content_for&lt;/code&gt; when you have multiple calls to &lt;code&gt;content_for&lt;/code&gt; otherwise it will break the purpose of streaming and/or it will concatenate the values from &lt;code&gt;content_for&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There&amp;#39;s also a “small” issue with NewRelic agent and Heroku: you need to disable browser instrumentation or else you&amp;#39;ll get a blank page[3], thankfully the fix is rather trivial:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/newrelic.yml&lt;/span&gt;
  &lt;span class="ss"&gt;browser_monitoring:
    &lt;/span&gt;&lt;span class="c1"&gt;# By default the agent automatically injects&lt;/span&gt;
    &lt;span class="c1"&gt;# the monitoring JavaScript into web pages&lt;/span&gt;
    &lt;span class="c1"&gt;# Turn this to false&lt;/span&gt;
    &lt;span class="ss"&gt;auto_instrument: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&amp;#39;s also &lt;code&gt;ActionController::Live&lt;/code&gt; that can be used to create a simple Rails 4 chat application[4][5].&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Chunked_transfer_encoding"&gt;[0] Chunked transfer encoding &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen"&gt;[1] Unicorn Configurator &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://api.rubyonrails.org/classes/ActionController/Streaming.html"&gt;[2] Rails API - Streaming  &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://icelab.com.au/articles/new-relic-heroku-and-rails-streaming-responses/"&gt;[3] New Relic, Heroku and Rails Streaming Responses &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://robots.thoughtbot.com/post/55421584115/chat-example-app-using-server-sent-events"&gt;[4] Chat Example App Using Server-Sent Events &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://rubysource.com/streaming-with-rails-4/"&gt;[5] Streaming with Rails 4 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/19</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-writing-artisanal-code"/>
    <title>On writing artisanal code</title>
    <content type="html">&lt;p&gt;Every artisan at a certain point in time just stops and starts thinking about the inner-workings of his craft. He ponders on the whole process from an Outside while trying to make connections between apparently dissimilar entities that his craft is made of.&lt;/p&gt;

&lt;p&gt;One might argue that this is a purely subjective endeavor: well, without any formal applicable theories it certainly is ! yet this doesn’t mean it cannot spark insights.&lt;/p&gt;

&lt;p&gt;Coding is a very elusive mixture, it involves formal rules (the language), it can be mixed or even expressed via mathematical modeling and it’s also a first-class subjective practice. Coding is thus artisanal because we have the metaphor of shaping an abstracted real-world idea into another realm. &lt;/p&gt;

&lt;p&gt;Code is ephemeral, it is driven by a set of semantics that must be transmitted. We can see it as a machine, yet one that must be plugged into another machine: the artisan himself, together they create meaning.&lt;/p&gt;

&lt;p&gt;The issue with this is that meaning is hardly quantifiable: it depends on a transient object -&amp;gt; the mind that interprets it thus code is an incomplete little machine. &lt;/p&gt;

&lt;p&gt;The question rises: what characteristics of code have enough in common with all minds that can form plateaus of recognizable values ? Well, if code conveys meaning, we want code that is expressive so that is able to convey its creator semantics to any reader or to put it another way : we want to maximize the function of adherent readers. &lt;/p&gt;

&lt;p&gt;OK, what makes code expressive ? Looking at its form we can have code that is verbose versus code that is succinct. How do we choose which one is better ? We might say that it depends on the code but think about this: let’s say we have two versions of the same implementation of a solution to a problem, now: giving that they have the same quotient of conveying meaning which one we choose ? The answer is clear: the succinct code but only when the quotient approaches the idealized value of one. &lt;/p&gt;

&lt;p&gt;How we maximize this quotient of conveying meaning ? I’ll ponder some more on this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It helps to have a language that is like molding clay rather than carving stone.” - Paul Graham&lt;/p&gt;
&lt;/blockquote&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/17</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/building-a-small-blog-cms-in-rails"/>
    <title>Building a small blog cms in rails</title>
    <content type="html">&lt;p&gt;Yes, building a blog cms in rails is apparently very simple, well to be true it’s simple if you want something really limited like a post model, a couple of static pages, controllers and one for the contact form and you’re settled. Actually for something like this one can use &lt;a target="_blank" href="http://www.sinatrarb.com/"&gt;Sinatra &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; (not to say that Sinatra is better for small applications, but you really don’t need all the Rails stack, of course Rails 3 is more modular and everything, but still).&lt;/p&gt;

&lt;p&gt;That’s why the cms (I really dislike this word) backing up my personal site it’s not going to be something trivial, I’ve actually based it on more general cms I’ve been using for projects I work on.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“It is better to take many small steps in the right direction than to make a great leap forward only to stumble backward.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From the vanilla state, I’ve got &lt;a target="_blank" href="http://en.wikipedia.org/wiki/Role-based_access_control"&gt;RBAC &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; implemented (keeping in mind that I’m the only one posting articles on the site it is a bit useless) - this will cost some _small _performance in the admin but having it online will let me test it and improuve it for actual usage in real world.&lt;/p&gt;

&lt;p&gt;Biography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://fe.com/"&gt;CMS &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - content management system&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.sinatrarb.com/"&gt;Sinatra &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - a DSL for quickly creating web applications in Ruby with minimal effort&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Role-based_access_control"&gt;RBAC &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - Role Based Acces Control&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/16</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/installing-a-rails-mysql-stack-on-my-laptop-was-a-bit-tricky"/>
    <title>Installing a rails mysql stack on my laptop was a bit tricky</title>
    <content type="html">&lt;p&gt;I’ll keep this short: firstly I’ve downloaded the ruby windows installer from &lt;a target="_blank" href="http://rubyinstaller.org/downloads/"&gt;here &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; , at the time of writing the version was _1.9.2-p0. _The problem with this one is that ruby crashes into oblivion from time to time (mostly after lots of page refreshes); after a googler search I’ve found out it’s a windows bug (yeah on *nix works like a charm) so not much really more to do except waiting for the next release, compilling myself the latest version or if you just want to be safe just get the 1.8.7 or .6 release, should work just fine (that’s what I have on my desktop with Rails 2.3.5 and works really great - for developing purposes.&lt;/p&gt;

&lt;p&gt;Ok, time to install rails: _gem install rails -v=2.3.5 -no-rdoc -no-ri _(to be noted I’m using an older version you can skip the -v to install the latest Rails 3). The last two parameters should skip installing the offline documentation (I don’t really use that, so why bother installing all those small files...)&lt;/p&gt;

&lt;p&gt;Next comes the mysql part, this was fun: the normal way to do it is to install &lt;em&gt;gem install mysql&lt;/em&gt;, have your &lt;em&gt;mysql/bin&lt;/em&gt; (that’s where &lt;em&gt;libmySQL.dll&lt;/em&gt; resides) into windows path variable and hope everything works just fine. If it doesn’t, well you can try the classic hack of putting (and by that I mean copying that moving the file) libmySQL.dll intro &lt;em&gt;ruby/bin&lt;/em&gt; folder. Needless to say, none worked on my setup.&lt;/p&gt;

&lt;p&gt;My way: &lt;em&gt;gem install mysql2&lt;/em&gt;, after I modified my database.yml to use the adapter: it worked ! Kudos to brianmario for coding that.&lt;/p&gt;

&lt;p&gt;All done !&lt;/p&gt;

&lt;p&gt;Biography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://rubyinstaller.org/downloads/"&gt;Ruby 1.9.2-p0 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - for windows&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://github.com/brianmario/mysql2"&gt;mysql2 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - source code on github, again kudos to brianmario&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/15</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/tomorrow-is-the-first-day-of-how-to-web-2010-this-should-be-fun"/>
    <title>Tomorrow is the first day of How to web 2010, this should be fun</title>
    <content type="html">&lt;p&gt;Just got in Bucharest, tomorrow I’ll be at &lt;a target="_blank" href="http://www.how-to-web.net/"&gt;How to Web 2010 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. Hope it’s going to be good, the presentations look juicy.&lt;/p&gt;

&lt;p&gt;I hope I’ll meet some inspiring people and hopefully something to burst some new ideas or perspectives about the web. All in all, its not such a simple subject like: &amp;quot;oh, so you do web stuff ? isn’t that nice&amp;quot;, the whole area is getting more and more complex by the day.&lt;/p&gt;

&lt;p&gt;That’s not a bad thing of course, the problem is coping with all that change coming in small bits at high speed. Let’s look at this problem from a programmers perspective, firstly we have the basic stuff, algorithms, patterns, good problem solving skills, testing and all that, but new technologies emerge at a fast pace. How can we cope with all that change ?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“oh, so you do web stuff ? isn’t that nice and simple...”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some time ago I was thinking that having a good/great understanding of the basics would suffice, well experience has proved me wrong. Yes, basics and everything I’ve enumerated are very important, but to actually be able to work into this environment you must be up to date (of course I’m not saying you should know all the HTML5 unfinished specifications, that’s just plain over-voltage).&lt;/p&gt;

&lt;p&gt;Returning to the subject, conferences like this are good, they catch all those new emerging ideas and spread them in big chunks at a small speed so we can get a picture of the whole thing, it’s like a snapshot of the state of the web (too much ?); well the next conference its going to be more specific.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://www.how-to-web.net"&gt;How to web &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - conference link&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/14</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/is-rubinius-2-2-3-production-ready"/>
    <title>Is Rubinius 2.2.3 production ready ?</title>
    <content type="html">&lt;p&gt;Ruby 2.1 was released this Christmas, great news everyone! It sports a better GC (RGenGC - gerational GC), hierarchical method caching, some small syntax changes and non-experimental rafinements. All in all one can expect 5% to 15% performance increase which is quite awesome.&lt;/p&gt;

&lt;p&gt;As I was reading the comments in the hacker news thread related to the release, one of them caught my eye - we need JIT in the MRI Ruby VM. OK but looks like everyone forgets about Rubinius that sports for some time a LLVM-based JIT, native threads, low-pause generational garbage collector and almost perfect support of C extensions.&lt;/p&gt;

&lt;p&gt;Yes we also have JRuby - it might just be the fastest implementation and when coupled with a server like TorqueBox is even faster, the main issue: some C libraries need to be swapped but as one can see later in the article this was just old thinking at play, clearly things are much better now as most gems do support JRuby without any issue.&lt;/p&gt;

&lt;p&gt;So we have this awesome middle-ground so to say between Ruby VMs: it supports both C extensions without a problem (yet, empirically at least, some more exotic gems might fail to install) but somehow it gets ignored ?&lt;/p&gt;

&lt;p&gt;The plan is simple, take one production Rails 4.0.2 app with all its dependences and convert it to Rubinius, install Puma and do some benchmarking then if all good deploy to staging.&lt;/p&gt;

&lt;h3&gt;The setup&lt;/h3&gt;

&lt;p&gt;Rubinius extracted most of the standard library into gems so in order to properly boot any Ruby script that uses them one needs this into the Gemfile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'racc'
gem 'rubysl'
gem 'puma'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rubysl - is a rather cryptic name for ruby standard library gem&lt;/li&gt;
&lt;li&gt;racc is a LALR(1) parser generator written by tenderlove - is also a
hard requirement for Rubinius or else no booting up Rails.&lt;/li&gt;
&lt;li&gt;puma - well this is the best server choice for Rubinius as it supports
native threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Gems that won&amp;#39;t work with Rubinius (will update if I&amp;#39;ll find more):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# gem 'oj'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Remember to comment them out or again Rails won&amp;#39;t boot up.&lt;/p&gt;

&lt;p&gt;For VM install and switching I&amp;#39;m using the good old RVM with the latest version of Rubinius 2.2.3 and Ruby 2.1.0.&lt;/p&gt;

&lt;h3&gt;On to the benchmarks!&lt;/h3&gt;

&lt;p&gt;OK for obvious reasons I can&amp;#39;t share the source code of the app, at some point I might create a public repository with something meaty for&lt;br&gt;
testing. These benchmarks are for a special case only and for some good fun also so before making any decisions based on them &lt;strong&gt;DO TEST&lt;/strong&gt; first on your own.&lt;/p&gt;

&lt;p&gt;The Rails app is actually an API so most of the important parts are disabled (i.e. Streaming DataStreaming Rendering RequestForgeryProtection) also the sprockets railtie.&lt;/p&gt;

&lt;h4&gt;ApacheBench config&lt;/h4&gt;

&lt;p&gt;I used the simple ApacheBench, Version 2.3 - clearly not the ideal tool for benchmarking (one should use siege or something similar) but&lt;br&gt;
for a quick glance like this test it fits the job nicely.&lt;/p&gt;

&lt;p&gt;The command to start it up:&lt;br&gt;
 &lt;code&gt;ab -n400 -c16 -T&amp;#39;application/json&amp;#39; http://localhost:3000/entries&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;unicorn.rb&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/unicorn.rb&lt;/span&gt;
&lt;span class="n"&gt;worker_processes&lt;/span&gt; &lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"WEB_CONCURRENCY"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
&lt;span class="n"&gt;preload_app&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;

&lt;span class="n"&gt;before_fork&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;worker&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="c1"&gt;# et cetera&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The command to start it up: &lt;br&gt;
&lt;code&gt;unicorn_rails -c config/unicorn.rb -p 3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Results after some runs:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level:      16
Time taken for tests:   6.769 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3611600 bytes
HTML transferred:       3346800 bytes
Requests per second:    59.09 [#/sec] (mean)
Time per request:       270.766 [ms] (mean)
Time per request:       16.923 [ms] (mean, across all concurrent requests)
Transfer rate:          521.03 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   1.0      0       5
Processing:    49  267  36.8    270     331
Waiting:       44  266  36.8    269     330
Total:         49  267  36.3    270     331

Percentage of the requests served within a certain time (ms)
  50%    270
  66%    283
  75%    288
  80%    293
  90%    308
  95%    315
  98%    324
  99%    327
 100%    331 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;puma.rb on Rubinius 2.2.3&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/puma.rb&lt;/span&gt;
&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;
&lt;span class="n"&gt;workers&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;preload_app!&lt;/span&gt;

&lt;span class="n"&gt;on_worker_boot&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# et cetera&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The command to start it up: &lt;br&gt;
&lt;code&gt;puma -C config/puma.rb -b tcp://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Results after several runs (so that the JIT can do its magic):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level:      16
Time taken for tests:   9.383 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3590400 bytes
HTML transferred:       3346800 bytes
Requests per second:    42.63 [#/sec] (mean)
Time per request:       375.311 [ms] (mean)
Time per request:       23.457 [ms] (mean, across all concurrent requests)
Transfer rate:          373.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    84  371 105.4    348     731
Waiting:       83  363 104.1    338     728
Total:         84  371 105.3    348     732

Percentage of the requests served within a certain time (ms)
  50%    348
  66%    390
  75%    431
  80%    458
  90%    526
  95%    571
  98%    640
  99%    683
 100%    732 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;puma.rb on JRuby 1.7.9&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/puma.rb&lt;/span&gt;
&lt;span class="n"&gt;threads&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;

&lt;span class="n"&gt;preload_app!&lt;/span&gt;

&lt;span class="n"&gt;on_worker_boot&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on_load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:active_record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;establish_connection&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The command to start it up: &lt;br&gt;
&lt;code&gt;puma -C config/puma.rb -b tcp://localhost:3000&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Gems that need replacement:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# gem 'pg'
gem 'activerecord-jdbcpostgresql-adapter'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Results after several runs (so that the JIT can do its magic):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Concurrency Level:      16
Time taken for tests:   4.019 seconds
Complete requests:      400
Failed requests:        0
Write errors:           0
Total transferred:      3590400 bytes
HTML transferred:       3346800 bytes
Requests per second:    99.53 [#/sec] (mean)
Time per request:       160.760 [ms] (mean)
Time per request:       10.048 [ms] (mean, across all concurrent requests)
Transfer rate:          872.42 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       1
Processing:    35  158  31.5    157     389
Waiting:       34  151  27.1    149     261
Total:         36  158  31.5    157     389

Percentage of the requests served within a certain time (ms)
  50%    157
  66%    166
  75%    173
  80%    177
  90%    189
  95%    204
  98%    232
  99%    260
 100%    389 (longest request)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;The poor Rubinius performance might be related to the racc gem as it might be really slow as detailed in this Github &lt;a target="_blank" href="https://github.com/rubinius/rubinius/issues/2632"&gt;thread &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;14 req/s vs 60 req/s (I disabled cache and the app produces lots of ActiveRecord objects that&amp;#39;s why the numbers are rather low) makes Rubinius, for now, not a good choice for this particular Rails app.&lt;/p&gt;

&lt;h4&gt;Update&lt;/h4&gt;

&lt;p&gt;Thanks to &lt;a target="_blank" href="https://twitter.com/headius"&gt;headius &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; I&amp;#39;ve revised the benchmarks:&lt;br&gt;
- apparently my VM was accessing only one core (thus the initial abysmal performance of Rubinius and JRuby) - bumped to four&lt;br&gt;
- updated all benchmarks and also added JRuby 1.7.9&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   jruby 99.53 #################################
   cruby 59.09 ###################
rubinius 42.63 ##############
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;As one can clearly see from the chart above JRuby is the winner by an impressive margin and with only one gem change I think it deserves to be pushed to staging.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/13</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/how-to-reduce-the-size-of-your-vms"/>
    <title>How to reduce the size of your VMs</title>
    <content type="html">&lt;p&gt;As the host OS I use Windows 8 (just upgraded to 8.1), well why ? one might ask.Is rather simple: Steam and after some dreadful searching and configuring tools I managed to install the basics for writing and deploying code: Vim, Git, PuTTy and a decent console replacement like Console2 [0].&lt;/p&gt;

&lt;p&gt;Of course those aren&amp;#39;t enough as 80% of my coding is *nix dependent and I wouldn&amp;#39;t run it on anything else; actually I&amp;#39;m still thinking of dropping Windows for something like ArchLinux [1]  &lt;em&gt;I fiddled with it and enjoyed all the low level stuff that just doesn&amp;#39;t exist in Ubuntu Desktop&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Needless to say I use lots of VMs: I currently have three images that I use daily: Ubuntu Desktop, Ubuntu Server and an old Windows XP &lt;em&gt;(well, I don&amp;#39;t use it daily more like monthly)&lt;/em&gt;. The problem of using just a few VMs with many projects is that it involves a lot of hdd trashing (i.e. git, deleting and creating lots of files).&lt;/p&gt;

&lt;p&gt;What I did not realize is that my VMs were growing bigger and bigger by the day while their internal hdd space usage stayed mostly the same as I deleted old files and cleaned them up. For reference these were the initial sizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu desktop: &lt;strong&gt;19.7 GB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Ubuntu server:  &lt;strong&gt;8.0 GB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Windows XP :  &lt;strong&gt;7.9 GB&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While trashing the SSD with all the writes and deletes I was expanding the VHDs despite the fact that I deleted the files in the VM. The cause: the actual blocks weren&amp;#39;t nil so running a &lt;code&gt;VM compact&lt;/code&gt; wouldn&amp;#39;t yield any results.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With the --compact option, can be used to compact disk images, i.e. remove blocks that only contains zeroes. This will shrink a dynamically allocated image again; it will reduce the physical size of the image without affecting the logical size of the virtual disk. Compaction works both for base images and for diff images created as part of a snapshot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The fix was clear: one needs from time to time to nullify the free space. After doing some quick research I&amp;#39;ve found easy ways to do it on every platform.&lt;/p&gt;

&lt;h2&gt;On Linux&lt;/h2&gt;

&lt;p&gt;The first version is using a tool like secure delete but with a very fast run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;secure-delete
sfill &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; /
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Where:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;-f&lt;/span&gt;  fast &lt;span class="o"&gt;(&lt;/span&gt;and insecure mode&lt;span class="o"&gt;)&lt;/span&gt;: no /dev/urandom, no synchronize mode.
&lt;span class="nt"&gt;-z&lt;/span&gt;  last wipe writes zeros, not random data.
&lt;span class="nt"&gt;-l&lt;/span&gt;  lessens the security &lt;span class="o"&gt;(&lt;/span&gt;use twice &lt;span class="k"&gt;for &lt;/span&gt;total insecure mode&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;-I&lt;/span&gt;  just wipe space, not inodes
&lt;span class="nt"&gt;-v&lt;/span&gt;  is verbose mode.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Or a much simpler way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bigemptyfile &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4096k
&lt;span class="nb"&gt;sudo rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /bigemptyfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will fill the entire empty space with a null file that is erased at the end. I haven&amp;#39;t tested this too much, it might yield issues but is faster than the first version.&lt;/p&gt;

&lt;h2&gt;On Windows&lt;/h2&gt;

&lt;p&gt;On Windows Xp one just has to download SysinternalsSuite [2] and run &lt;code&gt;sdelete –z&lt;/code&gt; from the command prompt.&lt;/p&gt;

&lt;p&gt;OK once all of this is over: shutdown the VMs and run compact on the VHD. Using VirtualBox just run from the console &lt;code&gt;VBoxManage modifyhd thedisk.vdi --compact&lt;/code&gt;, using VMware, well just click around till you find it.&lt;/p&gt;

&lt;h2&gt;OK time for results:&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu desktop: &lt;strong&gt;11.0 GB&lt;/strong&gt; ~ -45%&lt;/li&gt;
&lt;li&gt;Ubuntu server:  &lt;strong&gt;4.3  GB&lt;/strong&gt; ~ -47%&lt;/li&gt;
&lt;li&gt;Windows XP :  &lt;strong&gt;6.6 GB&lt;/strong&gt; ~ -16%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That&amp;#39;s a whopping &lt;strong&gt;13.7 GB&lt;/strong&gt; out of my main SSD 128G drive that I use as the system drive, not bad, not bad at all !&lt;/p&gt;

&lt;p&gt;Credits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://sourceforge.net/projects/console"&gt;[0]  Console2 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://www.archlinux.org"&gt;[1]  Arch Linux &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://technet.microsoft.com/en-us/sysinternals/bb842062.aspx"&gt;[2]  Sysinternals Suite &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/12</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/improve-rails-performance-by-adding-a-few-gems"/>
    <title>Improve Rails performance by adding a few gems</title>
    <content type="html">&lt;p&gt;Working with Rails for some time you start nitpicking on how to improve it. This is a first in the series of articles regarding on how to improve (even marginally) Rails&amp;#39;s performance.&lt;/p&gt;

&lt;p&gt;I&amp;#39;ll focus on a bunch of gems that speed up, in some cases considerably, small parts of Rails, like the html escaping, the String#blank? and JSON utils.&lt;/p&gt;

&lt;h4&gt;Benchmarking methodology&lt;/h4&gt;

&lt;p&gt;Methodology is a strong word for just running a couple of times in the console &lt;code&gt;wrk&lt;/code&gt; but I&amp;#39;m not searching for the holy grail here, just to get a raw idea.&lt;/p&gt;

&lt;p&gt;I switched from the old apache ab to &lt;a target="_blank" href="https://github.com/wg/wrk"&gt;wrk &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;wrk is a modern HTTP benchmarking tool capable of generating significant&lt;br&gt;
  load when run on a single multi-core CPU.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t10 -c10 -d10s http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This runs a benchmark for 10 seconds, using 10 threads, and keeping 50 HTTP connections open i.e. this should suffice. Just remember to benchmark on your actual app to see the real improvements.&lt;/p&gt;

&lt;h4&gt;The escape_utils gem&lt;/h4&gt;

&lt;p&gt;Just faster all html escaping via the lovely &lt;a target="_blank" href="https://github.com/brianmario/escape_utils"&gt;escape_utils &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; gem. In order to use it in Rails one needs to add an initializer that patches things up:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;begin&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'escape_utils/html/rack'&lt;/span&gt; &lt;span class="c1"&gt;# to patch Rack::Utils&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'escape_utils/html/erb'&lt;/span&gt; &lt;span class="c1"&gt;# to patch ERB::Util&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'escape_utils/html/cgi'&lt;/span&gt; &lt;span class="c1"&gt;# to patch CGI&lt;/span&gt;
  &lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'escape_utils/html/haml'&lt;/span&gt; &lt;span class="c1"&gt;# to patch Haml::Helpers&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt; &lt;span class="no"&gt;LoadError&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt; &lt;span class="s1"&gt;'Escape_utils is not in the gemfile'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The logic to test it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;escape_utils&lt;/span&gt;
  &lt;span class="vi"&gt;@escape_me&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="sh"&gt;
    &amp;lt;body class="application articles_show"&amp;gt;
      &amp;lt;!-- Responsive navigation
      ==================================================== --&amp;gt;
      &amp;lt;div class="container"&amp;gt;
        &amp;lt;nav id="nav"&amp;gt;
      &amp;lt;ul&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/"&amp;gt;&amp;lt;i class="ss-standard ss-home"&amp;gt;&amp;lt;/i&amp;gt;home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/home/about"&amp;gt;&amp;lt;i class="ss-standard ss-info"&amp;gt;&amp;lt;/i&amp;gt;about&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/contact"&amp;gt;&amp;lt;i class="ss-standard ss-ellipsischat"&amp;gt;&amp;lt;/i&amp;gt;contact&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/home/projects"&amp;gt;&amp;lt;i class="ss-standard ss-fork"&amp;gt;&amp;lt;/i&amp;gt;projects&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/tags"&amp;gt;&amp;lt;i class="ss-standard ss-tag"&amp;gt;&amp;lt;/i&amp;gt;tags&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
        &amp;lt;li&amp;gt;&amp;lt;a href="/articles?query=code"&amp;gt;&amp;lt;i class="ss-standard ss-search"&amp;gt;&amp;lt;/i&amp;gt;search&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
      &amp;lt;/ul&amp;gt;
    &amp;lt;/nav&amp;gt;
    &amp;lt;a href="#" class="ss-standard ss-list" id="nav-toggle" aria-hidden="true"&amp;gt;&amp;lt;/a&amp;gt;
&lt;/span&gt;&lt;span class="no"&gt;  HTML&lt;/span&gt;

  &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;inline: &lt;/span&gt;&lt;span class="s2"&gt;"Hello  world &amp;lt;%= @escape_me %&amp;gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With standard Rails:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    35.40ms    3.55ms  64.70ms   91.98%
    Req/Sec   142.19     11.68   164.00     83.12%
  2837 requests in 10.00s, 4.92MB read
Requests/sec:    283.61
Transfer/sec:    503.34KB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the escape_utils gem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    34.06ms    3.89ms  63.92ms   89.10%
    Req/Sec   148.65     13.36   180.00     75.94%
  2960 requests in 10.00s, 5.46MB read
Requests/sec:    295.98
Transfer/sec:    558.72KB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;The fast_blank gem&lt;/h4&gt;

&lt;p&gt;Living under the impression that the blank? method is too slow? say no more and just try the &lt;a target="_blank" href="https://github.com/SamSaffron/fast_blank"&gt;fast_blank &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; gem!&lt;/p&gt;

&lt;p&gt;Just add &lt;code&gt;gem &amp;#39;fast_blank&amp;#39;&lt;/code&gt; to your Gemfile and this should speed up quite nicely the String#blank? method as described in &lt;a target="_blank" href="http://thechangelog.com/fast_blank-stringblank-in-c/"&gt;this article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. For testing I just added this code:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;fast_blank is a simple extension which provides a fast implementation of active support&amp;#39;s string#blank? function&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fast_blank_test&lt;/span&gt;
    &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;

    &lt;span class="n"&gt;strings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\r\n\r\n&lt;/span&gt;&lt;span class="s2"&gt;  "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"this is a test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"   this is a longer test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s2"&gt;"   this is a longer test
      this is a longer test
      this is a longer test
      this is a longer test
      this is a longer test"&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="no"&gt;Benchmark&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bmbm&lt;/span&gt;  &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;strings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Fast Blank &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;    :"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;times&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;nothing: &lt;/span&gt;&lt;span class="kp"&gt;true&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With standard Rails:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.40s   207.72ms   1.58s    92.68%
    Req/Sec     3.10      2.11     6.00     53.66%
  69 requests in 10.01s, 33.08KB read
Requests/sec:      6.90
Transfer/sec:      3.31KB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With the fast_blank gem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.33s   179.56ms   1.41s    93.33%
    Req/Sec     3.07      0.80     4.00     40.00%
  72 requests in 10.00s, 34.52KB read
Requests/sec:      7.20
Transfer/sec:      3.45KB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;The oj gem&lt;/h4&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# oj gem&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'oj'&lt;/span&gt;
&lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'oj_mimic_json'&lt;/span&gt; &lt;span class="c1"&gt;# we need this for Rails 4.1.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The test logic is simple, just serialize all articles into JSON:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SidechannelsController&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationController&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;oj&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="no"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With standard Rails serializers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   108.37ms    5.12ms 134.90ms   83.33%
    Req/Sec    45.76      3.60    55.00     57.69%
  922 requests in 10.00s, 57.41MB read
Requests/sec:     92.17
Transfer/sec:      5.74MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;With oj gem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 10s test @ http://localhost:3000/sidechannels/bench
  2 threads and 10 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    78.06ms    4.43ms  92.83ms   81.31%
    Req/Sec    63.64      5.33    71.00     64.49%
  1277 requests in 10.00s, 79.83MB read
Requests/sec:    127.65
Transfer/sec:      7.98MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Using jemalloc&lt;/h4&gt;

&lt;p&gt;OK, this is not really a gem, if you want to dig into it then do check out my &lt;a target="_blank" href="https://gist.github.com/dakull/5f3d22eff5fffb43e511"&gt;gist &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. On initial testing it won&amp;#39;t yield much performance gains, at least for my use case. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;note&lt;/strong&gt;: it will be included by default in Ruby at some point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;update&lt;/strong&gt;: do try the &lt;a target="_blank" href="https://github.com/kzk/jemalloc-rb"&gt;jemalloc &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; gem by kzk:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;jemalloc

je &lt;span class="nt"&gt;-v&lt;/span&gt; rails s
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Dig into your Rails app&lt;/h4&gt;

&lt;p&gt;Fear not and use &lt;a target="_blank" href="https://github.com/MiniProfiler/rack-mini-profiler"&gt;MiniProfiler &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; with the awesome &lt;a target="_blank" href="http://samsaffron.com/archive/2013/03/19/flame-graphs-in-ruby-miniprofiler"&gt;FlameGraphs &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;  by Sam Saffron.&lt;/p&gt;

&lt;h4&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;Depending on what your app is doing you might want to add to your Gemfile some of these gems, I usually add them all just for good measure (you might want to check your RAM usage and have a full test suite before doing this though).&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;oj&lt;/strong&gt; gem is just great for a Rails based JSON API where you can drop the views and just serialize using representers or your pattern of choice.&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/11</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/a-few-words-on-frontend-performance-optimizations"/>
    <title>A few words on frontend performance optimizations</title>
    <content type="html">&lt;p&gt;I love optimizing stuff, sometimes I even break the rule (when I have time) and optimize just for the sheer fun of it. I’ve been using on my blog &lt;a target="_blank" href="http://requirejs.org/"&gt;&lt;em&gt;RequireJS&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; for some time but the last days I’ve rewritten the whole init script to use &lt;a target="_blank" href="http://labjs.com/"&gt;&lt;em&gt;LABjs&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Why would I do such heresy ?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, mostly because I din’t liked the way RequireJS works with jQuery (this might be a bit outdated): I was loading up a custom file with jQuery and RequireJS bundled together then I would load up other js files &lt;em&gt;normally&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;With LABjs I only have one js file loading, that being LABjs itself, after that I load jQuery and GA via &lt;em&gt;Google CDN&lt;/em&gt;, which is pretty fast: the DOMContentLoaded event triggers a bit more quickly now. This is good stuff to know &amp;amp; use for a busy site with lots of js.&lt;/p&gt;

&lt;p&gt;Hopefully at some point I will post some graphs with some benchmarking. The good stuff is that before jQuery 1.4, you couldn’t have the LABjs script first then load jQuery; that’s because of the way jQuery did the check if the DOM had loaded, yet now they’ve fixed that and all seems to play nicely. &lt;/p&gt;

&lt;p&gt;Next step ? a failsafe if the CDN breaks or takes too long to serve the js files, I’ve researched the code a bit and it’s pretty easy, I shall test it soon.&lt;/p&gt;

&lt;p&gt;Biography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://requirejs.org"&gt;RequireJS &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - before&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://labjs.com"&gt;LABjs &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - now&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/10</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-ruby-2-0-memory-usage-unicorn-and-heroku"/>
    <title>On Ruby 2.0 memory usage, Unicorn and Heroku</title>
    <content type="html">&lt;p&gt;On UNIX-like operating systems (e.g. Linux) there&amp;#39;s the concept of process forking via the &lt;code&gt;fork()&lt;/code&gt; system call. When one calls fork from a parent-process it will spawn a new child-process - that is a copy of its parent, then both processes return from fork. The child-process has an exact copy of the parent&amp;#39;s memory in a separate address space. Due to this clearly not being efficient, UNIX implements the copy-on-write semantics (i.e. CoW): this delays the actual copying of memory until it needs to be written - simple and elegant.&lt;/p&gt;

&lt;h2&gt;The old days of Ruby 1.8.7 and REE&lt;/h2&gt;

&lt;p&gt;Historically, Ruby has been really bad at forking and I&amp;#39;m not referring to the language itself but to its VMs implementations: especially the MRI and the YARV. The issue is the way garbage collection works in these versions, without entering into details, when the GC runs to clear some unused references from memory it changes every object making them &amp;quot;dirty&amp;quot; i.e. CoW fails quickly as the whole memory becomes dirty. Why is this important ? The best Rack compliant servers use pre-forking to max out all CPUs (e.g. &lt;a target="_blank" href="http://unicorn.bogomips.org/"&gt;Unicorn &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, &lt;a target="_blank" href="https://www.phusionpassenger.com/"&gt;Phusion Passenger &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;). &lt;/p&gt;

&lt;p&gt;When you fire up a Unicorn server it creates a number of a priori configured workers via the &lt;code&gt;fork()&lt;/code&gt; system call. On a 64bit Linux, a Rails 3.x, almost vanilla application takes out ~ 70M of memory and a rather complex one ~ 200M, now multiply that to the number of available cores on your system, let&amp;#39;s say eight and we have a memory usage spanning from &lt;em&gt;560M&lt;/em&gt; to a whopping &lt;em&gt;1.6G&lt;/em&gt;. Using Ruby 1.8.7 yields a nice bonus : there&amp;#39;s a CoW friendly version with an updated garbage collection algorithm written by the guys at &lt;a target="_blank" href="http://www.phusion.nl"&gt;Phusion &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - in the form of Ruby Enterprise Edition - &lt;a target="_blank" href="http://www.rubyenterpriseedition.com"&gt;REE &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is not a solution as 1.8.7 nears EOL on June 2013 and REE &lt;a target="_blank" href="https://blog.engineyard.com/2012/ruby-1-8-7-and-ree-end-of-life"&gt;is next &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, nevertheless REE was/is a great piece of software.&lt;/p&gt;

&lt;h2&gt;The recent release of Ruby 2.0&lt;/h2&gt;

&lt;p&gt;Narihiro Nakamura implemented the new GC algorithm - bitmap marking: in a nutshell - the Ruby VM can do its sweeping without actually modifying the objects i.e. CoW works as expected now. For more details you can check out a &lt;a target="_blank" href="http://vimeo.com/38994805"&gt;video &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; about Ruby&amp;#39;s GC and a recent really nice &lt;a target="_blank" href="http://patshaughnessy.net/2012/3/23/why-you-should-be-excited-about-garbage-collection-in-ruby-2-0"&gt;high-level explanation &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; by Pat Shaughnessy.&lt;/p&gt;

&lt;p&gt;When you combine Ruby 2.0 with Unicorn you can get some pretty impressive results. Using this simple script &lt;a target="_blank" href="https://gist.github.com/dakull/5454732"&gt;memstats.rb &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; I checked the memory usage with eight workers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; Memory Summary:
 private_clean                   0 kB
 private_dirty               1,584 kB
 pss                        12,884 kB
 rss                        80,152 kB
 shared_clean                1,984 kB
 shared_dirty               76,584 kB
 size                      275,704 kB
 swap                            0 kB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Based on this &lt;a target="_blank" href="https://gist.github.com/kenn/5105175"&gt;gist &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; : “rss represents the physical memory that is actually used, and it&amp;#39;s comprised of private_clean + private_dirty + shared_clean + shared_dirty” so practically ~ 76M is shared and ~ 1.5M is private. This is a huge step-up from YARV 1.9.3 where there&amp;#39;s no CoW friendly GC i.e. no memory sharing whatsoever. These results are impressive, of course my app is rather trivial (it just runs this blog) so in real ones the ratio will not be as high but still big enough to save lots or ram.&lt;/p&gt;

&lt;h2&gt;What about Heroku ?&lt;/h2&gt;

&lt;p&gt;One Heroku Dyno is limited to 512M of ram after which it starts swapping memory until it crashes and/or Heroku will send a SIGKILL and restart your app - &lt;a target="_blank" href="http://blog.newrelic.com/2013/03/07/new-relics-instances-tab/"&gt;more info &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. Due to the recent routing-miscommunication, we all know that the best server for a Rack compliant app is Unicorn (actually, that should be common knowledge). &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# config/unicorn.rb
worker_processes 8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What happens when you upgrade to Ruby 2.0 ? You can crank up the workers as much as doubling them without hitting the hard quota. &lt;/p&gt;

&lt;h2&gt;Wrap-up&lt;/h2&gt;

&lt;p&gt;Clearly this does not benefit only Heroku users, the upgrade to Ruby 2.0 is painless as in most cases is a drop-in replacement with the added bonus of lower memory usage. The big issue is with apps that run 1.8.7 or REE as upgrading from 1.8.7 to 1.9.3 and 2.0 yields some compatibility problems.&lt;/p&gt;

&lt;h3&gt;Related articles&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://news.ycombinator.com/item?id=3748175"&gt;Ruby GC implementations HN discussion &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://gist.github.com/authorNari/1273387"&gt;Narihiro Nakamura video transcript &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://tech.pro/blog/1247/ruby-versions-demystified"&gt;Ruby Versions Demystified &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://globaldev.co.uk/2013/03/ruby-2-0-0-in-detail"&gt;Ruby 2.0.0 in Detail &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/9</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/how-i-speeded-up-my-browsing-experience-by-a-really-nice-margin"/>
    <title>How I speeded up my browsing experience by a really nice margin</title>
    <content type="html">&lt;p&gt;From time to time I like to optimize a bit my Windows machine, yet mostly all I do is fine tunning, nothing really spectacular. This time while I was browsing the net and thinking about one of my old nags (browser performance loading several tabs, to more exact: Firefox loading up 20+ tabs) I realized something really simple.&lt;/p&gt;

&lt;p&gt;Firstly though, let me explain the problem. When Firefox loads up with a lot of tabs opened since the last session (I leave tabs opened using SessionManager, I use them as a to do list for the next day) it takes very long (30-90seconds) to load them all up, mostly because firefox is reading unchanged files from its cache.&lt;/p&gt;

&lt;p&gt;Back in the day, I’ve tried to solve this problem by saving the cache on a speedy USB stick (this way I would get much better random access time of small files but the sequential speed was really low).&lt;/p&gt;

&lt;p&gt;As was I saying, while searching the interwebs I’ve stumbled on a nice little &lt;a target="_blank" href="http://memory.dataram.com/products-and-services/software/ramdisk"&gt;utility &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; that can create RAM drives, needless to say my old idea just got better, what about a RAM drive especially for Firefox and IE cache ? It doesn’t need to be big (50Mb each would be enough) and I don’t really care about the safety of the date stored there (after all if the cache gets corrupted or lost, the browser will just redownload or a trusty CTRL+F5 would fix the problem).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It was sitting at the back of my mind for too long&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Configured my Ram Drive, moved firefox and IE cache folders there and then reloaded my 20+ tabbed ff. Results ? Well,  just next to amazing, everything runs snappy, but now I’ve found another bottleneck: my CPU.&lt;/p&gt;

&lt;p&gt;Biography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://memory.dataram.com/products-and-services/software/ramdisk"&gt;Ram Drive &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - content management system&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/8</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/the-inherent-entropy-in-our-lives-to-resist-or-to"/>
    <title> The inherent entropy in our lives: to resist or to... </title>
    <content type="html">&lt;p&gt;I’ve started thinking about it after I read this &lt;a target="_blank" href="http://forge38.com/blog/2010/07/fight-entropy-keep-moving-forward"&gt;article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and this tweet got me going for a couple of days: “My whole life I’ve been fighting entropy, maybe it’s time I stop fighting” by &lt;a target="_blank" href="http://twitter.com/#%21/stevenbristol/status/15960363246"&gt;@stevenbristol &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;. I love stuff like this, makes me ponder on what would be the best way one could choose and what would the solution imply, let’s say, in an empirical way.&lt;/p&gt;

&lt;p&gt;Firstly, one must define entropy, so here’s my cherry picked definition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“entropy is a measure of the uncertainty associated with a random variable.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course there are more, but I like this one because it works almost flawlessly as a metaphor for life itself: that random variable = life, entropy being the hazard (one should take this with a grain of whatever he/she likes, it’s merely a &lt;em&gt;reductionist definition&lt;/em&gt; for the purpose of this article).&lt;/p&gt;

&lt;p&gt;So let’s see... at the surface I see two possible solutions: to fight entropy (not necessarily fight it but more in a way of how a sailor doesn’t fight the storm, he embraces the flow of the weaves and the wind so he can make it safely to the other side) or to go with it. I just realized that I’ve been fighting entropy in a bad way, I’m always trying to apply structure to my life and to everything that I do, like work, yet most of the time something like &amp;quot;random(life)&amp;quot; happens and all that abstract structure that exists in my head goes away in a blink of an eye.&lt;/p&gt;

&lt;p&gt;There’s nothing wrong with having structures in your life, it’s a good thing, but one (especially me) must realize that it doesn’t always work out and high expectations go hand in hand with high delusions and the latter ones, hurt like hell. Going back to my point: one must embrace the flow of entropy, yet with a small nuance, not in a deterministic way, I mean, not in the way of letting our lives be driven by sheer randomness of events (that’s actually the second solution: to go with it), that’s bad. I find it hard to define it.&lt;/p&gt;

&lt;p&gt;Why is it bad to go with entropy, to embrace it and let one life be driven by it ? It’s pretty obvious, I think: we lose all little control we have on our lives. It’s clear we cannot be in full control of external things, yet at least we can be in full control of  how we react to entropy (I think that’s the key I needed for the previous paragraph).&lt;/p&gt;

&lt;p&gt;The solution: one must learn to be in control of his/hers reactions to entropy. How do we do that ? It varies from person to person, one might code an app for that or someone else (like me) might just realize this and try to apply it from now on.&lt;/p&gt;

&lt;p&gt;In the end, I wish you all the best and a Merry Christmas.&lt;/p&gt;

&lt;p&gt;Bibliography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Entropy_%28information_theory%29"&gt;1 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - wiki definition&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://forge38.com/blog/2010/07/fight-entropy-keep-moving-forward"&gt;Fight Entropy! Keep Moving Forward! &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - nice article&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://twitter.com/#%21/stevenbristol/status/15960363246"&gt;@stevenbristol &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - he started it all !&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/7</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-font-rendering-consistency-across-browsers"/>
    <title>On font rendering consistency across browsers</title>
    <content type="html">&lt;p&gt;Back in the day (i.e. when Firefox&amp;#39;s version was a single digit) font rendering was at its best on Mozilla&amp;#39;s browser. I remember comparing Firefox&amp;#39;s anti-aliasing to the new-kid-on-the-block Chrome : the difference was huge. &lt;/p&gt;

&lt;p&gt;Time went by and we all know what happened : most web developers found themselves using Chrome as their main browser for doing their work and this is exactly what happened to me. Somehow the font rendering issue went under the radar.&lt;/p&gt;

&lt;p&gt;Fast-forward to today : I fired up Firefox to check the rendering of my blog and surprise: &lt;em&gt;faux&lt;/em&gt; &lt;em&gt;bold&lt;/em&gt; on all my main body text. I&amp;#39;ve tried the classic &lt;code&gt;font-weight: normal&lt;/code&gt; and &lt;code&gt;opacity: 0.99&lt;/code&gt; with no luck. &lt;/p&gt;

&lt;p&gt;After some major goggling around I&amp;#39;ve found there&amp;#39;s a special CSS property that addresses this namely : &lt;code&gt;text-stroke&lt;/code&gt;. The &lt;a target="_blank" href="http://css-tricks.com/beefing-up-dull-text-in-webkit/"&gt;irony &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; : the article referencing this was trying to replicate the exact opposite i.e. making text render in Webkit browsers in a faux bold style à la Firefox. &lt;/p&gt;

&lt;p&gt;Playing a bit with the property in Chrome one can easily duplicate the FF rendering style with &lt;code&gt;text-stroke: 0.35px&lt;/code&gt; and by setting it to &lt;code&gt;0px&lt;/code&gt; we practically reset it ergo using the same in FF will reset its rendering. Sadly as you can see in this lovely table by &lt;a target="_blank" href="http://caniuse.com/text-stroke"&gt;caniuse &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; Firefox and Opera don&amp;#39;t support it, well, Opera by going the Webkit way, it &lt;em&gt;will&lt;/em&gt; support it soon. &lt;/p&gt;

&lt;p&gt;How about Internet Explorer ? Good question indeed ! and since IE10 is out for some time I updated my old version 9 (it needed a restart of course) and fired it up. Results : &lt;em&gt;horrendous&lt;/em&gt; text rendering and a very snappy experience.&lt;/p&gt;

&lt;p&gt;I&amp;#39;ll probably do some testing (all of this was done on Windows) on Linux and OS X soon, there I hope/should get more consistent results. &lt;/p&gt;

&lt;p&gt;There&amp;#39;s also: &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;css
-webkit-font-smoothing: none;
-webkit-font-smoothing: antialiased;
-webkit-font-smoothing: subpixel-antialiased;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;yet it doesn&amp;#39;t seem to do much. &lt;/p&gt;

&lt;p&gt;It seems that font rendering is &lt;em&gt;not&lt;/em&gt; consistent at all under the Windows platform, I reckon some of these problems are also due to the hardware acceleration implementation. I really dislike how Firefox renders fonts via the CSS &lt;code&gt;@font-face&lt;/code&gt; property (and yes the Verb typeface is optimized for web). &lt;/p&gt;

&lt;p&gt;The issue is clearly visible when using font sizes in the range of 16 ~ 32 pixels. On smaller sizes the effect is actually useful as it makes the text more readable.&lt;/p&gt;

&lt;h2&gt;Update № 1&lt;/h2&gt;

&lt;p&gt;There&amp;#39;s a simple fix for Firefox : detection is done via JavaScript and it just adds a class to the body element, then we can do :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;css
.firefox_is_bad section p { opacity: 0.75; }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;#39;s just a joke I still love Mozilla&amp;#39;s browser. &lt;/p&gt;

&lt;p&gt;The code for easily detecting Firefox :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;coffeescript
is_firefox: -&amp;gt;
  if window.mozInnerScreenX is undefined then false else true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Update № 2&lt;/h2&gt;

&lt;p&gt;Status on OS X : same issue yet the native antialiasing is somewhat better compared to the win platform. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-webkit-font-smoothing&lt;/code&gt; css property seems to work here in Chrome the rendering looks like :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://dl.dropbox.com/u/145562/none.png"&gt;none &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://dl.dropbox.com/u/145562/antialiased.png"&gt;antialiased &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://dl.dropbox.com/u/145562/subpixel-antialiased.png"&gt;subpixel-antialiased &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As one can clearly see using &lt;code&gt;-webkit-font-smoothing: antialiased;&lt;/code&gt; renders the text best, somehow I was living under the impression that subpixel antialiasing would render better.&lt;/p&gt;

&lt;p&gt;Hopefully this will get fixed at some point. &lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/6</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/programatica-2010-after-thoughts"/>
    <title>Programatica 2010 after thoughts</title>
    <content type="html">&lt;p&gt;Yes, I’ve attended one more conference &lt;a target="_blank" href="http://www.agora.ro/conferinta/programatica-2010"&gt;&lt;em&gt;Programatica 2010&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, this one was axed on programming. It started with some interesting to not so interesting presentations of the partners. What I’ve loved about them, was that they contrasted each-other pretty nicely to a point when it was actually funny.&lt;/p&gt;

&lt;p&gt;I’ve observed something interesting about the big names, like IBM, Microsoft and Oracle: they’re starting to converge their developer tools to open-source inspired models. How ? Well, let’s take their presentations one by one: &lt;a target="_blank" href="http://www.oracle.com/technetwork/developer-tools/adf/overview/index.html"&gt;&lt;em&gt;Oracle&lt;/em&gt; ADF &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - this is an interesting framework for developing enterprise class applications, for what I’ve seen is a set of extensions to JavaEE in the vein of the &lt;a target="_blank" href="http://www.springsource.org/"&gt;Spring Framework &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; this is nice and all that but I don’t really like uber-complex IDE’s (they might appear to save you time but you have to learn all the intricacies of how they work, also they might have bugs so my question is: Why spend time on finding solutions to the IDE bug when I could find solutions to the actual bugs in my application ?)&lt;/p&gt;

&lt;p&gt;Next in line: IBM Jazz open-source platform that can integrate their well-known tools (like &lt;em&gt;RUP&lt;/em&gt;) and all those nice pre-made processes for requirements, testing, developing, artifacts management and all that but with a nice open-source twist on them while also trying to make them flexible in an &lt;a target="_blank" href="http://agilemanifesto.org/"&gt;Agile &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; way. The path here its obvious, again open-source is the root of the big change, it’s an adaptation.&lt;/p&gt;

&lt;p&gt;And lastly but not last we have Microsoft with their &lt;a target="_blank" href="http://www.microsoft.com/windowsazure/"&gt;&lt;em&gt;Azure&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; cloud platform (I kinda like it, should scale pretty well and its not that pricey compared to the rest, of course I haven’t tested it so all that might be just gibberish) - they support open-source development in &lt;a target="_blank" href="http://www.codeplex.com/"&gt;&lt;em&gt;SourceForge&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; kind of way, all the community projects are hosted there - like PHP SDK for IIS.&lt;/p&gt;

&lt;p&gt;On Azure you can pretty much run anything from java to ruby and of course their beloved .NET. The problem with it is that if you want to run a really small app, you don’t really have a plan for that (besides trials and a free limited to 25 machine hours version), they start at 59$ or something like that, but you get 20 instances which is nice for a big app, one might guess. Also nice, is that their platform is &lt;em&gt;mostly&lt;/em&gt; complete and somehow &lt;em&gt;on par&lt;/em&gt; with clouds like Rackspace or Heroku.&lt;/p&gt;

&lt;p&gt;Ok, before finishing the post, I must write about &lt;a target="_blank" href="http://lucadezmir.com/interviuri/adrian-amariei/"&gt;Andrian Amariei &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;’s speech about how programmers tend to see things and sometimes the whole world in a restricted dualistic way: black or white, 1 or 0,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We’re being held captive between the walls of our own mind”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The thing is that the real world is made of shades of grey and not absolute truths, we must keep that in mind and wake up !&lt;/p&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/5</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-unnamed-abstracts-entities-and-happiness"/>
    <title>On unnamed abstracts, entities and happiness</title>
    <content type="html">&lt;p&gt;When I look upon my day to day behavior the pattern that pops out is that of abstracted sets of ideas interchanging one another or making connection to other entities, bringing them in the foreground, into my field of subjectivity.&lt;/p&gt;

&lt;p&gt;We try to to make perception ours thus abstracting it away in nicely grouped entities that can relate to one another. I&amp;#39;m doing this daily e.g when I open my browser and visit a webpage, what I&amp;#39;m actually doing is accessing an abstracted entity from my memory : a browser, a webpage, the Internet.&lt;/p&gt;

&lt;p&gt;The issue here is that a subjective quotient comes into play that creates leaky abstractions. Reductionist views in which details dissolve without a trace, one could argue that by themselves abstractions can be viewed as a reductionist view of perception or better yet : semantic compression of streaming information.&lt;/p&gt;

&lt;p&gt;We must draw a line between what a “good” abstraction would involve and&lt;br&gt;
a leaky one would dissolve. Seems like we have more questions than facts.&lt;br&gt;
Looking from another angle and replacing the &amp;quot;good&amp;quot; with the standard&lt;br&gt;
definition of utilitarianism :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Utilitarianism&lt;/em&gt; is a theory in normative ethics holding that the proper course of action is the one that maximizes utility, specifically defined as maximizing happiness and reducing suffering.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;From this perspective a proper abstraction is one that maximizes utility (happiness could be a far product of this). Utility in the sense that while keeping semantic compression close to an ideal ratio our abstraction preserves its most important details; by contrast a leaky one would miss them which in relationship with other entities this would cause wrong interpretations and thus unhappiness. &lt;/p&gt;

&lt;p&gt;Getting back to reality: all abstractions are leaky, the important thing is not to miss those capital details that could blur everything up. Our perception is by definition subjective but we also apply a semantic compression to different chunks of it to be able to easily work with them.&lt;/p&gt;

&lt;p&gt;This leads to another layer of subjectivity, the key is that we should be eternally vigilant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://utilitarianphilosophy.com/definition.eng.html"&gt;Utilitarianism 1 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - a much better description&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://en.wikipedia.org/wiki/Utilitarianism"&gt;Utilitarianism 2 &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - quote source&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/4</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-semantic-ruby-block-constructs"/>
    <title>On semantic Ruby block constructs</title>
    <content type="html">&lt;p&gt;I was cruising around my Github notifications and I found an interesting open issue created by Jim Weirich : “The Semantic Rule for { } VS do/end” in the Ruby style guide &lt;a target="_blank" href="https://github.com/bbatsov/ruby-style-guide"&gt;repository &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The semantic rule says use &lt;code&gt;{ }&lt;/code&gt; for blocks where the primary purpose of the block is to return a value, use do/end for blocks where the primary purpose of the block is to execute side effects.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The main issue with this rule is that it implies the existence of some cases when one must use &lt;code&gt;do/end&lt;/code&gt; on a single line. So what&amp;#39;s the problem with this ? Well, most will argue it&amp;#39;s not very good looking, it&amp;#39;s verbose and it feels a bit hacky e.g. :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# one-liner that abides to the semantic rule
(1..10).each do |i| some_instance.side_effect = i end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;versus :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# idiomatic Ruby one-liner
(1..10).each { |i| some_instance.side_effect = i } 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I would say the difference is not that big and by abiding to the semantic rule one could make code more readable. Moreover it&amp;#39;s not only about the aesthetics of code but also on its inherent semantics.&lt;/p&gt;

&lt;p&gt;This can branch out in two directions : &lt;em&gt;good&lt;/em&gt; i.e. improves codes readability or &lt;em&gt;bad&lt;/em&gt; i.e. it might be confusing to developers that don&amp;#39;t know how to interpret it.&lt;/p&gt;

&lt;p&gt;This can also be applied to the lambda notation :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt; { ... }
-&amp;gt;(obj) { ... }
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;versus&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-&amp;gt; do ... end
-&amp;gt;(obj) do ... end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Lambdas have one more semantic bagagge so to say: I usually use them to do some computation and return the result to be used somewhere else without creating side-effects for everything else procs are better suited.&lt;/p&gt;

&lt;p&gt;One could argue that the &lt;code&gt;{ }&lt;/code&gt; VS &lt;code&gt;do/end&lt;/code&gt; distinction is superfluous for lambdas.&lt;/p&gt;

&lt;p&gt;Pondering on it, the visual difference doesn&amp;#39;t seem colossal so I think I&amp;#39;ll adhere to this rule in my codebases from now on.&lt;/p&gt;

&lt;p&gt;Credits :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://devblog.avdi.org/2011/07/26/the-procedurefunction-block-convention-in-ruby"&gt;Avdi Grimm detailed article on the issue &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc"&gt;Jim Weirich original article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://github.com/bbatsov/ruby-style-guide/issues/162"&gt;Ruby style guide issue &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://gist.github.com/dakull/5507946"&gt;Proc VS lambda &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/3</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/on-ruby-modules-and-concerns"/>
    <title>On Ruby modules and concerns</title>
    <content type="html">&lt;p&gt;Modules are useful for highly specialized code that can be injected into other classes for fun and profit or for creating Namespaces.  An interesting approach is to use modules as an alternative to classical inheritance (without its the usual trade-offs) : the core idea is to share an abstracted &lt;em&gt;role&lt;/em&gt; that can be included in a set of different classes.&lt;/p&gt;

&lt;p&gt;One should start with the simplest definition of the word &lt;em&gt;role&lt;/em&gt; : &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The function assumed or part played by a person or thing in a particular situation.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;“... &lt;em&gt;or thing in a Particular situation&lt;/em&gt;” is key here so  how we &lt;em&gt;encode&lt;/em&gt; particulars in an object oriented way ? We create classes: blueprints that we can later instantiate and use at will. Next :  &lt;em&gt;a function assumed by a thing&lt;/em&gt; i.e.  a well defined function that is used by objects in particular situations; more exactly different objects start playing a common role and when this happens it becomes clear that the common behaviour can be extracted into a module.&lt;/p&gt;

&lt;p&gt;A &lt;em&gt;concern&lt;/em&gt; is a nice abstraction that simplifies sharing behaviour using modules. It can be found in &lt;a target="_blank" href="http://api.rubyonrails.org/classes/ActiveSupport/Concern.html"&gt;ActiveSupport::Concern &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; and it just removes some boiler-plate code e.g. :&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;included&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ClassMethods&lt;/span&gt;
  &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class_eval&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="c1"&gt;# ..&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;tl;dr : &lt;code&gt;included&lt;/code&gt; gets called when we do something like &lt;code&gt;include MyModule&lt;/code&gt; into a class, the &lt;code&gt;base&lt;/code&gt; argument is the object in which we include the module i.e. a Class instance (remember that classes in Ruby are instances of Class - interestingly the class of Class is again Class, so &lt;em&gt;it&amp;#39;s all turtles all the way down&lt;/em&gt;). &lt;/p&gt;

&lt;p&gt;The abstracted code looks like this : &lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;M&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Concern&lt;/span&gt;
  &lt;span class="n"&gt;included&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ClassMethods&lt;/span&gt;
    &lt;span class="c1"&gt;# this gets included automatically &lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Bit cleaner but the classic way is not bad either as long as you know what&amp;#39;s actually going on. We all love Rails&amp;#39;s “magic” yet one could say that using it without having any clue on what&amp;#39;s going on behind the scenes is an exercise in futility. &lt;/p&gt;

&lt;p&gt;Going back to our Concerns : what I do like is how it easily resolves dependencies -&amp;gt; one can ponder further upon the &lt;a target="_blank" href="https://github.com/rails/rails/blob/5b50c7d4d569069a1826c2876d654da7b047acc8/activesupport/lib/active_support/concern.rb#L107"&gt;source &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; -&amp;gt; some interesting coding going on in there.&lt;/p&gt;

&lt;p&gt;Some simple uses for Concerns ? One can use them to apply the &lt;a target="_blank" href="http://martinfowler.com/refactoring/catalog/extractMethod.html"&gt;&lt;em&gt;Extract Method&lt;/em&gt; &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; refactoring i.e. “code fragments that can be grouped together”. In &lt;a target="_blank" href="http://37signals.com/svn/posts/3167-code-spelunking-in-the-all-new-basecamp"&gt;this 37signals article &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;  we have a simple example of a before filter that does some cookie mingling. The only issue here is that taking before filters out of the controller  might result in some hard to debug code, one could argue that seeing all the filters per controller requires less mental switching than parsing &lt;code&gt;include Ajax&lt;/code&gt; -&amp;gt; “oh wait this sets a before filter”. &lt;/p&gt;

&lt;p&gt;Some much better usage one can find in this highly referenced article by DHH : &lt;a target="_blank" href="http://37signals.com/svn/posts/3372-put-chubby-models-on-a-diet-with-concerns"&gt;“Put chubby models on a diet with concerns” &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; e.g. a Taggable, Visible and Dropboxed are clusters of functionality that can be injected in other classes so that they can share a common role.&lt;/p&gt;

&lt;p&gt;Next stop up the ladder is the  &lt;a target="_blank" href="https://en.wikipedia.org/wiki/Data,_context_and_interaction"&gt;DCI paradigm &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, from a bird eye view, this is a more formalized way of using/sharing roles that also includes a scaffold framework of thinking for organizing code. I&amp;#39;ll get into the nitty-gritty details in a new post.&lt;/p&gt;

&lt;p&gt;Interesting stuff &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://stackoverflow.com/questions/156362/what-is-the-difference-between-include-and-extend-in-ruby"&gt;Include vs. extend detailed for reference &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://martinfowler.com/refactoring/catalog/index.html"&gt;Martin Fowler - Refactoring catalog &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="http://yehudakatz.com/2009/11/12/better-ruby-idioms"&gt;Better Ruby Idioms by Yehuda Katz &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://speakerdeck.com/qrush/basecamp-next-code-spelunking"&gt;Basecamp Next - Slides &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/1</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2025-07-05T21:20:05Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/using-firebug-as-a-scalpel-yet-not-for-web-development"/>
    <title>Using Firebug as a scalpel, yet not for web development</title>
    <content type="html">&lt;p&gt;Needless to say, I’m an avid user of &lt;a target="_blank" href="http://getfirebug.com/"&gt; Firebug &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt;, let’s face it: without it, web development would suck big time, yes we do have other variants but firebug remains the status quo, for me at least. Yet this is not an article about how to develop web apps with Firebug, no, is an article about how one can use it to have a little fun and be more practical.&lt;/p&gt;

&lt;p&gt;Everyone hates ads, well maybe &lt;em&gt;hate&lt;/em&gt; is a bit too much, everyone dislikes ads, I know I do. In Firefox we have the &lt;a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/1865"&gt;AdBlock Plus &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; extension. Does the job pretty nicely, yet nicely, doesn’t fit my taste, why ? you may ask. Well firstly, its not that fast and breaks my development by taking me out of the &lt;em&gt;flow&lt;/em&gt; (it does that by making Firefox and the pages I visit slow - this is an empirical observation, it’s been a while since I’ve used the extension) and secondly because&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;on some sites I actually want to see the ads and maybe even click the darn things so I can show my support&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(and that’s a big chunk of the sites I visit daily).&lt;/p&gt;

&lt;p&gt;Going back on track: today we use web apps for a considerable time: I open ad-supported web apps in the browser and keep them opened for the entire session (since I rarely close Firefox, that could mean a full day, or even more - I use hibernate). What I don’t like about them ? The ads of course, now this may come in conflict with the previous paragraph but I’ve got a good reason for killing them ads: most of the time they’re flash and they change at intervals of time, problem with that and Firefox in long sessions is that it memory leaks like crazy (it’s something related to the extensions, I reckon) and Flash and those ads seem to help a lot.&lt;/p&gt;

&lt;p&gt;By now, it’s pretty obvious: I kill them with Firebug. How ? Pretty easily, firstly I find the node, the container for the iframes or whatever they use, and delete that node using Firebug since the node doesn’t exist anymore, gone are the ads. One may ask “But you do this every time ? Why don’t you use some extension like greasemonkey or something to do that for you ?” My answer: I have long sessions and I only do that for 1-2 sites that I keep opened all the time so it doesn’t bother me, also using &lt;a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/748/"&gt;Greasemonkey &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; or something similar would add up to the resources that Firefox is using, probably getting me somewhere near the ad-block version.&lt;/p&gt;

&lt;p&gt;In conclusion, Firebug can be useful for something else then web development.&lt;/p&gt;

&lt;p&gt;Bibliography:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="http://getfirebug.com/"&gt;Firebug &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - what I use&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/1865"&gt;AdBlock Plus &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - what I used to use&lt;/li&gt;
&lt;li&gt;&lt;a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/748/"&gt;Greasemonkey &lt;sup&gt;&lt;svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="13" height="13" viewBox="0 0 13 13"&gt;&lt;path fill="#b30600" d="M0.48 10.38q-0.132 0.12-0.228 0.12h-0.060q-0.192-0.048-0.192-0.228 0.096-2.58 1.656-4.056t4.344-1.464v-2.256q0-0.156 0.144-0.216 0.132-0.072 0.264 0.024l5.496 4.248q0.096 0.072 0.096 0.192t-0.096 0.204l-5.496 4.248q-0.060 0.060-0.156 0.060-0.036 0-0.108-0.036-0.144-0.060-0.144-0.216v-2.256q-1.956 0-3.408 0.408t-2.112 1.224z"&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/sup&gt;&lt;/a&gt; - what I need to test some more&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Marian Posăceanu</name>
    </author>
  </entry>
</feed>
