<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kinvoki</title>
    <description>The latest articles on DEV Community by kinvoki (@kinvoki).</description>
    <link>https://dev.to/kinvoki</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F38824%2F0fce659f-c477-4dcc-8ad7-dcf6127a5a1b.png</url>
      <title>DEV Community: kinvoki</title>
      <link>https://dev.to/kinvoki</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kinvoki"/>
    <language>en</language>
    <item>
      <title>5 Strategies for Random Records from DB</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:39:36 +0000</pubDate>
      <link>https://dev.to/kinvoki/5-strategies-for-random-records-from-db-46lf</link>
      <guid>https://dev.to/kinvoki/5-strategies-for-random-records-from-db-46lf</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: I'm now using Strategy #5&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Table of Contents&lt;/li&gt;
&lt;li&gt;Why Get Random Records?&lt;/li&gt;
&lt;li&gt;Strategy #1 - Use &lt;code&gt;RANDOM()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Strategy #2 - Pluck &amp;amp; Array.sample&lt;/li&gt;
&lt;li&gt;Strategy #3 - &lt;code&gt;find_by&lt;/code&gt; with Min/Max ID&lt;/li&gt;
&lt;li&gt;Strategy #4 - Random Offset&lt;/li&gt;
&lt;li&gt;Strategy #5 - Where with Min/Max&lt;/li&gt;
&lt;li&gt;Tips and Caveats&lt;/li&gt;
&lt;li&gt;Your Thoughts?&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you've ever needed to collect a random record from a database whether using Rails or another framework/language, you may have found that there are several strategies to choose from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Get Random Records?
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let's consider why you'd need random records at all. Random behavior is rarely desired in applications since it leads to unpredictable results. But sometimes business requirements demand it.&lt;/p&gt;

&lt;p&gt;Common use cases include displaying random content (like a "random image" feature or "random author" on a book tracking site), or testing/debugging to catch edge cases and unexpected behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lhqkw722u7lfkzty65e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5lhqkw722u7lfkzty65e.png" alt="Elephant programmer analyzing database queries and performance metrics" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Strategy #1 - Use &lt;code&gt;RANDOM()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Use the database's built-in random function (&lt;code&gt;RANDOM()&lt;/code&gt; for PostgreSQL/SQLite, &lt;code&gt;RAND()&lt;/code&gt; for MySQL/MariaDB). This is the most robust approach, but slow as tables grow—around 260ms on my 1M-record test table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most robust&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gets slower as the table grows (~260ms on my 1M-record table)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'RANDOM()'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Strategy #2 - Pluck &amp;amp; Array.sample
&lt;/h2&gt;

&lt;p&gt;Pluck all IDs from the database and use Ruby's &lt;code&gt;Array#sample&lt;/code&gt; to pick one at random. Faster than Strategy #1 (~50ms on 1M records), but watch out for memory consumption as tables grow.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Faster (~50ms on 1M records)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gets slower as the table grows, and you may run out of memory (especially on small VMs)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Author&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="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Strategy #3 - &lt;code&gt;find_by&lt;/code&gt; with Min/Max ID
&lt;/h2&gt;

&lt;p&gt;A third strategy is to generate a random ID between the minimum and maximum ID values, then use &lt;code&gt;find_by&lt;/code&gt; to look up the record. This strategy is extremely fast, taking around 17ms on a table with 1,000,000 records, but it can be brittle if there are deleted records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely fast if no deleted records (~17ms on my test data)&lt;/li&gt;
&lt;li&gt;Table growth doesn't affect lookup speed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires numeric, sequential IDs (no GUIDs or varchar)&lt;/li&gt;
&lt;li&gt;Breaks with deleted records (missing IDs)&lt;/li&gt;
&lt;li&gt;Error handling helps, but gets slower with many gaps
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&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="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Strategy #4 - Random Offset
&lt;/h2&gt;

