<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Klimo.io</title><link>/</link><description>Recent content on Klimo.io</description><generator>Hugo</generator><language>en-us</language><copyright>Jan Klimo</copyright><lastBuildDate>Mon, 01 Dec 2025 18:38:20 +0700</lastBuildDate><atom:link href="/index.xml" rel="self" type="application/rss+xml"/><item><title>Migrating from Heroku Postgres to PlanetScale</title><link>/posts/heroku-planetscale-postgres-migration/</link><pubDate>Mon, 01 Dec 2025 00:00:00 +0000</pubDate><guid>/posts/heroku-planetscale-postgres-migration/</guid><description>&lt;p&gt;I recently migrated &lt;a href="https://apps.shopify.com/robin-pro-image-gallery"&gt;Robin PRO&lt;/a&gt; from Heroku Postgres to &lt;a href="https://planetscale.com/docs/postgres"&gt;PlanetScale Postgres&lt;/a&gt;. PlanetScale, known for their MySQL offering, launched managed Postgres support and I decided to give it a try. The migration turned out to be surprisingly smooth.&lt;/p&gt;
&lt;p&gt;There isn&amp;rsquo;t much content out there about migrating specifically from Heroku Postgres to PlanetScale, so I decided to write up my own process.&lt;/p&gt;
&lt;h2 id="exporting-from-heroku-postgres"&gt;Exporting from Heroku Postgres&lt;/h2&gt;
&lt;p&gt;First, you&amp;rsquo;ll need your Heroku database credentials. You can find these in your Heroku dashboard or by running:&lt;/p&gt;</description></item><item><title>Cloudflare Tunnel for Shopify App Development</title><link>/posts/cloudflare-tunnel-shopify-app-development/</link><pubDate>Sat, 11 Mar 2023 00:00:00 +0000</pubDate><guid>/posts/cloudflare-tunnel-shopify-app-development/</guid><description>&lt;p&gt;If you&amp;rsquo;ve worked on a Shopify app before, chances are you&amp;rsquo;ve used tunneling for local development.
Historically, Shopify has been &lt;a href="https://workshops.shopify.dev/workshops/getting-started-apps#0"&gt;recommending&lt;/a&gt; ngrok across
their docs and it&amp;rsquo;s still the go-to tool for most developers.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve been using paid version of ngrok mainly in order to configure custom URLs for my tunnels to
keep my configuration consistent. Using the following config, I&amp;rsquo;d route traffic to my web server and webpack
dev server.&lt;/p&gt;</description></item><item><title>Migrating acts_as_list to ranked-model</title><link>/posts/migrating-acts-as-list-ranked-model/</link><pubDate>Sun, 15 Jan 2023 00:00:00 +0000</pubDate><guid>/posts/migrating-acts-as-list-ranked-model/</guid><description>&lt;p&gt;I&amp;rsquo;ve been using &lt;a href="https://github.com/brendon/acts_as_list"&gt;&lt;code&gt;acts_as_list&lt;/code&gt;&lt;/a&gt; since the very first release of
my &lt;a href="https://apps.shopify.com/robin-pro-image-gallery"&gt;app&lt;/a&gt;. Lately I&amp;rsquo;ve noticed that it occasionally creates duplicate position values (a
position of a record in a sorted collection should be unique). Besides, the algorithm for reordering items is not exactly optimal.
For example, moving a record from the last position to the first will update every record in the list. Good reasons to migrate to
&lt;a href="https://github.com/brendon/ranked-model"&gt;&lt;code&gt;ranked-model&lt;/code&gt;&lt;/a&gt; which uses a more sophisticated
&lt;a href="https://github.com/brendon/ranked-model#internals"&gt;algorithm&lt;/a&gt; to minimize DB writes.&lt;/p&gt;</description></item><item><title>How to Embed Robin PRO Galleries</title><link>/posts/how-to-embed-robin-pro-galleries/</link><pubDate>Thu, 14 Jan 2021 00:00:00 +0000</pubDate><guid>/posts/how-to-embed-robin-pro-galleries/</guid><description>&lt;p&gt;You&amp;rsquo;ve created your first &lt;a href="https://apps.shopify.com/robin-pro-image-gallery"&gt;Robin PRO&lt;/a&gt;
gallery and uploaded some images. Great!&lt;/p&gt;
&lt;p&gt;This article will show you how to display it anywhere on your site. There are
two ways to do this and both of them are really simple.&lt;/p&gt;
&lt;h3 id="option-a-code-snippet"&gt;Option A: Code snippet&lt;/h3&gt;
&lt;p&gt;Don&amp;rsquo;t worry, there is absolutely no coding involved. All you need to do is:&lt;/p&gt;
&lt;h4 id="step-1"&gt;Step 1&lt;/h4&gt;
&lt;p&gt;Copy the code snippet.&lt;/p&gt;
&lt;p&gt;&lt;a class="app__thumbnail--link" alt="Copy Robin PRO gallery code snippet"
href="/posts/how-to-embed-robin-pro-galleries/copy_snippet.gif"&gt;
&lt;img src="/posts/how-to-embed-robin-pro-galleries/copy_snippet.gif" class="post-image"&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;h4 id="step-2"&gt;Step 2&lt;/h4&gt;
&lt;p&gt;Paste it into the &lt;strong&gt;HTML content&lt;/strong&gt; of your page.&lt;/p&gt;</description></item><item><title>Case Study: Double Your App's Throughput for Free With Jemalloc</title><link>/posts/case-study-double-your-throughput-jemalloc-heroku/</link><pubDate>Tue, 29 Dec 2020 00:00:00 +0000</pubDate><guid>/posts/case-study-double-your-throughput-jemalloc-heroku/</guid><description>&lt;p&gt;Many great things have been said about using &lt;code&gt;jemalloc&lt;/code&gt;, especially in the context
of memory-constrained environments, such as Heroku.&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;This is CodeTriage&amp;#39;s Sidekiq worker memory use with and without jemalloc. I&amp;#39;m really starting to wonder how much of Ruby&amp;#39;s memory problems are just caused by the allocator. &lt;a href="https://t.co/FD0fVbJCLt"&gt;pic.twitter.com/FD0fVbJCLt&lt;/a&gt;&lt;/p&gt;&amp;mdash; Nate Berkopec (@nateberkopec) &lt;a href="https://twitter.com/nateberkopec/status/936627901071466496?ref_src=twsrc%5Etfw"&gt;December 1, 2017&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src="https://platform.twitter.com/widgets.js" charset="utf-8"&gt;&lt;/script&gt;


