<?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-08-06T09:39:25Z</updated>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/48</id>
    <published>2026-08-06T09:31:05Z</published>
    <updated>2026-08-06T09:39:25Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/prs-are-dead-long-live-prs"/>
    <title>PRs are dead, long live PRs?</title>
    <content type="html">&lt;p&gt;Pull requests are an important part of programming culture. They serve multiple purposes and vary across teams, organisations and even technologies. In essence, they reflect how a team constantly evolves, both through its individual members and its code.&lt;/p&gt;
&lt;p&gt;They can be used to teach, fix bugs, refactor code, spark new ideas and even make friends through open source.&lt;/p&gt;
&lt;p&gt;The question is: how will they evolve in the age of LLMs? It&amp;#39;s trivial to create a PR now and even review it. With models like Sol working alongside a senior developer, the whole process can be semi-automated and still result in well-reviewed, high-quality code.&lt;/p&gt;
&lt;p&gt;But what about the other aspects surrounding a PR? What about teaching, knowledge transfer, the social aspect, the culture which they help to build?&lt;/p&gt;
&lt;p&gt;All of that seems to be gone to some degree when someone opens a PR with 10K+ LoC of changes. It seems PRs will become artefacts of a bygone era.&lt;/p&gt;
&lt;p&gt;The way forward is not clear. How do we keep the culture around PRs? How do we keep the human aspect? It will all live in &amp;quot;/agents/*&amp;quot;, in Markdown files which slowly evolve.&lt;/p&gt;
&lt;p&gt;It seems our culture will live on in Markdown artefacts, at least for now. The PR and its humanity will depend on how much of it is produced by an LLM and how much by the team itself.&lt;/p&gt;
&lt;p&gt;We now have to make an effort to stay human because we might lose the culture around these artefacts if we fully replace them with LLM workflows.&lt;/p&gt;
&lt;p&gt;How are we going to transfer knowledge, share idioms and help other developers grow if the PR is dead? I remember getting comments on my PRs more than a decade ago, looking up what they meant and reading articles and books just to get better at the craft. That shaped and enlightened me. I would not be the developer I am today without the feedback, the idioms and the culture that PRs helped foster.&lt;/p&gt;
&lt;p&gt;Sources&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.com/charliermarsh/status/2085154924564709465?s=20"&gt;https://x.com/charliermarsh/status/2085154924564709465?s=20&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/24</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-02T11:23:23Z</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;aside class="article-reference" aria-label="Reference"&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;/aside&gt;&lt;p&gt;I had this almost random idea of when a programmer hits his or her career &lt;em&gt;half-life&lt;/em&gt;. &lt;/p&gt;

&lt;h2&gt;Tangents&lt;/h2&gt;
&lt;p&gt;First a few points that need explaining: &lt;em&gt;almost random&lt;/em&gt; is in the sense that this time next year 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 nonsense.&lt;/p&gt;

&lt;h2&gt;Relation to age&lt;/h2&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 empirical 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 domain where a relational DB is eye-meltingly 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;h2&gt;The half-life of a programmer&lt;/h2&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;h2&gt;Referential humility&lt;/h2&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;h2&gt;Conclusion&lt;/h2&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/1</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical and safety note (2026):&lt;/strong&gt; Firebug reached end of life; its functionality moved into Firefox’s built-in Developer Tools. Adobe ended Flash Player support on December 31, 2020, and Flash is unsafe to install. Deleting a DOM node in Developer Tools is temporary and local: it does not change the server, undo requests already made, or provide persistent content blocking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current status&lt;/strong&gt;
&lt;a target="_blank" href="https://hacks.mozilla.org/2017/10/saying-goodbye-to-firebug/"&gt;Mozilla: Saying Goodbye to 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; · &lt;a target="_blank" href="https://www.adobe.com/products/flashplayer/end-of-life-alternative.html"&gt;Adobe Flash Player EOL &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;/aside&gt;&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, it 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, it’s 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 don’t I 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 leaks memory 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, first, 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 an 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 than web development.&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&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>
  <entry>
    <id>tag:marianposaceanu.com,2005:Article/15</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2026):&lt;/strong&gt; The conference URL and discussion refer to the 2010 event. HTML5 was unfinished then; W3C published HTML5 as a Recommendation in 2014, and HTML is now maintained as a Living Standard by WHATWG.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current context&lt;/strong&gt;
&lt;a target="_blank" href="https://www.howtoweb.co/"&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; · &lt;a target="_blank" href="https://html.spec.whatwg.org/"&gt;HTML Living Standard &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;/aside&gt;&lt;p&gt;Just got to 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, it’s 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 programmer’s 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 in 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 is going to be more specific.&lt;/p&gt;

&lt;h2&gt;Reference&lt;/h2&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/8</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; This essay uses information-theory entropy as a metaphor for uncertainty in life; it does not claim that the mathematical definition directly models a human life. The Forge38 and Twitter URLs below are retained as archival provenance and may no longer resolve.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Background&lt;/strong&gt;
&lt;a target="_blank" href="https://people.math.harvard.edu/~ctm/home/text/others/shannon/entropy/entropy.pdf"&gt;A Mathematical Theory of Communication &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://plato.stanford.edu/entries/information/"&gt;Information — Stanford Encyclopedia of Philosophy &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;/aside&gt;&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; it 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 waves 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’s 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/her 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;h2&gt;References&lt;/h2&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/27</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; This is a strongly worded 2010 opinion, not hiring or accessibility guidance. Modern web work includes both deep specializations and generalist roles; basic HTML and CSS syntax can be approachable, while production UI work also carries substantial accessibility and standards obligations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current standard&lt;/strong&gt;
&lt;a target="_blank" href="https://www.w3.org/TR/WCAG22/"&gt;Web Content Accessibility Guidelines (WCAG) 2.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;/p&gt;
&lt;/aside&gt;&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 following).&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 who do programming need to bracket themselves into this kind of namespacing.&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&lt;/em&gt;, &lt;em&gt;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 get 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 UIs 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 are 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 let’s 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/25</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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 could be changed in Rails migrations via:&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2015; updated context 2024):&lt;/strong&gt; These excerpts describe Rails 4.2-era behavior, not current defaults. RFC 4122 was obsoleted by RFC 9562 in May 2024; UUIDv4 has 122 random bits and implementations should use a CSPRNG. UUIDs are identifiers, not authentication secrets or bearer capabilities. Current PostgreSQL provides core UUID generation; the &lt;code&gt;uuid-ossp&lt;/code&gt; path below is historical. The non-OpenSSL fallback discussion is specific to the linked Ruby revision.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.rfc-editor.org/rfc/rfc9562.html"&gt;RFC 9562 &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://docs.ruby-lang.org/en/master/SecureRandom.html"&gt;Ruby &lt;code&gt;SecureRandom&lt;/code&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;a target="_blank" href="https://www.postgresql.org/docs/current/functions-uuid.html"&gt;PostgreSQL UUID functions &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;/aside&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;p&gt;parameter, 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 Ruby&amp;#39;s stdlib, the UUID method is found in &lt;code&gt;SecureRandom&lt;/code&gt;:&lt;/h5&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;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 the &lt;code&gt;uuid-ossp&lt;/code&gt; PostgreSQL extension:&lt;/h5&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;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 difference examined here is how the random bits are 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;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;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;It 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;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;p&gt;and the &lt;code&gt;uuid_generate_internal&lt;/code&gt; relevant part of the function:&lt;/p&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;p&gt;As we can see there&amp;#39;s a call to &lt;code&gt;uuid_generate_random&lt;/code&gt; - what is it?&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 a 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. PRNGs 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 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;h3&gt;References&lt;/h3&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; 
[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;
[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;
[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;
[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;
[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;
[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/21</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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 curious 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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2014):&lt;/strong&gt; Ruby 2.2.0-preview1 was prerelease software and must not be used now. Ruby 2.2 reached end of life on 31 March 2018; Rails 4.0.x and the tested stack are unsupported. These are single 20-second local runs with unknown hardware/configuration and no confidence intervals; the roughly 1.9% throughput difference and post-run Activity Monitor averages do not establish performance or memory improvements.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.ruby-lang.org/en/news/2014/09/18/ruby-2.2.0-preview1-released/"&gt;Release announcement &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.ruby-lang.org/en/downloads/branches/"&gt;Ruby branch status &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://rubyonrails.org/maintenance"&gt;Rails maintenance policy &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;/aside&gt;&lt;p&gt;First off let&amp;#39;s install the preview via RVM:&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rvm install ruby-2.2.0-preview1
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After fiddling around for 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 JSON for 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;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t10 -c10 -d20s http://localhost:8080/profiles
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The results are as follows:&lt;/p&gt;

&lt;h2&gt;Ruby 2.1.2p95&lt;/h2&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;h2&gt;Ruby 2.2.0preview1&lt;/h2&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;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 this 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;h2&gt;Issues&lt;/h2&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;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;TBD — this is a 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/6</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; This is a contemporaneous conference report from 2010. Product names, prices, hosting sites, terminology, and feature comparisons below describe that moment and are not current purchasing guidance. Java EE later became Jakarta EE. CodePlex—not SourceForge—was Microsoft’s separate forge, and Microsoft shut it down in 2017. The Azure prices and limits are preserved only as dated conference-era reporting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current and historical context&lt;/strong&gt;
&lt;a target="_blank" href="https://jakarta.ee/about/why-jakarta-ee/"&gt;Why Jakarta EE &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://devblogs.microsoft.com/bharry/shutting-down-codeplex/"&gt;Microsoft: Shutting down CodePlex &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;/aside&gt;&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 with 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, from 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 IDEs (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 is obvious, again open-source is the root of the big change, it’s an adaptation.&lt;/p&gt;
&lt;p&gt;And last but not least 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 it’s 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/2</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;h2&gt;Recursion, but with memory&lt;/h2&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;h2&gt;Adding just one more layer&lt;/h2&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 annotating the recursive structure with results already computed for its substructures. Instead of seeing only the immediate results of recursive calls, the computation can access those annotated subresults. 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: it applies a natural transformation to the base functor at each recursive step before the algebra is evaluated. The structure is reshaped as recursion proceeds, rather than transformed once in advance.&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;pre class="highlight plaintext"&gt;&lt;code&gt;zygo + histo + prepro + morphism
&lt;/code&gt;&lt;/pre&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 a deliberately composite recursion scheme combining a zygomorphism, a histomorphism, and a prepromorphism.&lt;/p&gt;

&lt;h2&gt;Why anyone would do this&lt;/h2&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;h2&gt;The cost of names&lt;/h2&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;h2&gt;A small observation&lt;/h2&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;

&lt;h2&gt;References and provenance&lt;/h2&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical note (2026).&lt;/strong&gt; This particular composite was intentionally overcomplicated and originated as a joke, although it received an implementation. The prose above is intuition, not a type-level definition, and should not imply common production use.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.haskell.org/haskellwiki/zygohistomorphic_prepromorphisms"&gt;HaskellWiki: Zygohistomorphic prepromorphisms &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://stackoverflow.com/questions/5057136/real-world-applications-of-zygohistomorphic-prepromorphisms/5058725#5058725"&gt;Edward Kmett&amp;#39;s historical 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;&lt;/p&gt;
&lt;/aside&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>2026-08-01T20:58:12Z</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 the 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—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 it is able to convey its creator’s 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, given 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 do 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/5</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; The first definition link is no longer a dependable source. The quotation is a broad summary of classical utilitarianism, not a definition shared by every utilitarian theory. “All abstractions are leaky” also echoes Joel Spolsky’s 2002 Law of Leaky Abstractions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Further context&lt;/strong&gt;
&lt;a target="_blank" href="https://plato.stanford.edu/entries/utilitarianism/"&gt;Utilitarianism — Stanford Encyclopedia of Philosophy &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.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions/"&gt;The Law of Leaky Abstractions &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;/aside&gt;&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 connections to other entities, bringing them in the foreground, into my field of subjectivity.&lt;/p&gt;
&lt;p&gt;We try 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
a leaky one would dissolve. Seems like we have more questions than facts.
Looking from another angle and replacing the &amp;quot;good&amp;quot; with the standard
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, 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;h2&gt;References&lt;/h2&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>2026-08-01T20:58:12Z</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;h2&gt;The semantic rule&lt;/h2&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# one-liner that abides to the semantic rule&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&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;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;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;some_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;versus :&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# idiomatic Ruby one-liner&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;..&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;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;some_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;side_effect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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., it improves code 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;h2&gt;Lambdas&lt;/h2&gt;
&lt;p&gt;This can also be applied to the lambda notation:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;-&amp;gt;&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&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;/code&gt;&lt;/pre&gt;&lt;p&gt;versus&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;obj&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="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Lambdas have one more piece of semantic baggage, 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;h2&gt;Conclusion&lt;/h2&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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Language and style context (2026).&lt;/strong&gt; Braces bind more tightly than &lt;code&gt;do...end&lt;/code&gt;, so changing delimiters can change which method receives a block. The “semantic rule” was a debated convention, not a Ruby language rule; current RuboCop makes delimiter style configurable.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://docs.ruby-lang.org/en/master/syntax/calling_methods_rdoc.html#label-Block+Argument"&gt;Ruby syntax: blocks &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://docs.rubocop.org/rubocop/cops_style.html#styleblockdelimiters"&gt;RuboCop &lt;code&gt;Style/BlockDelimiters&lt;/code&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;/aside&gt;
&lt;h2&gt;References&lt;/h2&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>2026-08-01T20:58:12Z</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 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;h2&gt;Roles and modules&lt;/h2&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 do 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;h2&gt;ActiveSupport::Concern&lt;/h2&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 boilerplate code e.g. :&lt;/p&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="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;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;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;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;p&gt;A 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;h2&gt;Uses and trade-offs&lt;/h2&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;A much better use can be found 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&amp;#39;s-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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Rails and Ruby context (2026).&lt;/strong&gt; The &lt;code&gt;module ClassMethods&lt;/code&gt; concern form was valid in 2013; modern Rails also offers &lt;code&gt;class_methods do&lt;/code&gt;. Rails now calls controller “before filters” &lt;code&gt;before_action&lt;/code&gt;. Inclusion order, method lookup, and callback side effects can make concerns coupled rather than independent roles.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://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; · &lt;a target="_blank" href="https://guides.rubyonrails.org/action_controller_overview.html#filters"&gt;Action Controller callbacks &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://docs.ruby-lang.org/en/master/Module.html"&gt;Ruby &lt;code&gt;Module&lt;/code&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;/aside&gt;
&lt;h2&gt;Further reading&lt;/h2&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/10</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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 VM implementations: especially MRI and 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 64-bit Linux, an almost vanilla Rails 3.x application takes up ~70 MB of memory and a rather complex one ~200 MB; now multiply that by 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 marking 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;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;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 ~76 MB is shared and ~1.5 MB 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 GC-preserved memory sharing. 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 of 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;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="mi"&gt;8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Worker-scaling note (2026).&lt;/strong&gt; Load-test worker counts against private/PSS memory, database connections, concurrency, and CPU. Copy-on-write does not make worker memory free, sharing erodes as processes write, and Unicorn pre-fork sharing depends on &lt;code&gt;preload_app true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://yhbt.net/unicorn/Unicorn/Configurator.html"&gt;Unicorn configurator documentation &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;&lt;strong&gt;2026 update:&lt;/strong&gt; For current Rails 8 boot-memory experiments, see &lt;a target="_blank" href="https://marianposaceanu.com/projects/rails-dependency-pruner"&gt;Rails Dependency Pruner &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 is experimental and should be evaluated with application-specific tests and repeated measurements.&lt;/p&gt;
&lt;/aside&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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical scope (2026).&lt;/strong&gt; These measurements describe one application, OS, Ruby build, and point in time; a single aggregate does not by itself establish per-worker memory. Ruby 2.0, Rails 3, Ruby 1.8.7, and REE are unsupported. The 512 MB paragraph describes a 2013 Heroku dyno, not every current tier.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://man7.org/linux/man-pages/man5/proc_pid_smaps.5.html"&gt;Linux &lt;code&gt;smaps&lt;/code&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;a target="_blank" href="https://www.ruby-lang.org/en/news/2013/02/24/ruby-2-0-0-p0-is-released/"&gt;Ruby 2.0.0-p0 announcement &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.ruby-lang.org/en/downloads/branches/"&gt;Ruby branches &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://guides.rubyonrails.org/maintenance_policy.html"&gt;Rails maintenance policy &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://devcenter.heroku.com/articles/dyno-sizes"&gt;Heroku dyno sizes &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://devcenter.heroku.com/articles/dyno-memory-behavior"&gt;Heroku memory behavior &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;/aside&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/7</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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 googling around I 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 had been 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 are also: &lt;/p&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;-webkit-font-smoothing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;none&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;-webkit-font-smoothing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;antialiased&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;-webkit-font-smoothing&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nt"&gt;subpixel-antialiased&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;yet they don&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 on 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;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.firefox_is_bad&lt;/span&gt; &lt;span class="nt"&gt;section&lt;/span&gt; &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.75&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;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;pre class="highlight coffeescript"&gt;&lt;code&gt;&lt;span class="na"&gt;is_firefox&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;mozInnerScreenX&lt;/span&gt; &lt;span class="o"&gt;is&lt;/span&gt; &lt;span class="no"&gt;undefined&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Update № 2&lt;/h2&gt;
&lt;p&gt;Status on OS X: the same issue, yet the native antialiasing is somewhat better compared to the Windows 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;

&lt;h3&gt;References and 2026 context&lt;/h3&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical browser note (2026).&lt;/strong&gt; This article records 2013 behavior. &lt;code&gt;-webkit-text-stroke&lt;/code&gt; is now broadly available, but it draws a glyph stroke rather than resetting font rendering. &lt;code&gt;-webkit-font-smoothing&lt;/code&gt; remains non-standard and macOS-only.&lt;/p&gt;
&lt;p&gt;The CoffeeScript check is browser sniffing, not stable feature detection. The opacity experiment can reduce text contrast; preserve it as a period experiment, prefer &lt;code&gt;CSS.supports()&lt;/code&gt; for current feature tests, and verify contrast against WCAG 2.2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sources&lt;/strong&gt;
&lt;a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke"&gt;MDN: &lt;code&gt;-webkit-text-stroke&lt;/code&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;a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/CSS/font-smooth"&gt;MDN: &lt;code&gt;font-smooth&lt;/code&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;a target="_blank" href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html"&gt;WCAG 2.2 contrast minimum &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;/aside&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>2026-08-01T20:58:12Z</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 — generational GC), hierarchical method caching, some small syntax changes and non-experimental refinements. All in all one can expect 5% to 15% performance increase which is quite awesome.&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2014):&lt;/strong&gt; Rubinius 2.2.3, JRuby 1.7.9, Ruby 2.1.0, Rails 4.0.2, and these server configurations are unsupported. This is not a current production-readiness guide. CRuby uses three Unicorn workers, Rubinius one Puma worker with threads, and JRuby threaded Puma; response sizes also differ. Four hundred localhost requests and selected runs do not establish sustained stability, tail behavior, memory behavior, GC pauses, compatibility, or correctness. Compatibility and performance claims are anecdotal and version-bound; the suspected &lt;code&gt;racc&lt;/code&gt; bottleneck was not demonstrated.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.ruby-lang.org/en/downloads/branches/"&gt;Ruby branch status &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.jruby.org/documentation"&gt;JRuby documentation &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://github.com/rubinius/rubinius"&gt;Rubinius 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;/aside&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, which has sported an LLVM-based JIT for some time, 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 dependencies 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;pre class="highlight plaintext"&gt;&lt;code&gt;gem 'racc'
gem 'rubysl'
gem 'puma'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Notes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rubysl — a rather cryptic name for the 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 find more):&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# gem 'oj'
&lt;/code&gt;&lt;/pre&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
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, and RequestForgeryProtection) also the sprockets railtie.&lt;/p&gt;

&lt;h3&gt;ApacheBench config&lt;/h3&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
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;code&gt;ab -n400 -c16 -T&amp;#39;application/json&amp;#39; http://localhost:3000/entries&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;unicorn.rb&lt;/h3&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;p&gt;The command to start it up: 
&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;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;h3&gt;puma.rb on Rubinius 2.2.3&lt;/h3&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;p&gt;The command to start it up: 
&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;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;h3&gt;puma.rb on JRuby 1.7.9&lt;/h3&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;p&gt;The command to start it up: 
&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;pre class="highlight plaintext"&gt;&lt;code&gt;# gem 'pg'
gem 'activerecord-jdbcpostgresql-adapter'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Results after several runs (so that the JIT can do its magic):&lt;/p&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;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;43 req/s vs. 59 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;h3&gt;Update&lt;/h3&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:
- apparently my VM was accessing only one core (thus the initial abysmal performance of Rubinius and JRuby) - bumped to four
- updated all benchmarks and also added JRuby 1.7.9&lt;/p&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;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/16</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical instructions (2010)—do not follow on a current machine.&lt;/strong&gt; Ruby 1.8/1.9 and Rails 2.3/3 are unsupported and lack current security fixes. Manually copying DLLs into runtime directories is unsafe and brittle. The commands below are retained only as a record of the old environment; use supported Ruby/Rails versions and current installation documentation instead.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current guidance&lt;/strong&gt;
&lt;a target="_blank" href="https://rubyonrails.org/maintenance"&gt;Rails maintenance policy &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.ruby-lang.org/en/downloads/branches/"&gt;Supported Ruby branches &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://guides.rubyonrails.org/install_ruby_on_rails.html"&gt;Install Ruby on 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;/aside&gt;&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 &lt;code&gt;1.9.2-p0&lt;/code&gt;. The problem with this one is that Ruby crashes into oblivion from time to time (mostly after lots of page refreshes); after a Google search I’ve found out it’s a Windows bug (yeah on *nix it works like a charm) so not much really more to do except waiting for the next release, compiling 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 well—for development purposes).&lt;/p&gt;
&lt;p&gt;Ok, time to install rails: &lt;code&gt;gem install rails -v 2.3.5 --no-rdoc --no-ri&lt;/code&gt; (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) in the Windows &lt;code&gt;PATH&lt;/code&gt; environment 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, not moving, the file) &lt;code&gt;libmySQL.dll&lt;/code&gt; into the &lt;code&gt;ruby/bin&lt;/code&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 &lt;code&gt;database.yml&lt;/code&gt; to use the adapter, it worked! Kudos to brianmario for coding that.&lt;/p&gt;
&lt;p&gt;All done !&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&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/12</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;After working with Rails for some time, you start nitpicking about how to improve it. This is the first in a series of articles on how to improve (even marginally) Rails&amp;#39;s performance.&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2014):&lt;/strong&gt; This is Rails 4.1-era advice. Do not copy these dependencies, native extensions, or global monkey patches into a current app without compatibility, maintenance, security, and workload review. The shown command uses 10 threads/10 connections, while every output reports 2 threads/10 connections; the producing command is unknown. Short local runs without confidence intervals or environment disclosure are observations, not portable gains. &lt;code&gt;escape_utils&lt;/code&gt;, &lt;code&gt;fast_blank&lt;/code&gt;, &lt;code&gt;oj_mimic_json&lt;/code&gt;, and jemalloc advice below is historical.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://rubyonrails.org/maintenance"&gt;Rails maintenance policy &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;/aside&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 HTML escaping, the String#blank? and JSON utils.&lt;/p&gt;

&lt;h2&gt;Benchmarking methodology&lt;/h2&gt;
&lt;p&gt;Methodology is a strong word for just running &lt;code&gt;wrk&lt;/code&gt; a couple of times in the console 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 ApacheBench (&lt;code&gt;ab&lt;/code&gt;) 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
  load when run on a single multi-core CPU.&lt;/p&gt;
&lt;/blockquote&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t10 -c10 -d10s http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This runs a benchmark for 10 seconds, using 10 threads, and keeping 10 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;h2&gt;The escape_utils gem&lt;/h2&gt;
&lt;p&gt;Just faster 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;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;p&gt;The logic to test it:&lt;/p&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;p&gt;With standard Rails:&lt;/p&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;p&gt;With the escape_utils gem:&lt;/p&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;h2&gt;The fast_blank gem&lt;/h2&gt;
&lt;p&gt;Living under the impression that the &lt;code&gt;blank?&lt;/code&gt; 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 &lt;code&gt;String#blank?&lt;/code&gt; method&lt;/p&gt;
&lt;/blockquote&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;p&gt;With standard Rails:&lt;/p&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;p&gt;With the fast_blank gem:&lt;/p&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;h2&gt;The oj gem&lt;/h2&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;p&gt;The test logic is simple, just serialize all articles into JSON:&lt;/p&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;p&gt;With standard Rails serializers:&lt;/p&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;p&gt;With the Oj gem:&lt;/p&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;h2&gt;Using jemalloc&lt;/h2&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;. In initial testing it didn&amp;#39;t yield many 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;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;h2&gt;Dig into your Rails app&lt;/h2&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;h2&gt;Conclusion&lt;/h2&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/23</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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 a large slice of the space-time continuum. I recall I almost started this blog for stuff like this, good memories [1]. &lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2015):&lt;/strong&gt; This is a contemporaneous conference diary; links, organizations, and social-media handles are preserved as they were referenced at publication.&lt;/p&gt;
&lt;/aside&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 Ballou&amp;#39;s [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 Mendeley [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;h3&gt;References&lt;/h3&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>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Archive note (2026):&lt;/strong&gt; The opening paragraph contains the corrupt fragment &lt;code&gt;I’vevolatile and utopic place most of the time.&lt;/code&gt;; it cannot be reconstructed safely and is left untouched pending an original source or backup.&lt;/p&gt;
&lt;/aside&gt;&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;lifestyle 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 lifestyle, you live for your business you &lt;em&gt;burn&lt;/em&gt; 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 probably 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 too seriously, self-irony from time to time is great, nobody is perfect, perfection is not the goal, accepting the imperfection and making the best of it is the key.&lt;/p&gt;
&lt;p&gt;All in all, great experience, kudos !&lt;/p&gt;

&lt;h2&gt;Reference&lt;/h2&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/13</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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? It 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 thrashing (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;p&gt;The historical sizes are compared below.&lt;/p&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 zero-filled 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 contain only 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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Destructive free-space operation — back up first.&lt;/strong&gt; The following historical commands intentionally consume all guest free space and can trigger &lt;code&gt;ENOSPC&lt;/code&gt;, disrupt services, exhaust reserved blocks, or cause data loss. Make a verified backup/export, stop applications, confirm the intended guest filesystem, leave headroom where possible, and consult the installed &lt;code&gt;sfill(1)&lt;/code&gt; manual before considering elevated privileges. Do not run these commands by rote on production systems.&lt;/p&gt;
&lt;p&gt;Where discard is supported end-to-end, verify whether &lt;code&gt;fstrim&lt;/code&gt; can reclaim thin-provisioned blocks without filling the filesystem.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://manpages.debian.org/sfill"&gt;Debian &lt;code&gt;sfill(1)&lt;/code&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;a target="_blank" href="https://www.virtualbox.org/manual/ch08.html"&gt;VirtualBox manual &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;/aside&gt;&lt;p&gt;The first version is using a tool like secure delete but with a very fast run:&lt;/p&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;p&gt;Where:&lt;/p&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;p&gt;Or a much simpler way:&lt;/p&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;--&lt;/span&gt; /bigemptyfile
&lt;/code&gt;&lt;/pre&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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Offline compaction — verify the image first.&lt;/strong&gt; A snapshot is not a backup. Make and verify a copy/export, identify the correct base or differencing image, shut the guest down cleanly, and ensure no process has the image open before host-side compaction. Current VirtualBox syntax is &lt;code&gt;VBoxManage modifymedium disk &amp;lt;uuid|filename&amp;gt; --compact&lt;/code&gt;; Oracle currently documents direct compaction for dynamically allocated VDI images. The old command below remains as 2013 evidence.&lt;/p&gt;
&lt;p&gt;SDelete&amp;#39;s &lt;code&gt;-z&lt;/code&gt; mode zeroes free space for virtual-disk optimization; it is write-intensive and is not secure SSD erasure.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.virtualbox.org/manual/ch08.html#vboxmanage-modifymedium"&gt;VirtualBox &lt;code&gt;modifymedium&lt;/code&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;a target="_blank" href="https://learn.microsoft.com/en-us/sysinternals/downloads/sdelete"&gt;Microsoft SDelete &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;/aside&gt;&lt;p&gt;OK once all of this is over: shut down the VMs and run compact on the VHD. Using VirtualBox, run &lt;code&gt;VBoxManage modifyhd thedisk.vdi --compact&lt;/code&gt; from the host console. Using VMware, consult the product&amp;#39;s disk-compaction documentation.&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;
&lt;div class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;VM&lt;/th&gt;
&lt;th style="text-align: right"&gt;Before&lt;/th&gt;
&lt;th style="text-align: right"&gt;After&lt;/th&gt;
&lt;th style="text-align: right"&gt;Reported reduction&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu desktop&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;19.7 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;11.0 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;~ -45%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ubuntu server&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;8.0 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;4.3 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;~ -47%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows XP&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;7.9 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;strong&gt;6.6 GB&lt;/strong&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;~ -16%&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;&lt;p&gt;That&amp;#39;s a whopping &lt;strong&gt;13.7 GB&lt;/strong&gt; out of my main 128 GB SSD that I use as the system drive, not bad, not bad at all !&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&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/9</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Historical Windows/Firefox optimization (2010)—do not move a current browser profile using these instructions.&lt;/strong&gt; RAM disks are volatile, so data disappears on shutdown or a crash. Moving the wrong profile path can lose history, sessions, cookies, or other user data. Back up the profile and treat this only as a dated account of moving disposable cache data; modern browser and storage behavior differs substantially. &lt;code&gt;Ctrl+F5&lt;/code&gt; is platform-dependent and is not a corruption-repair guarantee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current context&lt;/strong&gt;
&lt;a target="_blank" href="https://support.mozilla.org/en-US/kb/profiles-where-firefox-stores-user-data"&gt;Firefox profiles &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://developer.mozilla.org/en-US/docs/Web/HTTP/Caching"&gt;HTTP caching &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;/aside&gt;&lt;p&gt;From time to time I like to optimize a bit my Windows machine, yet mostly all I do is fine-tuning, 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 be 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 open 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–90 seconds) 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 I was 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 (50 MB each would be enough) and I don’t really care about the safety of the data 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 Firefox session with 20+ tabs. Results ? Well,  just next to amazing, everything runs snappy, but now I’ve found another bottleneck: my CPU.&lt;/p&gt;

&lt;h2&gt;Reference&lt;/h2&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; - RAM-disk utility&lt;/li&gt;
&lt;/ul&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>2026-08-01T20:58:12Z</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 who 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;h2&gt;Why stream&lt;/h2&gt;
&lt;p&gt;It is rather simple: in a normal Rails render, the server finishes producing the response before sending the body; that delays the browser from discovering assets placed in the layout. &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;h2&gt;Rails implementation&lt;/h2&gt;
&lt;p&gt;Implementation wise, you just need to add to your controller methods something like :&lt;/p&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;h2&gt;Unicorn&lt;/h2&gt;
&lt;p&gt;The latest version of Unicorn (4.x) comes with support for chunked responses by default.[1] You can always add to your &lt;code&gt;unicorn_config.rb&lt;/code&gt; something like: &lt;/p&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="nf"&gt;fetch&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="s2"&gt;"3000"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to_i&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;h2&gt;Rails rendering caveats&lt;/h2&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;h2&gt;New Relic&lt;/h2&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;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;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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Protocol and Rails context (2026).&lt;/strong&gt; Chunked transfer coding is HTTP/1.1 framing; HTTP/2 and HTTP/3 use their own frames. Rails 8.1 still documents template streaming, but proxies, compression, CDNs, servers, and body-changing middleware may buffer or rechunk output. Unicorn socket options tune transport; they do not enable template streaming. &lt;code&gt;ActionController::Live&lt;/code&gt; is a distinct threaded API: account for thread safety, database connections, disconnects, and close the stream in &lt;code&gt;ensure&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a target="_blank" href="https://www.rfc-editor.org/rfc/rfc9112.html#section-7.1"&gt;RFC 9112 &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.rfc-editor.org/rfc/rfc9113.html#section-8.2.2"&gt;RFC 9113 &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://api.rubyonrails.org/v8.1/classes/ActionController/Streaming.html"&gt;Rails 8.1 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;a target="_blank" href="https://api.rubyonrails.org/classes/ActionController/Live.html"&gt;ActionController::Live &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://yhbt.net/unicorn/Unicorn/Configurator.html#method-i-listen"&gt;Unicorn &lt;code&gt;listen&lt;/code&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;/aside&gt;
&lt;h2&gt;References&lt;/h2&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/28</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</updated>
    <link rel="alternate" type="text/html" href="https://marianposaceanu.com/articles/buzzwords-and-manifestos"/>
    <title>Buzzwords and manifestos</title>
    <content type="html">&lt;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; Here “NLP” refers to neuro-linguistic programming, not natural-language processing. Evidence reviews have found little support for claimed health outcomes. The HTTP-era links below may now be unavailable or changed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source context&lt;/strong&gt;
&lt;a target="_blank" href="https://webarchive.nationalarchives.gov.uk/ukgwa/20170106000347/https://www.nhs.uk/conditions/neuro-linguistic-programming/pages/introduction.aspx"&gt;Archived NHS introduction &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;/aside&gt;&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 a big problem of modern society, maybe it 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. It 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 us to a better path, yet the problem is that it is not your path, someone else created it to fit as many &lt;em&gt;yous&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 following some program and in the end realizing that nothing had changed.&lt;/p&gt;
&lt;p&gt;So let’s drop the buzzwords or blindly following someone else’s system without taking it through a critical hell ride in our minds.&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&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/17</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; This is a 2010 architectural sketch, not a current Rails security tutorial. RBAC is not inherently required for a single-author blog, production is not a substitute for automated or staging security tests, and authorization should fail closed under a current threat model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current guidance&lt;/strong&gt;
&lt;a target="_blank" href="https://guides.rubyonrails.org/security.html"&gt;Securing Rails Applications &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://developer.mozilla.org/en-US/docs/Glossary/CMS"&gt;CMS glossary &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;/aside&gt;&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 a 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 &lt;em&gt;small&lt;/em&gt; performance in the admin but having it online will let me test it and improve it for actual usage in real world.&lt;/p&gt;

&lt;h2&gt;References&lt;/h2&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 Access Control&lt;/li&gt;
&lt;/ul&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>2026-08-01T20:58:12Z</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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;&lt;strong&gt;Editor’s note (2026):&lt;/strong&gt; This describes a 2010 jQuery and script-loader setup. RequireJS and LABjs are historical tools, and the performance result is anecdotal because the promised graphs and test method are absent. Third-party CDN scripts also introduce availability, privacy, and supply-chain considerations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Current measurement and security context&lt;/strong&gt;
&lt;a target="_blank" href="https://web.dev/articles/user-centric-performance-metrics"&gt;User-centric performance metrics &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://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity"&gt;Subresource Integrity &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;/aside&gt;&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 in the last few 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 didn’t like 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 and 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;h2&gt;References&lt;/h2&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/46</id>
    <published>2026-03-29T23:20:11Z</published>
    <updated>2026-08-01T19:14:16Z</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 this small Rails app. The stable path used here performs brute-force search, which felt perfectly reasonable at my scale. As of June 13, 2026, sqlite-vec’s official &lt;a target="_blank" href="https://github.com/asg017/sqlite-vec/issues/25"&gt;ANN tracking issue #25 &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; remains open; however, the v0.1.10 alpha &lt;a target="_blank" href="https://github.com/asg017/sqlite-vec/releases"&gt;releases &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; introduced experimental DiskANN/ANN work. That experimental support is distinct from the stable brute-force approach in this article.&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 using a local Docker container on my server.&lt;/p&gt;

&lt;h2&gt;Benchmarks&lt;/h2&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. This was an informal comparison on one corpus and machine, not a controlled or generalizable benchmark.&lt;/p&gt;
&lt;div class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;Memory ratio comparison (lower is better)&lt;/th&gt;
&lt;th style="text-align: right"&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;embeddinggemma-300m / e5-base&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.16x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e5-large / e5-base&lt;/td&gt;
&lt;td style="text-align: right"&gt;2.07x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;e5-large / embeddinggemma-300m&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.78x&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;&lt;p&gt;Maximum ratio: 2.07x.&lt;/p&gt;
&lt;figure class="article-chart" role="img" aria-label="MEMORY RATIO MAP (max ratio = 2.07x; lower is better): embeddinggemma-300m / e5-base 1.16, e5-large / e5-base 2.07, e5-large / embeddinggemma-300m 1.78"&gt;&lt;figcaption class="article-chart-title"&gt;MEMORY RATIO MAP (max ratio = 2.07x; lower is better)&lt;/figcaption&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;embeddinggemma-300m / e5-base&lt;/strong&gt;&lt;span&gt;1.16&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="56.0" max="100" aria-hidden="true"&gt;1.16&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;1.16x&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;e5-large / e5-base&lt;/strong&gt;&lt;span&gt;2.07&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="100.0" max="100" aria-hidden="true"&gt;2.07&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;2.07x&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;e5-large / embeddinggemma-300m&lt;/strong&gt;&lt;span&gt;1.78&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="86.0" max="100" aria-hidden="true"&gt;1.78&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;1.78x&lt;/div&gt;&lt;/div&gt;&lt;/figure&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="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th style="text-align: right"&gt;Dimensions&lt;/th&gt;
&lt;th style="text-align: right"&gt;Memory&lt;/th&gt;
&lt;th style="text-align: right"&gt;Time&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intfloat/multilingual-e5-base&lt;/td&gt;
&lt;td style="text-align: right"&gt;768d&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.95 GB&lt;/td&gt;
&lt;td style="text-align: right"&gt;~6.47s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;google/embeddinggemma-300m&lt;/td&gt;
&lt;td style="text-align: right"&gt;768d&lt;/td&gt;
&lt;td style="text-align: right"&gt;2.26 GB&lt;/td&gt;
&lt;td style="text-align: right"&gt;~6.24s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;intfloat/multilingual-e5-large&lt;/td&gt;
&lt;td style="text-align: right"&gt;1024d&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.03 GB&lt;/td&gt;
&lt;td style="text-align: right"&gt;~7.37s&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;&lt;figure class="article-chart" role="img" aria-label="EMBEDDING MODEL MEMORY IN GB (lower is better): intfloat/multilingual-e5-base 1.95, google/embeddinggemma-300m 2.26, intfloat/multilingual-e5-large 4.03"&gt;&lt;figcaption class="article-chart-title"&gt;EMBEDDING MODEL MEMORY IN GB (lower is better)&lt;/figcaption&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;intfloat/multilingual-e5-base&lt;/strong&gt;&lt;span&gt;1.95&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="48.4" max="100" aria-hidden="true"&gt;1.95&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;768d&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;google/embeddinggemma-300m&lt;/strong&gt;&lt;span&gt;2.26&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="56.1" max="100" aria-hidden="true"&gt;2.26&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;768d&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;intfloat/multilingual-e5-large&lt;/strong&gt;&lt;span&gt;4.03&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="100.0" max="100" aria-hidden="true"&gt;4.03&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;1024d&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class="article-chart" role="img" aria-label="EMBEDDING MODEL TIME IN SECONDS (lower is better): intfloat/multilingual-e5-base 6.47, google/embeddinggemma-300m 6.24, intfloat/multilingual-e5-large 7.37"&gt;&lt;figcaption class="article-chart-title"&gt;EMBEDDING MODEL TIME IN SECONDS (lower is better)&lt;/figcaption&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;intfloat/multilingual-e5-base&lt;/strong&gt;&lt;span&gt;6.47&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="87.8" max="100" aria-hidden="true"&gt;6.47&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;~6.47s, 768d&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;google/embeddinggemma-300m&lt;/strong&gt;&lt;span&gt;6.24&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="84.7" max="100" aria-hidden="true"&gt;6.24&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;~6.24s, 768d&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;intfloat/multilingual-e5-large&lt;/strong&gt;&lt;span&gt;7.37&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="100.0" max="100" aria-hidden="true"&gt;7.37&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;~7.37s, 1024d&lt;/div&gt;&lt;/div&gt;&lt;/figure&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;h2&gt;Rails implementation&lt;/h2&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 small service running Gemma with two API endpoints: &lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+--------------------------------------------------------------------------+
| EMBEDDING API FLOW                                                       |
+--------------------------------------------------------------------------+
| GET /health                                                              |
|   |                                                                      |
|   v                                                                      |
| 200 OK                                                                   |
|                                                                          |
| POST /embed                                                              |
| { "query": "text to embed" }                                             |
|   |                                                                      |
|   v                                                                      |
| 200 OK                                                                   |
| {                                                                        |
|   "embedding": [0.01, -0.02, ...],                                       |
|   "dimensions": 768,                                                     |
|   "model": "google/embeddinggemma-300m"                                  |
| }                                                                        |
+--------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This runs within the Docker network created by Kamal so it will be accessible by the Rails app. My VM has 6GB of RAM and two Ampere Arm vCPUs; 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;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 class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;Latency range (ms)&lt;/th&gt;
&lt;th style="text-align: right"&gt;Count&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0-999&lt;/td&gt;
&lt;td style="text-align: right"&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1000-1999&lt;/td&gt;
&lt;td style="text-align: right"&gt;18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2000-2999&lt;/td&gt;
&lt;td style="text-align: right"&gt;10&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3000-3999&lt;/td&gt;
&lt;td style="text-align: right"&gt;15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4000-4999&lt;/td&gt;
&lt;td style="text-align: right"&gt;4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5000-6999&lt;/td&gt;
&lt;td style="text-align: right"&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;&lt;figure class="article-chart" role="img" aria-label="EMBEDDING LATENCY HISTOGRAM (ms): 0-999 2, 1000-1999 18, 2000-2999 10, 3000-3999 15, 4000-4999 4, 5000-6999 1"&gt;&lt;figcaption class="article-chart-title"&gt;EMBEDDING LATENCY HISTOGRAM (ms)&lt;/figcaption&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;0-999&lt;/strong&gt;&lt;span&gt;2&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="11.1" max="100" aria-hidden="true"&gt;2&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;1000-1999&lt;/strong&gt;&lt;span&gt;18&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="100.0" max="100" aria-hidden="true"&gt;18&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;2000-2999&lt;/strong&gt;&lt;span&gt;10&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="55.6" max="100" aria-hidden="true"&gt;10&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;3000-3999&lt;/strong&gt;&lt;span&gt;15&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="83.3" max="100" aria-hidden="true"&gt;15&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;4000-4999&lt;/strong&gt;&lt;span&gt;4&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="22.2" max="100" aria-hidden="true"&gt;4&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div class="article-chart-row"&gt;&lt;div class="article-chart-label"&gt;&lt;strong&gt;5000-6999&lt;/strong&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;progress class="article-chart-bar" value="5.6" max="100" aria-hidden="true"&gt;1&lt;/progress&gt;&lt;div class="article-chart-meta"&gt;1&lt;/div&gt;&lt;/div&gt;&lt;/figure&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;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;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;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;h2&gt;The embedding service&lt;/h2&gt;
&lt;p&gt;The inference component is documented as the &lt;a target="_blank" href="https://marianposaceanu.com/projects/embeddinggemma-rails-kamal-inference"&gt;EmbeddingGemma service for Rails and Kamal &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 page summarizes the HTTP contract and deployment boundary. The full source remains available in &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;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="n"&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;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 are a home-brewed RRF implementation and a more rigorous look at performance.&lt;/p&gt;

&lt;h2&gt;Notes&lt;/h2&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/29</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 Rubies 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;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;p&gt;&lt;strong&gt;Eleven gigabytes&lt;/strong&gt; in total, almost all of it in gemsets according to this breakdown, wow, that&amp;#39;s something. Let&amp;#39;s try to clean this up a bit:&lt;/p&gt;

&lt;h2&gt;Step one&lt;/h2&gt;
&lt;p&gt;Always update RVM to its latest stable release:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rvm get stable
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step two&lt;/h2&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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rvm gemset globalcache &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step three  (attention destructive)&lt;/h2&gt;
&lt;p&gt;Before using a destructive third-party cleanup script, back up what you need, inspect the script, and prefer a dry run or a current RVM-supported cleanup command. Do not assume this old script is safe for a current installation.&lt;/p&gt;
&lt;p&gt;The 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; was what I used to further clean up my 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;h2&gt;Step four&lt;/h2&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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;rvm cleanup all
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note: this will take some time to complete.&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ rvm disk-usage all
  ...
  Total Disk Usage: 9.0G
&lt;/code&gt;&lt;/pre&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;h2&gt;Credits:&lt;/h2&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/38</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 sure (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 &amp;quot;Retina&amp;quot;-mode HiDPI, can create large files because the format default compression engine is DEFLATE which is OK but the problem is not the algorithm itself, the issue is that it 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). Actual PNG-versus-JPEG size savings depend on the screenshot’s content and whether lossy compression is acceptable for the intended use. &lt;/p&gt;
&lt;p&gt;Why is this an issue for me? I keep all my screenshots and sync them in iCloud. It is 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;h2&gt;Change the screenshot format&lt;/h2&gt;
&lt;p&gt;But I digress, I always forget the command to change the default image format to jpeg and as far as I know there is still no global UI way to set it - therefore here goes, the &lt;em&gt;magic&lt;/em&gt; commands:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;defaults write com.apple.screencapture &lt;span class="nb"&gt;type &lt;/span&gt;jpg
killall SystemUIServer
&lt;/code&gt;&lt;/pre&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;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;h2&gt;Change the save location&lt;/h2&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/34</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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;This article describes a 2017 JRuby, concurrent-ruby, and RSpec setup. Current APIs, integrations, and behavior may differ, so the workaround below should be read as historical rather than current guidance.&lt;/p&gt;
&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 request 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 a UI where you have to use chrome-cli or phantomjs just to get near 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 on JRuby 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;h2&gt;The failing synchronization approach&lt;/h2&gt;
&lt;p&gt;This was the initial implementation, by the way:&lt;/p&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;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;h2&gt;Running tests sequentially&lt;/h2&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;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Run everything sequentially 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;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;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;p&gt;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;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;p&gt;Regarding specs/coverage - the next step is to improve code-coverage in the unit-tests realm to ensure all the small parts are working correctly.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;JRuby + concurrent-ruby is a boon - there are some drawbacks as pointed out 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;h2&gt;Updates&lt;/h2&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;aside class="article-reference" aria-label="Reference"&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;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;/aside&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;aside class="article-reference" aria-label="Reference"&gt;&lt;p&gt;Problem may be that sometimes the tasks in the queue need 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;p&gt;Another suggestion from &lt;code&gt;moomaka&lt;/code&gt; - this time for keeping parallelism on:&lt;/p&gt;
&lt;/aside&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/44</id>
    <published>2025-07-06T14:40:06Z</published>
    <updated>2026-08-01T18:57:22Z</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.&lt;/p&gt;
&lt;p&gt;This is just to add 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 is purely from my empirical point of view.&lt;/p&gt;

&lt;h2&gt;A small LLM-generated script&lt;/h2&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 applied 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 kinds of duplicates. The generated code looks like this:&lt;/p&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="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'digest'&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;# Verify content and act; preview is the default&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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
     &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SHA256&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;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;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SHA256&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;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;# Review every reported pair before rerunning with --delete.&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;p&gt;It&amp;#39;s pretty meh from many points of view, 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, after a couple of prompt iterations - now, if I were to write this from scratch, knowing myself - this would have been a small gem and I&amp;#39;d probably have spent 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;h2&gt;Cognitive debt&lt;/h2&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. Will I 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/42</id>
    <published>2025-07-06T08:37:13Z</published>
    <updated>2026-08-01T18:57:22Z</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;Originally published after the 2023 Friendly.rb conference in Romania, this is an archival account of that talk.&lt;/p&gt;
&lt;p&gt;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 the Lisp family, implemented here 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 features common to Lisp-family languages. 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;pre class="highlight clojure"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;html&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:main&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:section&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"ruby-lisp-alien-tech"&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="no"&gt;:h2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Ruby &amp;amp; the LISP Alien Tech"&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="no"&gt;:p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"When we talk about the world of programming languages, two stand out in their unique ways: Ruby, "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"with its elegant syntax and Rails powerhouse, and LISP, the ancient language known for its "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"code-as-data philosophy. The connection? Both embody the spirit of making the impossible possible. "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"Join us as we delve into the mysteries of Ruby and LISP and uncover the alien tech within."&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="no"&gt;:img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"https://www.lisperati.com/lisplogo_warning2_256.png"&lt;/span&gt;&lt;span class="n"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:alt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"LISP Alien Tech Illustration"&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="no"&gt;:section&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"s-expressions"&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="no"&gt;:h2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"What are S-expressions?"&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="no"&gt;:p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"S-expressions, or symbolic expressions, are a fundamental part of the LISP (LISt Processing) "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"programming language. They are used to represent both data and code. An S-expression can be an "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"atom or a list. At its core, an S-expression is a way to represent nested list data in a uniform "&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="s"&gt;"manner."&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="no"&gt;:figure&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;:img&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="no"&gt;:src&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVUA"&lt;/span&gt;&lt;span class="n"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;:alt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"S-expression diagram (illustrative truncated data URI)"&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="no"&gt;:figcaption&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;"A visual representation of S-expressions."&lt;/span&gt;&lt;span class="p"&gt;]]]])&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The base64 image data above is intentionally truncated for illustration, so that excerpt is not directly runnable.&lt;/p&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;At the time, I was already revving up for the following 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, &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; were already on sale when this was published.&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;h2&gt;Resources&lt;/h2&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/31</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 from 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 is the history of the dollar sign, and why is it there?&amp;quot; - so like every good question I started to Google my way into some answers:&lt;/p&gt;

&lt;h2&gt;From Perl to PHP&lt;/h2&gt;
&lt;p&gt;First influence - Perl:&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&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;p&gt;via &lt;a href="http://stackoverflow.com/a/3073818"&gt;http://stackoverflow.com/a/3073818&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;&lt;p&gt;Perl might have inherited this from shell tools like awk et al. hence:&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&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;p&gt;via &lt;a href="http://stackoverflow.com/a/3596504"&gt;http://stackoverflow.com/a/3596504&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;&lt;p&gt;A Quora answer provided a useful anecdote that further connected the ideas above:&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&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;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;/aside&gt;
&lt;h2&gt;Sigils in Perl&lt;/h2&gt;
&lt;p&gt;From this we find out that $, @ and % are actually called sigils [2] in computer programming. Next stop was to see what these sigils indicated in Perl:&lt;/p&gt;
&lt;aside class="article-reference" aria-label="Reference"&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;pre class="highlight perl"&gt;&lt;code&gt;  &lt;span class="nv"&gt;$days&lt;/span&gt;               &lt;span class="c1"&gt;# the simple scalar value "days"&lt;/span&gt;
  &lt;span class="nv"&gt;$days&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;           &lt;span class="c1"&gt;# the 29th element of array @days&lt;/span&gt;
  &lt;span class="nv"&gt;$days&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;Feb&lt;/span&gt;&lt;span class="p"&gt;'}&lt;/span&gt;        &lt;span class="c1"&gt;# the 'Feb' value from hash %days&lt;/span&gt;
  &lt;span class="nv"&gt;$#days&lt;/span&gt;              &lt;span class="c1"&gt;# the last index of array @days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;pre class="highlight perl"&gt;&lt;code&gt;  &lt;span class="nv"&gt;@days&lt;/span&gt;               &lt;span class="c1"&gt;# ($days[0], $days[1],... $days[n])&lt;/span&gt;
  &lt;span class="nv"&gt;@days&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="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;        &lt;span class="c1"&gt;# same as ($days[3],$days[4],$days[5])&lt;/span&gt;
  &lt;span class="nv"&gt;@days&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="p"&gt;','&lt;/span&gt;&lt;span class="s1"&gt;c&lt;/span&gt;&lt;span class="p"&gt;'}&lt;/span&gt;      &lt;span class="c1"&gt;# same as ($days{'a'},$days{'c'})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Entire hashes are denoted by &amp;#39;%&amp;#39;:&lt;/p&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;  &lt;span class="nv"&gt;%days&lt;/span&gt;               &lt;span class="c1"&gt;# (key1, val1, key2, val2 ...)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;/aside&gt;
&lt;h2&gt;A Ruby comparison&lt;/h2&gt;
&lt;p&gt;Very interesting and they remind me of Ruby&amp;#39;s shorthand or general delimited input:&lt;/p&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;h2&gt;Conclusion&lt;/h2&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;
[1] - &lt;a href="https://github.com/jenssegers/codeigniter-hmvc-modules"&gt;https://github.com/jenssegers/codeigniter-hmvc-modules&lt;/a&gt;
[2] - &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;
&lt;p&gt;This historical chain is reconstructed from secondary and Q&amp;amp;A sources; it is a plausible trail, not primary-source proof of PHP’s design lineage.&lt;/p&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>2026-08-01T18:57:22Z</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 ownership of the data consumed by a fleet of microservices is distributed across several systems of record. Derived data is then scattered across multiple storage solutions, each designed to solve a specific problem using a subset of it. It may be mirrored and synchronized into a specific format (consider a graph database), or enriched with features that exist only in another store (consider a relational database with custom metadata). These read models can derive from relational or document databases, resources exposed through APIs, or a mixture of them.&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 source-of-truth ownership is distributed across multiple data stores and third-party APIs, each with its 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&amp;#39;s consistency need to be properly defined - how fresh must the data be? does access require bidirectional synchronization—reads and writes—or is it just a read model 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;h2&gt;Read-only consistency&lt;/h2&gt;
&lt;p&gt;The simplest case is a read-only mode with little or no metadata. It is also important to know whether the metadata is encapsulated or leaks into other microservices’ business logic. If we&amp;#39;re talking about the former then the rules for data freshness are dictated by its consumers.&lt;/p&gt;

&lt;h2&gt;Bidirectional consistency&lt;/h2&gt;
&lt;p&gt;The next one is when we have to deal with bidirectional consistency i.e. a client updates something in our data store and we need to sync that back with all the relevant systems of record. 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 is shared between multiple micro-services.&lt;/p&gt;

&lt;h2&gt;Shared metadata and queues&lt;/h2&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 database and a relational database). Optimally, metadata that cuts across multiple microservices should reside in one system of record 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 events 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 with distributed systems of record with an added complication of cross cutting metadata within a fleet of micro-services is quite an interesting problem to solve - in 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 emerge from the graph of (inter)dependencies in the form of an attached mechanism which abstracts the distributed ownership behind one coherent view that stays within the confines of its consumers, thus delivering the expected business value on all fronts. &lt;/p&gt;

&lt;h2&gt;More information:&lt;/h2&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/43</id>
    <published>2025-07-06T08:38:30Z</published>
    <updated>2026-08-01T18:57:22Z</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 deliberately unrealistic test where I scrolled through the file quickly, latency felt lower and I did not notice dropped frames. These are anecdotal observations from my machine, not controlled benchmarks.&lt;/p&gt;

&lt;h2&gt;Configuration tweaks&lt;/h2&gt;
&lt;p&gt;To further optimize Kitty, I made several configuration tweaks:
- 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;.
- 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;.
- 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.
- Benefited from Kitty leveraging vector CPU instructions, which can &lt;a target="_blank" href="https://github.com/kovidgoyal/kitty/issues/7005"&gt;enhance 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;h2&gt;My Kitty configuration explained&lt;/h2&gt;
&lt;p&gt;Here are some specific Kitty configurations I used and what they do:&lt;/p&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;figure class="article-figure"&gt;&lt;img src="https://raw.githubusercontent.com/marianposaceanu/dot-files/master/assets/comparison.jpg" alt="Side-by-side comparison of iTerm2 and Kitty" loading="lazy" decoding="async"&gt;&lt;figcaption&gt;iTerm2 versus Kitty terminal rendering comparison&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;&lt;em&gt;Figure: iTerm2 versus Kitty with my configurations.&lt;/em&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/26</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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;This article reflects the 2016-era Rails asset pipeline—Ruby Sass, libsass, Compass, therubyracer, and Node.js 6—and is not current Rails asset guidance.&lt;/p&gt;
&lt;p&gt;We all know that for a rather large Rails app with a substantial front end, 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;h2&gt;Replacing Ruby Sass with libsass&lt;/h2&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 several times 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;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;p&gt;Not orders of magnitude faster, but then again a roughly 4.5x improvement 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;h2&gt;Compass intermission&lt;/h2&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;h2&gt;Replacing the JS transpiling/minification/etc. with mini_racer&lt;/h2&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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&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 &lt;span class="o"&gt;(&lt;/span&gt;2 threads&lt;span class="o"&gt;)&lt;/span&gt; 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 &lt;span class="o"&gt;(&lt;/span&gt;2 threads&lt;span class="o"&gt;)&lt;/span&gt; - DOES NOT FINISH

Killed: 9
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here we are talking about roughly a 9–11x 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;h2&gt;Alternative to mini-racer for best possible performance&lt;/h2&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 precompiling 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;div class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;Runtime&lt;/th&gt;
&lt;th&gt;Timing&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;node.js 6.2.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;rake assets:clobber assets:precompile 7.30s user 1.27s system 102% cpu 8.358 total&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;mini-racer with libv8 5.x&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;rake assets:clobber assets:precompile 14.96s user 1.20s system 101% cpu 15.943 total&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&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;h2&gt;Should I use these in production?&lt;/h2&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/30</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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;Historical note: these are macOS Sierra 10.12-era procedures. SIP paths, Karabiner, Notes, and &lt;code&gt;photoanalysisd&lt;/code&gt; behavior may differ on current macOS releases.&lt;/p&gt;
&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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;brew update
&lt;span class="nv"&gt;$ &lt;/span&gt;rvm get stable
&lt;/code&gt;&lt;/pre&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;h2&gt;Fixing slow key repeat&lt;/h2&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 is 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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;defaults write NSGlobalDomain KeyRepeat &lt;span class="nt"&gt;-int&lt;/span&gt; 1
&lt;span class="nv"&gt;$ &lt;/span&gt;defaults write NSGlobalDomain InitialKeyRepeat &lt;span class="nt"&gt;-int&lt;/span&gt; 12
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Note: these can be reset back to their default via:&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;​defaults delete NSGlobalDomain KeyRepeat
&lt;span class="nv"&gt;$ &lt;/span&gt;defaults delete NSGlobalDomain InitialKeyRepeat
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order for them to take effect you can just log out or restart your Mac.&lt;/p&gt;

&lt;h2&gt;Fixing my pink external EIZO display&lt;/h2&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;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 re-enable 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;h2&gt;Notes.app crashing and minor performance tweaks&lt;/h2&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;h2&gt;Other issues&lt;/h2&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;h2&gt;Conclusion&lt;/h2&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;aside class="article-reference" aria-label="Reference"&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.&lt;/p&gt;
&lt;p&gt;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;/aside&gt;&lt;p&gt;In the end, the upgrade wasn&amp;#39;t painless; El Capitan went more smoothly by comparison 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;h2&gt;Credits&lt;/h2&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/35</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 works in progress. 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;Historical note: these examples reflect an older Rails and Ubuntu 14.04-era deployment, so current Rack::Attack and infrastructure APIs may differ.&lt;/p&gt;
&lt;p&gt;The main idea here is to filter as much of the noise in Rails logs 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;h2&gt;Nginx&lt;/h2&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;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server_name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;the-ip-address&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;404&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;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;h2&gt;Rack::Attack&lt;/h2&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_attack.rb&lt;/code&gt; and add:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# After 1 blocked request 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;p&gt;Any request 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;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;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;h2&gt;Conclusion&lt;/h2&gt;
&lt;div class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;404 exceptions/day&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;before&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;220 to 240&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;after&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;10 or fewer&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;&lt;p&gt;and this without completely turning off 404 exceptions.&lt;/p&gt;
&lt;p&gt;Filtering scanner noise is not access control. A one-request, one-day IP ban can also affect people behind NAT or a shared IP, and multi-instance deployments need shared state plus edge or global controls for consistent enforcement.&lt;/p&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-08-01T18:57:22Z</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 my turntable back into working order. 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;h2&gt;From Capistrano to Kamal&lt;/h2&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 al. 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 Dockerfile and direct support for Kamal: the Docker-based deployment 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 Oracle Cloud Always Free instance, using its Arm-based Ampere A1 Compute offering. On this server and connection, a fresh deploy that transfers the 130MB+ compressed Docker image over the SSH tunnel has stayed below 300KB/s. That is a small price to pay in my case, but it is an observation from this setup rather than a universal Kamal limit.&lt;/p&gt;

&lt;h2&gt;A smaller Docker image&lt;/h2&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;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# syntax = docker/dockerfile:1&lt;/span&gt;
&lt;span class="k"&gt;ARG&lt;/span&gt;&lt;span class="s"&gt; RUBY_VERSION=4.0.6&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;ruby:$RUBY_VERSION-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /rails&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; RAILS_ENV="production" BUNDLE_WITHOUT="development:test" BUNDLE_DEPLOYMENT="1"&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;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

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

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUNDLE_PATH="/usr/local/bundle" PATH="/usr/local/bundle/bin:$PATH" BUNDLE_JOBS=4 BUNDLE_RETRY=3&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;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

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --link . .&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x bin/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&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

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;base&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;runtime&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;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

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; BUNDLE_PATH="/usr/local/bundle" PATH="/usr/local/bundle/bin:$PATH"&lt;/span&gt;

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

&lt;span class="k"&gt;RUN &lt;/span&gt;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
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; rails:rails&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DATABASE_URL="sqlite3:///data/production.sqlite3" RUBY_YJIT_ENABLE="1"&lt;/span&gt;

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

&lt;span class="k"&gt;VOLUME&lt;/span&gt;&lt;span class="s"&gt; /data&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 80&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["bundle", "exec", "puma", "-C", "config/puma.rb"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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, is 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 stylus and its housing. This is straightforward as it doesn&amp;#39;t require me to go crazy trying to adjust a new cartridge from scratch.&lt;/p&gt;

&lt;h2&gt;Replacing Docker Desktop with Colima&lt;/h2&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;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;p&gt;I can replace most of Docker Desktop by running:&lt;/p&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;p&gt;But this does not give me a running Docker daemon; this is where Colima jumps in, as it provides:
- a Linux VM with Docker Engine (dockerd)
- a Docker context/socket (docker context use colima)
- runtime for Buildx builder containers
- 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;Before removing Docker Desktop, preserve or migrate any local images, volumes, and Docker contexts you still need; uninstalling it can otherwise discard local data. After I fully removed Docker Desktop from its own UI (this felt quite nice), all I had to do was:&lt;/p&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;p&gt;Then verify that everything works correctly:&lt;/p&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;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, Colima adds a container runtime without breaking Kamal whilst removing the icky Docker Desktop behemoth.&lt;/p&gt;

&lt;h2&gt;Credits&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a target="_blank" href="https://colima.run/docs/faq"&gt;Colima FAQ &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/33</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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, but the issue at hand should be reproducible on rbenv too. &lt;/p&gt;
&lt;p&gt;Historical note: this article reflects a Vim 8.0, JRuby, RVM, and macOS system Ruby setup from 2017; versions and defaults differ on current macOS systems.&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;h2&gt;Profiling Vim startup&lt;/h2&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: for almost 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;pre class="highlight shell"&gt;&lt;code&gt;vim &lt;span class="nt"&gt;--startuptime&lt;/span&gt; profile-data app/controllers/application_controller.rb
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;which yielded (edited for brevity):&lt;/p&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;p&gt;Over one second to open a simple Ruby file? ouch!&lt;/p&gt;
&lt;p&gt;The relevant line in that file is:&lt;/p&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;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;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;s:query_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;root&lt;span class="p"&gt;)&lt;/span&gt; abort
  &lt;span class="k"&gt;let&lt;/span&gt; code &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"print $:.join %q{,}"&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="k"&gt;shell&lt;/span&gt; &lt;span class="p"&gt;=~&lt;/span&gt;# &lt;span class="s1"&gt;'sh'&lt;/span&gt; &amp;amp;&amp;amp; &lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&amp;amp;&lt;span class="nb"&gt;shellxquote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; prefix &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'env PATH='&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;shellescape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$PATH&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; prefix &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &amp;amp;&lt;span class="nb"&gt;shellxquote&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"'"&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; path_check &lt;span class="p"&gt;=&lt;/span&gt; prefix&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'ruby --disable-gems -e "'&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; code &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; path_check &lt;span class="p"&gt;=&lt;/span&gt; prefix&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"ruby --disable-gems -e '"&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt; code &lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"'"&lt;/span&gt;
  &lt;span class="k"&gt;endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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;h2&gt;Forcing vim-ruby to use CRuby&lt;/h2&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; searching—including a historical Google+ link that is now unavailable— the solution was an appendix to &lt;code&gt;.vimrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;matchstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;$MY_RUBY_HOME&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jruby'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;g:ruby_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/usr/bin/ruby'&lt;/span&gt;
&lt;span class="k"&gt;endif&lt;/span&gt;
&lt;/code&gt;&lt;/pre&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 Ruby (on the macOS version used for this 2017 test, CRuby 2.0.0p648)&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;Results after the fix:&lt;/p&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;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/37</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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;, and 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 the Ruby world this is quite easy to implement - actually no one even calls it AOP (most likely because it is not within the pure definition of it) it&amp;#39;s just a 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 under the &amp;quot;instrumentation API for Ruby&amp;quot; moniker.&lt;/p&gt;

&lt;h2&gt;Instrumentation with ActiveSupport::Notifications&lt;/h2&gt;
&lt;p&gt;A simple example would look like:&lt;/p&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;p&gt;In some other part of our logic - preferably in a config file we can subscribe to those events:&lt;/p&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;'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="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 'some_action_that_requires_instrumentation' 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;h2&gt;Credits&lt;/h2&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/32</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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;​
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;Before performing these disk operations, take a verified backup or snapshot, shut down the VM correctly, and confirm that the virtual disk is thin-provisioned and that adequate free space is available. Writing from &lt;code&gt;/dev/zero&lt;/code&gt; intentionally exhausts the guest’s free space; understand and monitor the operation before proceeding.&lt;/p&gt;
&lt;p&gt;Without further ado: &lt;/p&gt;

&lt;h2&gt;Step one&lt;/h2&gt;
&lt;p&gt;Fill up all  the empty space with 0x00&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /dev/zero &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; zeroed_empty_space&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;zeroed_empy_space
&lt;/code&gt;&lt;/pre&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;h2&gt;Step two&lt;/h2&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;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s1"&gt;'/Applications/VMware Fusion.app/Contents/Library/'&lt;/span&gt; 
&lt;span class="nv"&gt;$ &lt;/span&gt;vmware-vdiskmanager &lt;span class="nt"&gt;-k&lt;/span&gt; your_vm_name.vmdk
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;and wait until it finishes - ensure the host has sufficient working free space for your particular disk and VMware configuration; do not rely on an exact one-to-one requirement.&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;
&lt;div class="article-table"&gt;&lt;table&gt;&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;VM size&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Original vmdk size&lt;/td&gt;
&lt;td style="text-align: right"&gt;70GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After zeroing free space and clean-up&lt;/td&gt;
&lt;td style="text-align: right"&gt;40GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;Credits:&lt;/h2&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/39</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 it 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 of 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 of 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 our perception of the world? It 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 career 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/41</id>
    <published>2025-07-05T21:20:05Z</published>
    <updated>2026-08-01T18:57:22Z</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 the metaphorical equivalent of 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>2026-08-01T18:57:22Z</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 in 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 arisen: 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-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’ timbre, 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 Sonys - 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 dawned 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 dissatisfaction 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>
</feed>