&lt;p&gt;Use a random offset to look up a record. Not as fast (~120ms on 1M records), but works with any ID type and needs no error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No error handling needed, works with any ID type&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Slower and inconsistent: averages 120ms but varies from 20ms to 600ms depending on offset position (that's how offset works)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;rand&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="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;limit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Strategy #5 - Where with Min/Max
&lt;/h2&gt;

&lt;p&gt;My preferred approach: generate a random ID between min and max values, then use a &lt;code&gt;where&lt;/code&gt; lookup. Extremely fast (1-6ms on 1M records) and handles deleted records gracefully.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blazing fast: 1-6ms&lt;/li&gt;
&lt;li&gt;Table growth doesn't affect speed (faster than offset)&lt;/li&gt;
&lt;li&gt;Handles deleted records by finding the next available match&lt;/li&gt;
&lt;li&gt;Chainable with other scopes (e.g., &lt;code&gt;Author.published_today.random_record&lt;/code&gt;), though this reimplements offset behavior and may limit your data set. Still faster than using &lt;code&gt;offset&lt;/code&gt; directly due to &lt;a href="https://stackoverflow.com/a/4502426/198424" rel="noopener noreferrer"&gt;how offset works&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less intuitive than &lt;code&gt;offset&lt;/code&gt;, but offset gets slower with large datasets
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# `random_id` Can be memoized / cached,&lt;/span&gt;
&lt;span class="c1"&gt;# but beware of scoping before caching(!!!)&lt;/span&gt;
&lt;span class="c1"&gt;# as you can get empty results where many are expected&lt;/span&gt;
&lt;span class="n"&gt;random_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;rand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;minimum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;maximum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="no"&gt;Author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"id &amp;gt;= ?"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;limit&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used to prefer Strategy #3 (even with occasional deleted records, it stayed fast). Strategy #4 also worked well. However, while answering a StackOverflow question that led to this blog post, I discovered Strategy #5—now my preferred option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gypb0am1ijss01v7uqy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gypb0am1ijss01v7uqy.png" alt="Ruby developer elephant working on database optimization code" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Tips and Caveats
&lt;/h2&gt;

&lt;p&gt;A couple of tips to keep in mind when implementing these strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Caching &lt;code&gt;Author.count&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the exact current count isn't critical, preload &lt;code&gt;Author.count&lt;/code&gt; and memoize it (e.g., as &lt;code&gt;Author.total_count&lt;/code&gt;), or cache it in an app-load config. This eliminates the count query overhead and speeds up Strategy #3 to under 5ms and Strategy #4 to about 100ms. Here's an example of how to do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;total_count&lt;/span&gt;
  &lt;span class="vi"&gt;@total_count&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Be Careful with Optimizations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;However, I would warn against going crazy with optimizations here, such as caching min/max values, as depending on the context, scoping, etc, it may lead to unexpected results. If you use Strategy #5, the bottleneck won't be in the database, so making 3 quick queries is preferable, unless it really-really matters for your WebScale™ app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error Handling:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For error handling, you have two options: retry lookups until finding an existing record, or do multiple Strategy #3 lookups and randomly select from the results. Depending on how many gaps you have in your data set, Strategy #3 may not be the best choice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Thoughts?
&lt;/h2&gt;

&lt;p&gt;Did I miss any approaches, or made a glaring blunder? Please, comment and let me know :)&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: February 20, 2024&lt;/em&gt; • &lt;em&gt;Last updated: January 15, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/5-strategies-random-records-from-db/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/5-strategies-random-records-from-db/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>postgres</category>
      <category>database</category>
    </item>
    <item>
      <title>Valentine's Day Equation Plotted in Ruby</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:39:20 +0000</pubDate>
      <link>https://dev.to/kinvoki/valentines-day-equation-plotted-in-ruby-37bp</link>
      <guid>https://dev.to/kinvoki/valentines-day-equation-plotted-in-ruby-37bp</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: Use Ruby and GNUPlot to plot the mathematical Valentine's Day heart equation. Perfect for teaching kids programming through something they relate to.&lt;/p&gt;




&lt;p&gt;I'm trying to expose my kids to programming bit by bit. 😊 Things that work the best are something that they relate to. Almost right on cue, &lt;a href="https://dev.to/devlorenzo/the-san-valentine-equation-1jb9"&gt;this blog post&lt;/a&gt; came up on my feed on &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; on Saturday and I thought it would be a fun activity to help kids make Valentines in a new way. Thank you, &lt;a href="https://dev.to/devlorenzo"&gt;@DevLorenz0&lt;/a&gt; for the formula! ❤️&lt;/p&gt;

&lt;p&gt;All you need is &lt;a href="https://www.ruby-lang.org/en/downloads/" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt; &amp;amp; &lt;a href="http://www.gnuplot.info/" rel="noopener noreferrer"&gt;GNUPlot&lt;/a&gt;, and possibly &lt;a href="http://www.gnuplot.info/docs_5.4/Gnuplot_5_4.pdf" rel="noopener noreferrer"&gt;GNUPlot official docs PDF&lt;/a&gt; if you want to experiment further.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# on Mac&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;gnuplot
gem &lt;span class="nb"&gt;install &lt;/span&gt;gnuplot

&lt;span class="c"&gt;# Chocolatey has a package for Windows&lt;/span&gt;
&lt;span class="c"&gt;# choco install gnuplot&lt;/span&gt;

&lt;span class="c"&gt;# Use rpm / apt on Linux&lt;/span&gt;
&lt;span class="c"&gt;# apt-get install gnuplot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# valentine_day_plot.rb&lt;/span&gt;
&lt;span class="c1"&gt;# Tested on Ruby 2.7.2&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'rubygems'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'gnuplot'&lt;/span&gt;

&lt;span class="n"&gt;valentines_day_function&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;lambda&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&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="mi"&gt;16&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;sin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&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="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;13&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;5&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&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;cos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# Experimentally found that rounding to 4&lt;/span&gt;
  &lt;span class="c1"&gt;# produces the best results for us&lt;/span&gt;
  &lt;span class="p"&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;round&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="nf"&gt;to_f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&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="nf"&gt;to_f&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Adjust this to get more or less number of plot points&lt;/span&gt;
&lt;span class="c1"&gt;# At the end we transpose array, to shape data for&lt;/span&gt;
&lt;span class="c1"&gt;# Gnuplot::DataSet input below&lt;/span&gt;
&lt;span class="no"&gt;DISTANCE_BETWEEN_POINTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.0001&lt;/span&gt;
&lt;span class="no"&gt;COORDINATES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="no"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DISTANCE_BETWEEN_POINTS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                                 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_a&lt;/span&gt;
                                 &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&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;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;valentines_day_function&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&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;transpose&lt;/span&gt;