&lt;p&gt;What &lt;code&gt;jemalloc&lt;/code&gt; is or how it works is outside the scope of this post (visit the
project&amp;rsquo;s &lt;a href="http://jemalloc.net/"&gt;homepage&lt;/a&gt; or read the
&lt;a href="https://people.freebsd.org/~jasone/jemalloc/bsdcan2006/jemalloc.pdf"&gt;research paper&lt;/a&gt;
if you&amp;rsquo;d like to dive in).&lt;/p&gt;</description></item><item><title>Robin PRO Image Gallery</title><link>/shopify-apps/robin-pro-image-gallery/</link><pubDate>Sat, 05 Oct 2019 00:00:00 +0000</pubDate><guid>/shopify-apps/robin-pro-image-gallery/</guid><description>&lt;p class="app__content--pitch"&gt;
 Robin PRO is the definitive image gallery app built for 2023. Fast,
 mobile-first, beautiful. A must-have for any shop owner who cares about
 details and pixel-perfect user experience. Create a great first impression
 that converts into sales!
&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
 &lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/WrZSOml1Si4?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"&gt;&lt;/iframe&gt;
 &lt;/div&gt;

&lt;p class="app__content--pitch"&gt;
 Ready to take your store to the next level?
&lt;/p&gt;</description></item><item><title>Seeding Images From Unsplash</title><link>/posts/unsplash-seeds/</link><pubDate>Mon, 30 Sep 2019 00:00:00 +0000</pubDate><guid>/posts/unsplash-seeds/</guid><description>&lt;p&gt;Recently we&amp;rsquo;ve been making a more conscious effort to improve the quality of our seed data.&lt;/p&gt;
&lt;p&gt;Firstly, to make it easier to onboard new team members. Secondly, to make the app ready for
&lt;a href="https://devcenter.heroku.com/articles/github-integration-review-apps"&gt;Review Apps&lt;/a&gt;. Certainly,
it&amp;rsquo;s a great thing to be able to just re-seed your database if your local experiments go awry 😅&lt;/p&gt;
&lt;p&gt;The kind of seeds I&amp;rsquo;ve seen over the years typically neglect images. They either use a minimal
placeholder, use a few nicer images that are repeated over and over and checked into source
control, or don&amp;rsquo;t do anything at all. How can we get this kind of awesomeness with almost
no effort?&lt;/p&gt;</description></item><item><title>What Is Your App's MRR?</title><link>/posts/what-is-your-apps-mrr/</link><pubDate>Tue, 17 Sep 2019 00:00:00 +0000</pubDate><guid>/posts/what-is-your-apps-mrr/</guid><description>&lt;p&gt;One of the most useful features I&amp;rsquo;d love to see Shopify add to the Partners portal is
a chart of your MRR (monthly recurring revenue).&lt;/p&gt;
&lt;p&gt;This answers a simple question of &lt;em&gt;What is the current value of all of my active recurring charges?&lt;/em&gt;
In other words, how much money will you receive in the &lt;em&gt;next&lt;/em&gt; 30 days?&lt;/p&gt;
&lt;p&gt;There is one problem with the &lt;em&gt;Earnings&lt;/em&gt; chart:&lt;/p&gt;
&lt;p&gt;&lt;img src="/posts/what-is-your-apps-mrr/earnings.png" alt="earnings"&gt;&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a &lt;em&gt;lagging&lt;/em&gt; indicator showing you the &lt;em&gt;last&lt;/em&gt; 30 days. Not very helpful if you need to know
if your marketing or sales efforts are moving the needle.&lt;/p&gt;</description></item><item><title>Automating AWS Workflows</title><link>/posts/automating-aws-workflows/</link><pubDate>Sat, 10 Aug 2019 00:00:00 +0000</pubDate><guid>/posts/automating-aws-workflows/</guid><description>&lt;blockquote&gt;
&lt;p&gt;This article shows you how to use AWS CLI programmatically to automate your deployment process. We&amp;rsquo;ll upload assets to S3 (specifying their ACL and caching behavior) and invalidate CloudFront cache. Starting with a local shell script, we conclude with plugging the script into our CircleCI workflow. It may be especially useful if you&amp;rsquo;re using Rails with &lt;code&gt;Webpacker&lt;/code&gt;, but no experience with Rails is required.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My latest Shopify app &lt;a href="https://apps.shopify.com/robin-pro-image-gallery"&gt;Robin PRO&lt;/a&gt; renders image galleries on customer stores. When the store loads, it fetches a remote script (&lt;code&gt;client.js&lt;/code&gt;) together with a stylesheet (&lt;code&gt;client.css&lt;/code&gt;), requests gallery data and renders the result.&lt;/p&gt;</description></item><item><title>Speeding Things Up With Ruby Inline</title><link>/posts/optimizing-performance-ruby-inline/</link><pubDate>Thu, 21 Feb 2019 13:26:15 +0700</pubDate><guid>/posts/optimizing-performance-ruby-inline/</guid><description>&lt;p&gt;One of the fun exercises in &lt;a href="https://www.railsspeed.com/"&gt;The Complete Guide to Rails Performance&lt;/a&gt; is to profile
the &lt;a href="https://github.com/petergoldstein/dalli"&gt;&lt;code&gt;dalli&lt;/code&gt;&lt;/a&gt; gem with &lt;a href="https://github.com/ruby-prof/ruby-prof"&gt;&lt;code&gt;ruby-prof&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After cloning the repository and running &lt;code&gt;ruby-prof test/benchmark_test.rb&lt;/code&gt;, we get the following summary:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; %self total self wait child calls name
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 13.56 9.181 9.181 0.000 0.000 193917 &amp;lt;Class::IO&amp;gt;#select
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 8.12 9.604 5.498 0.000 4.106 275009 Dalli::Ring#binary_search
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; 5.01 3.390 3.390 0.000 0.000 272536 Kgio::SocketMethods#kgio_write
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That sparked my curiosity. Why is &lt;code&gt;dalli&lt;/code&gt; spending so much time in &lt;code&gt;binary_search&lt;/code&gt; and can it be improved?&lt;/p&gt;</description></item><item><title/><link>/privacy-policy/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>/privacy-policy/</guid><description>&lt;h1 id="privacy-policy"&gt;Privacy Policy&lt;/h1&gt;
&lt;p&gt;Your privacy is important to us. It is our policy to respect your privacy regarding any information we may collect from you across any apps we own and operate.&lt;/p&gt;
&lt;p&gt;We only ask for personal information when we truly need it to provide a service to you. We collect it by fair and lawful means, with your knowledge and consent. We also let you know why we’re collecting it and how it will be used.&lt;/p&gt;</description></item></channel></rss>