&lt;span class="no"&gt;Gnuplot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&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;gp&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="no"&gt;Gnuplot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Plot&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="n"&gt;gp&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;plot&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;output&lt;/span&gt; &lt;span class="s1"&gt;'valentine_day_plot.pdf'&lt;/span&gt;
    &lt;span class="c1"&gt;# Sized as a nice square to be printed on a A4 or US Letter page&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;terminal&lt;/span&gt; &lt;span class="s1"&gt;'pdf colour size 8in,8in'&lt;/span&gt;

    &lt;span class="c1"&gt;# The following is self-explanatory, but setting range too small,&lt;/span&gt;
    &lt;span class="c1"&gt;# will put the graph out of bounds&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xrange&lt;/span&gt; &lt;span class="s1"&gt;'[-20:20]'&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;yrange&lt;/span&gt; &lt;span class="s1"&gt;'[-20:20]'&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt;  &lt;span class="s2"&gt;"Valentine's Day Equation"&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ylabel&lt;/span&gt; &lt;span class="s1"&gt;'Love-y'&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;xlabel&lt;/span&gt; &lt;span class="s1"&gt;'Love-x'&lt;/span&gt;

    &lt;span class="c1"&gt;# DataSet.new can also take the formula directly, not just an array&lt;/span&gt;
    &lt;span class="c1"&gt;# of shape [x-coordinates, y-coordinates]&lt;/span&gt;
    &lt;span class="n"&gt;plot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Gnuplot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DataSet&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="no"&gt;COORDINATES&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;ds&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="c1"&gt;# You can also draw graph using various other types,&lt;/span&gt;
      &lt;span class="c1"&gt;# 'linespoints','steps','fillsteps', etc&lt;/span&gt;
      &lt;span class="c1"&gt;# See http://www.gnuplot.info/docs_5.4/Gnuplot_5_4.pdf&lt;/span&gt;
      &lt;span class="c1"&gt;# for full docs&lt;/span&gt;
      &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'lines'&lt;/span&gt;
      &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;linewidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;
      &lt;span class="c1"&gt;# Change this to the name of your Valentine&lt;/span&gt;
      &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'My Valentine'&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpniopyedi0poy13kxeee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpniopyedi0poy13kxeee.png" alt="Valentine's Day heart graph plotted with smooth lines using Ruby and GNUPlot" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is an example of the plotted graph if we change &lt;code&gt;DISTANCE_BETWEEN_POINTS = 0.1&lt;/code&gt; and &lt;code&gt;ds.with = 'steps'&lt;/code&gt; and &lt;code&gt;ds.linewidth = 1&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzn3bntmxk3bxhwk26bw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzn3bntmxk3bxhwk26bw.png" alt="Valentine's Day heart graph plotted with steps style showing discrete points" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Bonus: Converting to Image Formats
&lt;/h2&gt;

&lt;p&gt;The PDF we generated above is perfect for printing, but if you wanted to convert it to a JPEG or PNG, you can use a graphics app like Photoshop or GIMP, or simply use ImageMagick:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install it if you don't have it&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;imagemagick
convert &lt;span class="nt"&gt;-quality&lt;/span&gt; 300 &lt;span class="nt"&gt;-density&lt;/span&gt; 300 valentine_day_plot.pdf valentine_day_plot.jpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.ruby-lang.org/en/downloads/" rel="noopener noreferrer"&gt;Ruby Downloads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gnuplot.info/" rel="noopener noreferrer"&gt;GNUPlot Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.gnuplot.info/docs_5.4/Gnuplot_5_4.pdf" rel="noopener noreferrer"&gt;GNUPlot Documentation PDF&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://imagemagick.org/" rel="noopener noreferrer"&gt;ImageMagick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/devlorenzo/the-san-valentine-equation-1jb9"&gt;Original Valentine Equation Post&lt;/a&gt; by &lt;a class="mentioned-user" href="https://dev.to/devlorenz0"&gt;@devlorenz0&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Last updated&lt;/strong&gt;: February 15, 2021&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: February 15, 2021&lt;/em&gt; • &lt;em&gt;Last updated: January 25, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/valentines-day-equation-plotted-in-ruby/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/valentines-day-equation-plotted-in-ruby/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>gnuplot</category>
      <category>math</category>
      <category>datavisualization</category>
    </item>
    <item>
      <title>Upgrading to GitLab 15.0 CE from GitLab 14.9.3</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:35:45 +0000</pubDate>
      <link>https://dev.to/kinvoki/upgrading-to-gitlab-150-ce-from-gitlab-1493-5f0n</link>
      <guid>https://dev.to/kinvoki/upgrading-to-gitlab-150-ce-from-gitlab-1493-5f0n</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: GitLab won't let you upgrade directly from 14.9.3 to 15.0. You must upgrade to 14.10.x first, then to 15.0.&lt;/p&gt;




&lt;p&gt;Every time I need to upgrade to the next big GitLab CE release, I end up jumping through hoops, looking for specific steps or going through &lt;code&gt;.bash_history&lt;/code&gt; to see what I've done in the past. This is a small self-note on what to do next time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbon4m1khg38ruzbdbp9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbon4m1khg38ruzbdbp9.png" alt="Determined DevOps engineer dog carefully managing GitLab upgrade process with multiple monitors" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;So running my usual update commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Led me to a cryptic message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Preparing to unpack .../gitlab-ce_15.0.0-ce.0_amd64.deb ...
gitlab preinstall: It seems you are upgrading from major version 14 to major version 15.
gitlab preinstall: It is required to upgrade to the latest 14.10.x version first before proceeding.
gitlab preinstall: Please follow the upgrade documentation at https://docs.gitlab.com/ee/update/index.html#upgrade-paths
dpkg: error processing archive /var/cache/apt/archives/gitlab-ce_15.0.0-ce.0_amd64.deb &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nt"&gt;--unpack&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, I'm running &lt;code&gt;14.9.3&lt;/code&gt; which is pretty recent, considering that I update my machines every few weeks. The confusing part here is that there are no simple instructions in the error message or on the &lt;a href="https://docs.gitlab.com/ee/update/index.html#upgrade-paths" rel="noopener noreferrer"&gt;support website&lt;/a&gt; as to what to do next. The more confounding thing is, that support website doesn't even mention version 14.10 as an option.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The support link outlines the upgrade path and which versions to use, **but not how to do it&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  ⚠️ Warning
&lt;/h2&gt;

&lt;p&gt;The following command was executed on a system that was &lt;strong&gt;FULLY&lt;/strong&gt; backed up using both &lt;a href="https://docs.gitlab.com/omnibus/settings/backups.html#backup" rel="noopener noreferrer"&gt;GitLab backup process&lt;/a&gt; and a VM snapshot.&lt;/p&gt;

&lt;p&gt;Do this at your own risk 😳&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well, turns out it's actually very simple on Ubuntu/Debian using apt for my GitLab:&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Upgrade to 14.10.x
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First I removed old backups that were taking up a lot of space&lt;/span&gt;
&lt;span class="c"&gt;# from /var/opt/gitlab/backups/&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; gitlab-ce&lt;span class="o"&gt;=&lt;/span&gt;14.10.0-ce.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process may take a bit of time depending on how fast your VM is &amp;amp; how big your GitLab install is. Took about 15 mins for me.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  🔑 Key Thing
&lt;/h2&gt;

&lt;p&gt;If you need to upgrade from an earlier version of GitLab - the same steps apply. You just need to follow the upgrade path outlined on the &lt;a href="https://docs.gitlab.com/ee/update/index.html#upgrade-paths" rel="noopener noreferrer"&gt;support website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And NO you CANNOT skip steps, the result will be unpredictable.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: GitLab has a convenient &lt;a href="https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/" rel="noopener noreferrer"&gt;Upgrade Path tool&lt;/a&gt; that shows you exactly which versions to upgrade through.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Step 2: Upgrade to 15.0
&lt;/h3&gt;

&lt;p&gt;Now we're all ready to install the latest GitLab CE, which can be done using a general command like so, if you like to risk it all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;apt autoremove &lt;span class="nt"&gt;-y&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt upgrade &lt;span class="nt"&gt;-y&lt;/span&gt; gitlab-ce
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Don't Panic
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;P.S.:&lt;/strong&gt; Don't forget to reboot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S.:&lt;/strong&gt; Reminder to Future Me - don't freak out if you see a 500 error response. GitLab takes 3-5 mins to fully come up after a full reboot.&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/ee/update/index.html#upgrade-paths" rel="noopener noreferrer"&gt;GitLab Upgrade Paths Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitlab-com.gitlab.io/support/toolbox/upgrade-path/" rel="noopener noreferrer"&gt;GitLab Upgrade Path Tool&lt;/a&gt; (shows exact upgrade sequence)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.gitlab.com/omnibus/settings/backups.html#backup" rel="noopener noreferrer"&gt;GitLab Backup Process&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Last updated&lt;/strong&gt;: May 24, 2022&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: May 24, 2022&lt;/em&gt; • &lt;em&gt;Last updated: January 22, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/upgrading-to-gitlab-15-from-14/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/upgrading-to-gitlab-15-from-14/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gitlab</category>
      <category>devops</category>
      <category>infrastructure</category>
      <category>selfhosting</category>
    </item>
    <item>
      <title>Lucky is Lightning Fast!</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:34:05 +0000</pubDate>
      <link>https://dev.to/kinvoki/lucky-is-lightning-fast-3m2c</link>
      <guid>https://dev.to/kinvoki/lucky-is-lightning-fast-3m2c</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: Lucky Framework (Crystal) ranks near the top in TechEmpower Benchmark #20, competing strongly against full-stack frameworks like Rails, Phoenix, and Django.&lt;/p&gt;




&lt;p&gt;TechEmpower just posted &lt;a href="https://www.techempower.com/benchmarks/#section=data-r20&amp;amp;hw=ph&amp;amp;test=json&amp;amp;c=e" rel="noopener noreferrer"&gt;Round #20&lt;/a&gt; of their Web Framework benchmarks and &lt;a href="https://luckyframework.org/" rel="noopener noreferrer"&gt;Lucky Framework&lt;/a&gt; (that runs on &lt;a href="https://crystal-lang.org/" rel="noopener noreferrer"&gt;Crystal Language&lt;/a&gt;) is in it, thanks to work done by &lt;a class="mentioned-user" href="https://dev.to/matthewmcgarvey"&gt;@matthewmcgarvey&lt;/a&gt; (Great job on the benchmark!).&lt;/p&gt;

&lt;p&gt;Pretty solid result! Especially if you only consider "Full-stack" frameworks and not micro-stuff.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Lucky is near the top in Crystal-land (and beyond)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the Crystal ecosystem, Lucky ranks near the top. And in the broader web framework landscape too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Crystal frameworks dominate speed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The main speed-competitor in Crystal-land is Raze framework (which has been retired). Lucky's speed is comparable to most other Crystal frameworks: Kemal, Spider-Gazelle, Onyx &amp;amp; Amber (all did pretty well on speed thanks to Crystal 😊)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Lucky holds its own against full-featured frameworks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most important comparison for me is against true full-featured frameworks like Rails, Hanami, Phoenix, Django, Symphony, Grails, Spring, Prologue, etc. And &lt;strong&gt;Lucky&lt;/strong&gt; is doing really well there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Filtering the Noise
&lt;/h2&gt;

&lt;p&gt;If you go to the main page, there are ~250 benchmark results for every web-framework under the sun. However, that's too many results for my taste 😊 i.e. things like "h2o.cr" on the list are too purpose-specific for me or anything I would do in a normal course of things, developing an enterprise or user-facing web application.&lt;/p&gt;

&lt;p&gt;I'm currently considering a Full-Stack Framework for a small web-based enterprise-type project and deciding between Phoenix, Lucky &amp;amp; Rails. Each has its own strengths for my use-case. So &lt;a href="https://www.techempower.com/benchmarks/#section=data-r20&amp;amp;hw=ph&amp;amp;test=fortune&amp;amp;b=4&amp;amp;l=ypto4d-sd&amp;amp;c=a&amp;amp;d=8&amp;amp;o=8&amp;amp;f=zhayfb-zik03j-xalvcv-zih1f3-zhxjwf-zijunz-g3bqwu-zijzei-zfzbi7-zhx79b-piyn7j-cm7" rel="noopener noreferrer"&gt;this particular comparison is more of an interest to me&lt;/a&gt;, based on frameworks &amp;amp; languages that I've had exposure to or would even consider for such an application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p6su483uzys91davxaj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2p6su483uzys91davxaj.png" alt="TechEmpower Benchmark comparison showing Lucky Framework performance against Rails, Phoenix, Django and other full-stack frameworks" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the benchmark, and I hope you give &lt;a href="https://luckyframework.org/" rel="noopener noreferrer"&gt;Lucky Framework&lt;/a&gt; a spin.&lt;/p&gt;




&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  ⚠️ Take These Benchmarks with a Grain of Salt
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Not all frameworks are properly optimized&lt;/strong&gt; - Some frameworks on the list may not be configured for optimal performance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed is only one aspect&lt;/strong&gt; - Performance should be one consideration among many&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystem matters more&lt;/strong&gt; - Ecosystem, dev experience, and productivity are all more important than speed in most cases&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Having said that&lt;/strong&gt; ☝️ - Lucky ticks all 3 boxes for me&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://luckyframework.org/" rel="noopener noreferrer"&gt;Lucky Framework Official Site&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://crystal-lang.org/" rel="noopener noreferrer"&gt;Crystal Language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.techempower.com/benchmarks/#section=data-r20&amp;amp;hw=ph&amp;amp;test=json&amp;amp;c=e" rel="noopener noreferrer"&gt;TechEmpower Benchmark Round #20&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.techempower.com/benchmarks/#section=data-r20&amp;amp;hw=ph&amp;amp;test=fortune&amp;amp;b=4&amp;amp;l=ypto4d-sd&amp;amp;c=a&amp;amp;d=8&amp;amp;o=8&amp;amp;f=zhayfb-zik03j-xalvcv-zih1f3-zhxjwf-zijunz-g3bqwu-zijzei-zfzbi7-zhx79b-piyn7j-cm7" rel="noopener noreferrer"&gt;My Framework Comparison&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Last updated&lt;/strong&gt;: February 9, 2021&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: February 08, 2021&lt;/em&gt; • &lt;em&gt;Last updated: February 09, 2021&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/lucky-framework-lightning-fast-benchmarks/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/lucky-framework-lightning-fast-benchmarks/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>crystal</category>
      <category>lucky</category>
      <category>benchmarks</category>
      <category>performance</category>
    </item>
    <item>
      <title>When FTP Stands for Frustrating Transfer Problem</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:33:38 +0000</pubDate>
      <link>https://dev.to/kinvoki/when-ftp-stands-for-frustrating-transfer-problem-1p32</link>
      <guid>https://dev.to/kinvoki/when-ftp-stands-for-frustrating-transfer-problem-1p32</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR&lt;/strong&gt;: After 5 hours of troubleshooting FTP over OpenVPN, the solution was enabling compression—the exact opposite of what everyone recommended.&lt;/p&gt;




&lt;p&gt;Let's talk about FTP file transfers, shall we? Picture this: A typical day, armed with FileZilla (for adhoc testing) and a Ruby script employing &lt;code&gt;Net::FTP&lt;/code&gt; gem, tasked with the simple job of uploading files to a remote ProFTPD server. Sounds like a snooze-fest, right? Oh, how wrong you'd be!&lt;/p&gt;

&lt;p&gt;In the office, everything was smooth sailing. File uploads were as effortless as breathing. But from home, connected through OpenVPN, I was greeted with an error so cryptic it might as well have been written in hieroglyphics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Net::FTPTempError: 450 Transfer aborted. Link to file server lost.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And so began my descent into the swirling vortex of troubleshooting. pfSense firewall settings, NAT configurations, FTP server settings—everything was under scrutiny.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp59c0aeurhr7zkabs9s6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp59c0aeurhr7zkabs9s6.png" alt="Frustrated network administrator alligator in glasses surrounded by tangled network cables and connection diagrams" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  5 Hours In...
&lt;/h2&gt;

&lt;p&gt;I tried every trick in the book:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Switching between active and passive FTP&lt;/li&gt;
&lt;li&gt;Peering into the soul of the OpenVPN configuration&lt;/li&gt;
&lt;li&gt;Allowing ALL traffic between LAN &amp;amp; OpenVPN (a big no-no)&lt;/li&gt;
&lt;li&gt;Trying every possible permission configuration&lt;/li&gt;
&lt;li&gt;ProFTPd config changes&lt;/li&gt;
&lt;li&gt;Even blaming poor FileZilla for a hot second&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But alas! I was no closer to a solution.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwn1yfdac7snzt85pqtf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiwn1yfdac7snzt85pqtf.png" alt="Determined dog developer in headphones debugging network issues with multiple monitors showing network diagrams and logs" width="800" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes. This too was not out of the question.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Ghost Files
&lt;/h2&gt;

&lt;p&gt;Oh, and let's not forget the ghost files! Every failed attempt at uploading a file would result in the creation of a zero-sized file on the server. It was like the server was pointing and laughing at me, creating an empty shell of a file, a cruel reminder of my predicament.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Plot Twist
&lt;/h2&gt;

&lt;p&gt;Just when I was about to call it a day and accept defeat, I stumbled upon a tidbit of advice:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Some VPN configurations may have compression enabled, which can cause issues with certain types of traffic. You can try disabling compression on your OpenVPN configuration to see if it resolves the issue."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I thought to myself, "Could it be that simple?"&lt;/p&gt;

&lt;p&gt;But wait! Plot twist! &lt;strong&gt;My OpenVPN configuration already had compression disabled.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So, in an act of sheer desperation, I decided to go against the grain. I enabled compression, held my breath, and...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eureka!&lt;/strong&gt; It worked. I kid you not. It worked faster too!&lt;/p&gt;




&lt;h2&gt;
  
  
  The Lesson
&lt;/h2&gt;

&lt;p&gt;So there you have it, folks. The thrilling saga of FTP troubleshooting, where the hero turns out to be the unsuspecting compression feature, the very thing that was supposedly the villain of the piece. Just goes to show, when it comes to networking, expect the unexpected.&lt;/p&gt;

&lt;p&gt;If there's a moral to this story, it's this: &lt;strong&gt;The answer might not always be where you expect it to be.&lt;/strong&gt; And sometimes, you just gotta switch things on instead of off.&lt;/p&gt;

&lt;p&gt;So, here's to more head-scratching adventures in the world of IT. Because let's face it, we wouldn't have it any other way!&lt;/p&gt;

&lt;p&gt;Happy troubleshooting!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: May 13, 2024&lt;/em&gt; • &lt;em&gt;Last updated: January 18, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/when-ftp-stands-for-frustrating-transfer-problem/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/when-ftp-stands-for-frustrating-transfer-problem/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ftp</category>
      <category>networking</category>
      <category>troubleshooting</category>
      <category>devops</category>
    </item>
    <item>
      <title>Functional Ruby Programming with Trailblazer</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:25:33 +0000</pubDate>
      <link>https://dev.to/kinvoki/functional-ruby-programming-with-trailblazer-3h29</link>
      <guid>https://dev.to/kinvoki/functional-ruby-programming-with-trailblazer-3h29</guid>
      <description>&lt;p&gt;You might be surprised to learn that you're already using many functional programming (FP) approaches in your Ruby code without realizing it. While languages like Haskell enforce these principles, Ruby and Python leave it up to the programmer to make conscious choices. Let's explore how we can leverage these FP concepts in Ruby, particularly when using the Trailblazer (TRB) framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Functional Programming Concepts in Ruby and Trailblazer
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Embrace Immutability
&lt;/h3&gt;

&lt;p&gt;In Trailblazer, we treat incoming data as immutable. The only mutable data structure is the internal hash (&lt;code&gt;options[:my_key]&lt;/code&gt; or &lt;code&gt;ctx[:my_other_key]&lt;/code&gt; in TRB 2.1). When transforming data, always assign the result to a new key instead of modifying in place:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;transform_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_array&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;ctx&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:transformed_array&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input_array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upcase&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consider using gems like Hamster for immutable data structures, or simply maintain self-discipline in your approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Simplify Function Signatures
&lt;/h3&gt;

&lt;p&gt;Keep your method signatures simple, limiting them to 2-4 attributes. This practice enhances testability and reduces code complexity. For most Trailblazer steps, this structure suffices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;my_step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&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="c1"&gt;# Your logic here&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take advantage of named parameters and default values when applicable.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Break Down Complex Operations
&lt;/h3&gt;

&lt;p&gt;Divide your methods, steps, and operations into smaller, more manageable units. This approach may require more initial setup but pays dividends in maintainability and composability. Treat your Trailblazer operations as functional objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateUser&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:validate&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:persist&lt;/span&gt;
  &lt;span class="n"&gt;step&lt;/span&gt; &lt;span class="ss"&gt;:send_welcome_email&lt;/span&gt;

  &lt;span class="c1"&gt;# Each step is a small, focused function&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Eliminate External State
&lt;/h3&gt;

&lt;p&gt;Ensure your operations receive everything they need when called, avoiding reliance on global or external state. This doesn't preclude the use of global configs, but their values should be provided explicitly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;CreateUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;user_params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;config: &lt;/span&gt;&lt;span class="no"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Leverage Trailblazer's Functional Design
&lt;/h3&gt;

&lt;p&gt;Trailblazer is built with functional concepts in mind. Many steps can be implemented as lambda functions, embracing a pure functional approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UpdateProfile&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Trailblazer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Operation&lt;/span&gt;
  &lt;span class="n"&gt;step&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;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&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="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;# More steps...&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Consider Adding Type Checking
&lt;/h3&gt;

&lt;p&gt;While Ruby is dynamically typed, you can add type checking to your codebase using gems like Sorbet, RBS, Dry-Types or Literal. This can help catch errors early and improve code clarity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Using Sorbet&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'sorbet-runtime'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
  &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;T&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Sig&lt;/span&gt;

  &lt;span class="n"&gt;sig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;name: &lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;age: &lt;/span&gt;&lt;span class="no"&gt;Integer&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;void&lt;/span&gt; &lt;span class="p"&gt;}&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="nb"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="vi"&gt;@name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;name&lt;/span&gt;
    &lt;span class="vi"&gt;@age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7. Implement Lazy Evaluation
&lt;/h3&gt;

&lt;p&gt;For large data structures, consider using lazy evaluation to improve performance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;large_collection&lt;/span&gt; &lt;span class="o"&gt;=&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;1_000_000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;|&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;2&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="ss"&gt;:even?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many of these functional programming principles are likely already part of your Ruby toolkit, even if you haven't explicitly labeled them as such. By consciously applying these concepts, especially when working with Trailblazer, you can write more maintainable, testable, and robust code.&lt;/p&gt;

&lt;p&gt;Remember, while Ruby may not be Haskell, it offers the flexibility to incorporate functional programming paradigms alongside its object-oriented nature. Embrace this hybrid approach to level up your Ruby and Trailblazer projects!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published: November 29, 2018&lt;/em&gt; • &lt;em&gt;Last updated: January 20, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/functional-programming-with-trailblazer/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/functional-programming-with-trailblazer/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>trailblazer</category>
      <category>functional</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Ruby Hash - String Keys Vs Symbol Keys</title>
      <dc:creator>kinvoki</dc:creator>
      <pubDate>Mon, 03 Nov 2025 06:12:09 +0000</pubDate>
      <link>https://dev.to/kinvoki/ruby-hash-string-keys-vs-symbol-keys-202p</link>
      <guid>https://dev.to/kinvoki/ruby-hash-string-keys-vs-symbol-keys-202p</guid>
      <description>&lt;p&gt;There was recently a discussion on &lt;a href="https://trailblazer.to/" rel="noopener noreferrer"&gt;Trailblazer&lt;/a&gt; Gitter channel about Hashes as params, how to pass them around, and as customary a flame-war war &lt;del&gt;insued&lt;/del&gt; never happened, and it came down to a measuring contest: &lt;del&gt;whose&lt;/del&gt; which key is better and faster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  TLDR;
&lt;/h2&gt;

&lt;p&gt;For small hashes it doesn’t really matter, for larger ones :symbol is 1.15x faster than string’ keys. Frozen string’ keys are in-between.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The best way to argue, is to present facts. So I coded a couple of benchmarks, and submitted a pull request to &lt;a href="https://github.com/JuanitoFatas/fast-ruby/pull/135" rel="noopener noreferrer"&gt;fast-ruby (Github)&lt;/a&gt;. Here are the details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Round 1: Hash[:symbol] vs Hash[“string”]
&lt;/h2&gt;

&lt;p&gt;First lets measure allocating Hash in various ways that Ruby gives us&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"benchmark/ips"&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;symbol_key&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;symbol: &lt;/span&gt;&lt;span class="mi"&gt;42&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;def&lt;/span&gt; &lt;span class="nf"&gt;symbol_key_arrow&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:symbol&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&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;def&lt;/span&gt; &lt;span class="nf"&gt;symbol_key_in_string_form&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sym_str'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&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;def&lt;/span&gt; &lt;span class="nf"&gt;string_key_arrow_double_quotes&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&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;def&lt;/span&gt; &lt;span class="nf"&gt;string_key_arrow_single_quotes&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&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;ips&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Hash comparison"&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="sx"&gt;%({symbol: 42} )&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="n"&gt;symbol_key&lt;/span&gt; &lt;span class="p"&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="sx"&gt;%({:symbol =&amp;gt; 42} )&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;symbol_key_arrow&lt;/span&gt; &lt;span class="p"&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="sx"&gt;%({'sym_str': 42} )&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;symbol_key_in_string_form&lt;/span&gt; &lt;span class="p"&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="sx"&gt;%({"string" =&amp;gt; 42} )&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;string_key_arrow_double_quotes&lt;/span&gt; &lt;span class="p"&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="sx"&gt;%({'string' =&amp;gt; 42} )&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;string_key_arrow_single_quotes&lt;/span&gt; &lt;span class="p"&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;compare!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Hash&lt;/span&gt; &lt;span class="n"&gt;comparison&lt;/span&gt;
&lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;a5688e2a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;PRISM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arm64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;darwin25&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Warming&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="o"&gt;--------------------------------------&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;symbol: &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;    &lt;span class="mf"&gt;807.478&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:symbol&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;1.070&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sym_str'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;1.083&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;1.075&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;1.047&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="no"&gt;Calculating&lt;/span&gt; &lt;span class="o"&gt;-------------------------------------&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;symbol: &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;10.776&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;9.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;92.80&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;53.294&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.010967&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:symbol&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;10.921&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;4.1&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;91.56&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;54.591&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.007897&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sym_str'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;10.703&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;9.3&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;93.44&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;53.090&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.023091&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;10.543&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;9.8&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;94.85&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;52.653&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.063165&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;      &lt;span class="mf"&gt;10.561&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;94.69&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;52.338&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.024881&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="no"&gt;Comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:symbol&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10921358.3&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;s&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;symbol: &lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10775503.9&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'sym_str'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10702520.9&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;'string'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10560679.5&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;10543355.5&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Used to be a difference on Ruby 2.*, but running benchmark on Ruby 3.4.7 - we can see that style doesn’t really matter. Using string &lt;code&gt;"42"&lt;/code&gt; for values instead of number &lt;code&gt;42&lt;/code&gt;, will slightly change the benchmark, but insignificant in the grand scheme of things.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  Don’t overcomplicate
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;string&lt;/em&gt; keys are not going to be the speed bottleneck in your app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Round 2: But what about large hashes?
&lt;/h2&gt;

&lt;p&gt;Don’t worry I got you covered. Lets try it out for a 1000 key-value pairs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"benchmark/ips"&lt;/span&gt;


&lt;span class="no"&gt;STRING_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&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="s2"&gt;"key_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&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="nf"&gt;shuffle&lt;/span&gt;
&lt;span class="no"&gt;FROZEN_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2001&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&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="s2"&gt;"key_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&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="nf"&gt;freeze&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;
&lt;span class="no"&gt;SYMBOL_KEYS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3001&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;4000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;{&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="s2"&gt;"key_&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;x&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="nf"&gt;to_sym&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;shuffle&lt;/span&gt;

&lt;span class="c1"&gt;# If we use static values for Hash, speed improves even more.&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;symbol_hash&lt;/span&gt;
  &lt;span class="no"&gt;SYMBOL_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&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="nb"&gt;rand&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;100&lt;/span&gt;&lt;span class="p"&gt;)]}.&lt;/span&gt;&lt;span class="nf"&gt;to_h&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;string_hash&lt;/span&gt;
  &lt;span class="no"&gt;STRING_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&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="nb"&gt;rand&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;100&lt;/span&gt;&lt;span class="p"&gt;)]}.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# See this article for the discussion of using frozen strings instead of symbols&lt;/span&gt;
&lt;span class="c1"&gt;# http://blog.arkency.com/could-we-drop-symbols-from-ruby/&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;frozen_hash&lt;/span&gt;
  &lt;span class="no"&gt;FROZEN_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="o"&gt;|&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="nb"&gt;rand&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;100&lt;/span&gt;&lt;span class="p"&gt;)]}.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;


&lt;span class="no"&gt;SYMBOL_HASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;symbol_hash&lt;/span&gt;
&lt;span class="no"&gt;STRING_HASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;string_hash&lt;/span&gt;
&lt;span class="no"&gt;FROZEN_HASH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frozen_hash&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reading_symbol_hash&lt;/span&gt;
  &lt;span class="no"&gt;SYMBOL_HASH&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;SYMBOL_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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;def&lt;/span&gt; &lt;span class="nf"&gt;reading_string_hash&lt;/span&gt;
  &lt;span class="no"&gt;STRING_HASH&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;STRING_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&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;def&lt;/span&gt; &lt;span class="nf"&gt;reading_frozen_hash&lt;/span&gt;
  &lt;span class="no"&gt;FROZEN_HASH&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;FROZEN_KEYS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&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;ips&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Creating large Hash"&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;"Symbol Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;symbol_hash&lt;/span&gt; &lt;span class="p"&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;"String Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;string_hash&lt;/span&gt; &lt;span class="p"&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;"Frozen Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;frozen_hash&lt;/span&gt; &lt;span class="p"&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;compare!&lt;/span&gt;
&lt;span class="k"&gt;end&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;ips&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="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"Reading large Hash"&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;"Symbol Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;reading_symbol_hash&lt;/span&gt; &lt;span class="p"&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;"String Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;reading_string_hash&lt;/span&gt; &lt;span class="p"&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;"Frozen Keys"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;reading_frozen_hash&lt;/span&gt; &lt;span class="p"&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;compare!&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s see the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Creating&lt;/span&gt; &lt;span class="n"&gt;large&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
&lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;a5688e2a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;PRISM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arm64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;darwin25&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Warming&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="o"&gt;--------------------------------------&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;   &lt;span class="mf"&gt;816.000&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;   &lt;span class="mf"&gt;541.000&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;   &lt;span class="mf"&gt;723.000&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="no"&gt;Calculating&lt;/span&gt; &lt;span class="o"&gt;-------------------------------------&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;      &lt;span class="mf"&gt;7.759&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;8.5&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;128.88&lt;/span&gt; &lt;span class="err"&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;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;38.352&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.005736&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;      &lt;span class="mf"&gt;5.716&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;4.8&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;174.96&lt;/span&gt; &lt;span class="err"&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;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;28.673&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.030225&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;      &lt;span class="mf"&gt;7.411&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;8.3&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;134.93&lt;/span&gt; &lt;span class="err"&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;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;36.873&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.037710&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="no"&gt;Comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mf"&gt;7759.1&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;s&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mf"&gt;7411.3&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="mf"&gt;5715.6&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.36&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;  &lt;span class="n"&gt;slower&lt;/span&gt;

&lt;span class="no"&gt;Reading&lt;/span&gt; &lt;span class="n"&gt;large&lt;/span&gt; &lt;span class="no"&gt;Hash&lt;/span&gt;
&lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="mf"&gt;3.4&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2025&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt; &lt;span class="n"&gt;revision&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;a5688e2a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="no"&gt;PRISM&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;arm64&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;darwin25&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="no"&gt;Warming&lt;/span&gt; &lt;span class="n"&gt;up&lt;/span&gt; &lt;span class="o"&gt;--------------------------------------&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;     &lt;span class="mf"&gt;1.373&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;   &lt;span class="mf"&gt;930.582&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;     &lt;span class="mf"&gt;1.205&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="no"&gt;Calculating&lt;/span&gt; &lt;span class="o"&gt;-------------------------------------&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;     &lt;span class="mf"&gt;13.467&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;7.6&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;74.26&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;67.269&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.046055&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;     &lt;span class="mf"&gt;11.345&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;1.7&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;88.14&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;56.766&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.005134&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;     &lt;span class="mf"&gt;11.820&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;±&lt;/span&gt; &lt;span class="mf"&gt;8.8&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&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;s&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;84.60&lt;/span&gt; &lt;span class="n"&gt;ns&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;span class="o"&gt;-&lt;/span&gt;     &lt;span class="mf"&gt;59.025&lt;/span&gt;&lt;span class="no"&gt;M&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;   &lt;span class="mf"&gt;5.068781&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;

&lt;span class="no"&gt;Comparison&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="no"&gt;Symbol&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;13466641.7&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;s&lt;/span&gt;
         &lt;span class="no"&gt;Frozen&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;11819675.1&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;same&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="ss"&gt;ish: &lt;/span&gt;&lt;span class="n"&gt;difference&lt;/span&gt; &lt;span class="n"&gt;falls&lt;/span&gt; &lt;span class="n"&gt;within&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;
         &lt;span class="no"&gt;String&lt;/span&gt; &lt;span class="no"&gt;Keys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;11344986.8&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;s&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;1.19&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;  &lt;span class="n"&gt;slower&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So as we can see on a larger Hashes with with 1000's of keys, the difference become more apparent and being mindful of it can help improve speed, if you are using a lot of large hashes. Otherwise, keep using what works better for you app. If string keys make it easier, let's say you are importing them from an external file, don't go through the trouble of converting them to symbols, it's probably not worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But don't take my word for it. Just measure it.&lt;/strong&gt;&lt;/p&gt;







&lt;p&gt;&lt;em&gt;Originally published: October 27, 2017&lt;/em&gt; • &lt;em&gt;Last updated: November 01, 2025&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cross-published from my blog: &lt;a href="https://kinvoki.com/programming/ruby-string-vs-symbol-keys/" rel="noopener noreferrer"&gt;https://kinvoki.com/programming/ruby-string-vs-symbol-keys/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>performance</category>
      <category>benchmarks</category>
      <category>optimization</category>
    </item>
  </channel>
</rss>
