<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.bnjs.co/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.bnjs.co/" rel="alternate" type="text/html" /><updated>2026-07-29T03:42:35+00:00</updated><id>https://www.bnjs.co/feed.xml</id><title type="html">bnjs</title><subtitle>Blogs, translations, and notes.</subtitle><entry><title type="html">Domain Guide for QR Codes</title><link href="https://www.bnjs.co/2026/02/04/domain-guide-for-qr-codes/" rel="alternate" type="text/html" title="Domain Guide for QR Codes" /><published>2026-02-04T00:00:00+00:00</published><updated>2026-02-04T00:00:00+00:00</updated><id>https://www.bnjs.co/2026/02/04/domain-guide-for-qr-codes</id><content type="html" xml:base="https://www.bnjs.co/2026/02/04/domain-guide-for-qr-codes/"><![CDATA[<p>Choosing a short domain for QR codes lets you use smaller, simpler QR codes that are easier to print and scan. This guide is a reference for the exact domain name length that fits in different QR code types, versions, ECC levels, and encodings.</p>

<p>For example, suppose your printing use-case calls for a Level 1, ECC L standard QR code, and you want to use lowercase letters in the URL which means binary encoding. The shortest URL that can be represented by this type of QR code is a 10 character domain name, like <code class="language-plaintext highlighter-rouge">abcdefg.io</code> or <code class="language-plaintext highlighter-rouge">abcdef.com</code>, with no path. So the full encoded URL would be <code class="language-plaintext highlighter-rouge">http://abcdefg.io</code> or <code class="language-plaintext highlighter-rouge">http://abcdef.com</code>. Since there is no room for a path, one 10-char domain would allow only one QR code of this type. A path at the end of the URL (eg. <code class="language-plaintext highlighter-rouge">http://abcdefg.io/index</code>) would exceed the character limit for this type of QR code.</p>

<p>If you wanted the option of generating multiple QR codes with a domain name, you would need at most an 8-char domain name, like <code class="language-plaintext highlighter-rouge">abcde.io</code> or <code class="language-plaintext highlighter-rouge">abcd.com</code>. The full encoded URL would be like <code class="language-plaintext highlighter-rouge">http://abcde.io/a</code> or <code class="language-plaintext highlighter-rouge">http://abcd.com/a</code>. There are about 36 different paths that are possible with one character. A longer path would require a shorter domain to stay within the desire QR code’s limits.</p>

<p>The tables below show the longest URL possible for a given QR code specification.</p>

<nav class="toc" aria-label="Table of contents">
<strong>Contents</strong>
<ul>
<li><a href="#reference-tables-qr-code-capacity-by-version-and-ecc">Reference tables</a></li>
<li><a href="#standard-qr-code-version-1-limits">Standard QR V1 limits</a></li>
<li><a href="#standard-qr-code-version-1-with-2-char-tld-io-co">Standard QR V1, 2-char TLD (.io, .co)</a></li>
<li><a href="#standard-qr-code-version-1-with-3-char-tld-com-net">Standard QR V1, 3-char TLD (.com)</a></li>
<li><a href="#micro-qr-code-limits">Micro QR limits</a></li>
<li><a href="#micro-qr-version-1-with-2-char-tld-io-co">Micro QR, 2-char TLD</a></li>
<li><a href="#micro-qr-version-1-with-3-char-tld-com-net">Micro QR, 3-char TLD</a></li>
</ul>
</nav>

<h2 id="reference-tables-qr-code-capacity-by-version-and-ecc">Reference tables: QR code capacity by version and ECC</h2>

<h3 id="standard-qr-code-version-1-limits">Standard QR Code Version 1 Limits</h3>

<table>
  <thead>
    <tr>
      <th>QR VERSION</th>
      <th>MODULES</th>
      <th>ECC LEVEL</th>
      <th>ALPHANUMERIC CHARACTERS</th>
      <th>BINARY CHARACTERS</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Version 1</td>
      <td>21</td>
      <td>L</td>
      <td>25</td>
      <td>17</td>
    </tr>
    <tr>
      <td>Version 1</td>
      <td>21</td>
      <td>M</td>
      <td>20</td>
      <td>14</td>
    </tr>
    <tr>
      <td>Version 1</td>
      <td>21</td>
      <td>Q</td>
      <td>16</td>
      <td>11</td>
    </tr>
    <tr>
      <td>Version 1</td>
      <td>21</td>
      <td>H</td>
      <td>10</td>
      <td>7</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="standard-qr-code-version-1-with-2-char-tld-io-co">Standard QR Code Version 1 with 2-char TLD (.io, .co)</h3>

<p>Anything longer than the given URLs becomes a Version 2 QR code. Version 2 has more modules than Version 1, meaning either the overall QR code will be larger (takes more space to print), or the module size will be smaller (requires a higher print resolution to be scannable).</p>

<p>For alphanumeric encoding, the URL must be in uppercase. URL paths are case sensitive, so your server would need to properly handle the uppercase path.</p>

<table>
  <thead>
    <tr>
      <th>ECC LEVEL</th>
      <th>NAME LENGTH</th>
      <th>ALPHANUMERIC</th>
      <th>BINARY</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/TYRANNOSAURUS</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.io/horse</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/HIPPOPOTAMUS</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.io/lion</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/RATTLESNAKE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.io/cat</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO/CHIMPANZEE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcd.io/ox</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.IO/ARMADILLO</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcde.io/a</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.IO/ELEPHANT</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcdef.io/</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.IO/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcdefg.io</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.IO/MONKEY</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.IO/HORSE</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJ.IO/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>11</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJK.IO/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>12</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKL.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>13</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKLM.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>14</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKLMN.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>15</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKLMNO.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/ELEPHANT</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.io/ox</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.io/a</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/MONKEY</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.io/</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcd.io</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.IO/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.IO/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJ.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/LION</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.io</code></td>
    </tr>
    <tr>
      <td>Q</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>H</td>
      <td>1</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="standard-qr-code-version-1-with-3-char-tld-com-net">Standard QR Code Version 1 with 3-char TLD (.com, .net)</h3>

<table>
  <thead>
    <tr>
      <th>ECC LEVEL</th>
      <th>NAME LENGTH</th>
      <th>ALPHANUMERIC</th>
      <th>BINARY</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/HIPPOPOTAMUS</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.com/lion</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/RATTLESNAKE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.com/cat</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM/CHIMPANZEE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.com/ox</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.COM/ARMADILLO</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcd.com/a</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.COM/ELEPHANT</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcde.com/</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.COM/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcdef.com</code></td>
    </tr>
    <tr>
      <td>L</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.COM/MONKEY</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.COM/HORSE</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.COM/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJ.COM/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>11</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJK.COM/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>12</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKL.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>13</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKLM.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>L</td>
      <td>14</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJKLMN.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.com/a</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/MONKEY</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.com/</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.com</code></td>
    </tr>
    <tr>
      <td>M</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.COM/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.COM/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.COM/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>Q</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>H</td>
      <td>1</td>
      <td>-</td>
      <td>-</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="micro-qr-code-limits">Micro QR Code limits</h3>

<p>At the time of writing, Micro QR codes are not well-supported by smartphones. For example, the iOS camera app on iPhone/iPad will not scan them. You would need to download a 3rd party app to scan them.</p>

<table>
  <thead>
    <tr>
      <th>MICRO QR VERSION</th>
      <th>MODULES</th>
      <th>ECC LEVEL</th>
      <th>ALPHANUMERIC CHARACTERS</th>
      <th>BINARY CHARACTERS</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>M1</td>
      <td>11</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M2</td>
      <td>13</td>
      <td>L</td>
      <td>6</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M2</td>
      <td>13</td>
      <td>M</td>
      <td>5</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>15</td>
      <td>L</td>
      <td>14</td>
      <td>9</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>15</td>
      <td>M</td>
      <td>11</td>
      <td>7</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>17</td>
      <td>L</td>
      <td>21</td>
      <td>15</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>17</td>
      <td>M</td>
      <td>18</td>
      <td>13</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>17</td>
      <td>Q</td>
      <td>13</td>
      <td>9</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="micro-qr-version-1-with-2-char-tld-io-co">Micro QR Version 1 with 2-char TLD (.io, .co)</h3>

<p>At the time of writing, Micro QR codes are not well-supported by smartphones. For example, the iOS camera app on iPhone/iPad will not scan them. You would need to download a 3rd party app to scan them.</p>

<table>
  <thead>
    <tr>
      <th>MICRO QR VERSION</th>
      <th>ECC LEVEL</th>
      <th>NAME LENGTH</th>
      <th>ALPHANUMERIC</th>
      <th>BINARY</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>M2</td>
      <td>L</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M2</td>
      <td>M</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>M</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/ARMADILLO</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.io/cat</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/ELEPHANT</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.io/ox</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.io/a</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO/MONKEY</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcd.io/</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.IO/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcde.io</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.IO/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.IO/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJ.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>11</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJK.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/MONKEY</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.io/a</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.io/</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO/LION</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.io</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.IO/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.IO/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.IO</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>Q</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.IO/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>Q</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.IO/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>Q</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.IO</code></td>
      <td>-</td>
    </tr>
  </tbody>
</table>

<hr />

<h3 id="micro-qr-version-1-with-3-char-tld-com-net">Micro QR Version 1 with 3-char TLD (.com, .net)</h3>

<p>At the time of writing, Micro QR codes are not well-supported by smartphones. For example, the iOS camera app on iPhone/iPad will not scan them. You would need to download a 3rd party app to scan them.</p>

<table>
  <thead>
    <tr>
      <th>MICRO QR VERSION</th>
      <th>ECC LEVEL</th>
      <th>NAME LENGTH</th>
      <th>ALPHANUMERIC</th>
      <th>BINARY</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>M2</td>
      <td>L</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M2</td>
      <td>M</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M3</td>
      <td>M</td>
      <td>-</td>
      <td>-</td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/ELEPHANT</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.com/ox</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/GIRAFFE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.com/a</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM/MONKEY</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abc.com/</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.COM/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://abcd.com</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.COM/LION</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.COM/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.COM/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>8</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGH.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>9</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHI.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>L</td>
      <td>10</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFGHIJ.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/HORSE</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://a.com/</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM/LION</code></td>
      <td><code class="language-plaintext highlighter-rouge">http://ab.com</code></td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>3</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABC.COM/CAT</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>4</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCD.COM/OX</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>5</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDE.COM/A</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>6</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEF.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>M</td>
      <td>7</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://ABCDEFG.COM</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>Q</td>
      <td>1</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://A.COM/</code></td>
      <td>-</td>
    </tr>
    <tr>
      <td>M4</td>
      <td>Q</td>
      <td>2</td>
      <td><code class="language-plaintext highlighter-rouge">HTTP://AB.COM</code></td>
      <td>-</td>
    </tr>
  </tbody>
</table>]]></content><author><name></name></author><summary type="html"><![CDATA[QR code domain length reference: how short your URL must be for Version 1 and Micro QR codes with .io, .co, .com TLDs. Tables for ECC levels and alphanumeric vs binary encoding.]]></summary></entry><entry><title type="html">At-Cost Single Employee Service (ACSES)</title><link href="https://www.bnjs.co/2025/12/01/at-cost-single-employee-service/" rel="alternate" type="text/html" title="At-Cost Single Employee Service (ACSES)" /><published>2025-12-01T00:00:00+00:00</published><updated>2025-12-01T00:00:00+00:00</updated><id>https://www.bnjs.co/2025/12/01/at-cost-single-employee-service</id><content type="html" xml:base="https://www.bnjs.co/2025/12/01/at-cost-single-employee-service/"><![CDATA[<p><strong>UPDATE: I wasn’t looking forward enough. These services will run themselves.</strong></p>

<hr />

<p>There’s a meme circulating in tech circles: the one-person billion dollar company. AI has amplified individual productivity so dramatically that a single talented operator could build and run what used to require entire teams. It’s an exciting vision.</p>

<p>But what if that person doesn’t need a billion dollars?</p>

<h2 id="the-opportunity">The Opportunity</h2>

<p>Look at the current SaaS landscape. Companies charge considerable subscription fees for tools that are trivial to build and maintain. These companies made sense before AI — building a successful SaaS took a whole team, and paying $10s/mo or $100s/mo was worth not having to build your own. These companies don’t make sense now.</p>

<p>Today, a single talented generalist could run one of these companies alone. Maybe one of them will create that mythical billion-dollar solo venture. Or maybe they’ll be satisfied with something different: a generous salary, delivering maximum value to users, and a monopoly.</p>

<h2 id="what-is-acses">What is ACSES?</h2>

<p>Enter the At-Cost Single Employee Service, or ACSES (pronounced like “access”). The model is simple: accept a good salary, take no additional profits, and pass the savings to customers as an extremely low price.</p>

<p>It’s like Open Source Software, but instead of developing a free software package, you develop a SaaS product that costs money and pays a salary. You focus on building the best possible product for your customers and charge only what it costs to run — your salary plus infrastructure costs, divided by your user base.</p>

<h2 id="the-natural-moat">The Natural Moat</h2>

<p>A natural market consolidation effect emerges. The more customers an ACSES has, the lower the price drops. Customers are incentivized to promote the service, creating viral growth. To get the greatest value per dollar, the market consolidates on a reliable monopoly. By supporting the established ACSES you’ll pay the lowest price and the price is likely to decrease over time.</p>

<h2 id="its-not-just-about-price">It’s Not Just About Price</h2>

<p>But the advantage goes beyond pricing. We see many examples where the profit motive sacrifices product quality and user experience: confusing pricing tiers, gotchyas that make your bill spike, UIs AB tested to increase revenue through obfuscation, features artificially gated to justify an “enterprise” plan. The product becomes a vehicle for extraction rather than the best possible tool for the user.</p>

<p>An ACSES flips this dynamic. Without the pressure to maximize revenue, the operator can focus entirely on building the best possible product. No tricks or gotchyas aimed at moving the revenue needle. Just a straightforward tool that is easy to use, priced at what it costs to run.</p>

<h2 id="race-to-minimum-wage">Race To Minimum Wage?</h2>

<p>Won’t ACSES operators just compete on lowest salary? I don’t think so. I think the market will prefer an operator that is well-compensated, comfortable and happy to operate long-term. Financial incentive is the effective mechanism for motivating the highest quality service to the market – a non-profit model or Open Source Software-style volunteer model won’t cut it.</p>

<h2 id="its-not-ideological">It’s Not Ideological</h2>

<p>This isn’t about altruism or anti-capitalism. It’s about recognizing that some problems are now small enough for one person to solve alone. As AI begins to eliminate software engineering roles, a good salary with job security is an attractive prospect to a software engineer. And by reorienting a business’ focus from maximum profit to maximum quality, a great new array of products enter the market: maximum quality at minimum cost.</p>

<h2 id="first-steps">First Steps</h2>

<p>I launched <a href="http://www.acses.dev">acses.dev</a> to debut some ACSES offerings. This organization will aim to establish ACSES standards and best practices, and build a team of operators – the best and brightest – to bring ACSES to as many domains as there is demand. If you’re interested, visit <a href="http://www.acses.dev">acses.dev</a> to join the early access waitlist.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[UPDATE: I wasn’t looking forward enough. These services will run themselves.]]></summary></entry><entry><title type="html">Budget your motivational content consumption</title><link href="https://www.bnjs.co/2024/04/02/budget-your-motivational-content-consumption/" rel="alternate" type="text/html" title="Budget your motivational content consumption" /><published>2024-04-02T00:00:00+00:00</published><updated>2024-04-02T00:00:00+00:00</updated><id>https://www.bnjs.co/2024/04/02/budget-your-motivational-content-consumption</id><content type="html" xml:base="https://www.bnjs.co/2024/04/02/budget-your-motivational-content-consumption/"><![CDATA[<p>When it comes to consuming motivational content (books, videos, podcasts, movies, etc.), as with anything, it’s important to strike the optimal balance.</p>

<p>If you consume too little motivational content, you might not be motivated enough to get much done. But if you consume too much motivational content, you might not get anything done because you’ve been spending all your time getting motivated.</p>

<p>The solution is to budget your consumption. You should spend only as much time consuming motivational content as needed to stay motivated, and the rest of your time should be spent actually getting something done. For me that translates to a couple of hours per week listening to my favorite podcast, or reading an inspiring biography or whatever it may be.</p>

<p>The problem is that just feeling motivated by itself feels good. Just by feeling motivated, you feel like you’re in a better position than if you weren’t. That’s true in a way, but it’s not really true unless you actually use that motivation to do something beneficial.</p>

<p>Feeling motivated shouldn’t be the end, even though it feels good. Eventually you’ll become frustrated that you had all this motivation and didn’t really do anything with it. Motivation is a means to an end, that is getting something done that is actually beneficial to your life.</p>

<p>Stay motivated, but more importantly, be productive.</p>]]></content><author><name></name></author><summary type="html"><![CDATA[When it comes to consuming motivational content (books, videos, podcasts, movies, etc.), as with anything, it’s important to strike the optimal balance.]]></summary></entry><entry><title type="html">Link Docs - a simple solution to tab overload</title><link href="https://www.bnjs.co/2024/03/26/link-docs-a-simple-alternative-to-tab-overload/" rel="alternate" type="text/html" title="Link Docs - a simple solution to tab overload" /><published>2024-03-26T00:00:00+00:00</published><updated>2024-03-26T00:00:00+00:00</updated><id>https://www.bnjs.co/2024/03/26/link-docs-a-simple-alternative-to-tab-overload</id><content type="html" xml:base="https://www.bnjs.co/2024/03/26/link-docs-a-simple-alternative-to-tab-overload/"><![CDATA[<h2 id="the-problem-with-tabs">The problem with tabs</h2>

<p>Tab overload is when you end up with hundreds or thousands of open tabs because you don’t want to lose track of those pages. You remember more or less what’s where and the tab serves as a reminder about the page.</p>

<p>The problem is that tabs are the easiest way of keeping track of pages, but not a good way. Although browsers have released features meant to help you organize tabs, like tab groups, tabs are not well-suited to organizing large numbers of documents.</p>

<h2 id="bookmarks-the-would-be-solution">Bookmarks, the would-be solution</h2>

<p>Browser bookmarks are meant to solve the problem, but the UX is clunky and outdated. The web app replaced the native desktop app, and of all places the browser is where we except a web app – so there’s cognitive dissonance in using the native app bookmarks feature to oragnize your web pages and web apps.</p>

<p>These days bookmarks can also live on the cloud, but again we are accustomed to the conventions of the web, like pages and URLs.</p>

<h2 id="link-docs-a-simple-workaround">Link docs, a simple workaround</h2>

<p>A link doc is a Google doc containing a list of links, including links to other link docs.</p>

<p>I like to have three sections in my link docs: Pinned, Recent, and Archive. The Pinned section is for important links that will always be at the top. The Recent section is ordered by newest links first, serving as a recent history. The Archive section is for links that you probably don’t need anymore but may be useful to keep for long-term history.</p>

<p>You can have many link docs, but you should have one root link doc that links off to all others. As long as you keep your root link doc open or easy to pull up, you can close all other tabs and still quickly get back to any page you saved.</p>

<p>It takes a little bit of active upkeep, but frees you from tab overload without requiring you to lose all those pages.</p>

<hr />]]></content><author><name></name></author><summary type="html"><![CDATA[The problem with tabs]]></summary></entry><entry><title type="html">When 1.day is not an Object</title><link href="https://www.bnjs.co/2015/01/14/rails-date-class-durations-and-ruby-basicobject/" rel="alternate" type="text/html" title="When 1.day is not an Object" /><published>2015-01-14T00:00:00+00:00</published><updated>2015-01-14T00:00:00+00:00</updated><id>https://www.bnjs.co/2015/01/14/rails-date-class-durations-and-ruby-basicobject</id><content type="html" xml:base="https://www.bnjs.co/2015/01/14/rails-date-class-durations-and-ruby-basicobject/"><![CDATA[<style>
code { color: crimson; }
pre code { color: inherit; }
</style>

<p><em>FEATURED IN <a href="http://rubyweekly.com/issues/229" target="_blank">RUBY WEEKLY ISSUE #229</a></em></p>

<p>Once in a while working with Rails we encounter something that makes us scratch our heads. When this happens, I make a point to try to figure it out. There is a lot to be learned in the process.</p>

<p>In this adventure, <a href="https://twitter.com/davidstosik">David</a> stumbles upon some strange Rails behavior and together we investigate.</p>

<p><strong>UPDATE: David encounters some more fun with Rails dates and <code class="language-plaintext highlighter-rouge">Duration</code> in this follow-up post: <a href="https://davidstosik.me/rails/2015/02/19/rails-1month-variable-length/">Rails’ <code class="language-plaintext highlighter-rouge">1.month</code> has a variable length</a></strong></p>

<hr />

<h2 id="rails-41-date-class">Rails 4.1 Date class</h2>

<p>There is some strange behavior involving Rails <code class="language-plaintext highlighter-rouge">Date</code> class and durations like <code class="language-plaintext highlighter-rouge">1.day</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; Date.current + 1.day
=&gt; 2015-01-15

&gt; 1.day
=&gt; 86400

&gt; Date.current + 86400
=&gt; 2251-08-05
</code></pre></div></div>

<p>Adding <code class="language-plaintext highlighter-rouge">1.day</code> to the current date returns tomorrow’s date, as expected. <code class="language-plaintext highlighter-rouge">1.day</code> returns <code class="language-plaintext highlighter-rouge">86400</code> the number of seconds in a day. But adding <code class="language-plaintext highlighter-rouge">86400</code> to the current date returns the date 86,400 days from today. Huh?</p>

<p>They must be different classes?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; 1.day.class
=&gt; Fixnum

&gt; 86400
=&gt; Fixnum
</code></pre></div></div>

<p>No, they’re both <code class="language-plaintext highlighter-rouge">Fixnum</code>. How does <code class="language-plaintext highlighter-rouge">Date#+</code> know the difference?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rails/activesupport/lib/active_support/core_ext/date/calculations.rb
class Date
  ...
  def plus_with_duration(other) #:nodoc:
    if ActiveSupport::Duration === other
      other.since(self)
    else
      plus_without_duration(other)
    end
  end
  alias_method :plus_without_duration, :+
  alias_method :+, :plus_with_duration
  ...
end
</code></pre></div></div>

<p><a href="https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/core_ext/date/calculations.rb#L88">https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/core_ext/date/calculations.rb#L88</a></p>

<p>So Rails’ <code class="language-plaintext highlighter-rouge">Date#+</code> (<code class="language-plaintext highlighter-rouge">Date#plus_with_duration</code>) has special handling for <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code> instances, and everything else goes to default Ruby <code class="language-plaintext highlighter-rouge">Date#+</code>.</p>

<p>But <code class="language-plaintext highlighter-rouge">1.day</code> is a <code class="language-plaintext highlighter-rouge">Fixnum</code>, right?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; 1.day.class
=&gt; Fixnum

&gt; 1.day.is_a? ActiveSupport::Duration
=&gt; true
</code></pre></div></div>

<p>Apparently it’s also an <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code>.</p>

<hr />

<h2 id="activesupportduration">ActiveSupport::Duration</h2>

<p>Why is <code class="language-plaintext highlighter-rouge">1.day</code> a <code class="language-plaintext highlighter-rouge">Fixnum</code> as well as an <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code>? Does <code class="language-plaintext highlighter-rouge">Duration</code> inherit from <code class="language-plaintext highlighter-rouge">Fixnum</code>, or override <code class="language-plaintext highlighter-rouge">#class</code>?</p>

<p><a href="https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/duration.rb">https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/duration.rb</a></p>

<p>Neither. It inherits from <code class="language-plaintext highlighter-rouge">ProxyObject</code>. Hmm, this looks like something:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rails/activesupport/lib/active_support/duration.rb
module ActiveSupport
  ...
  class Duration &lt; ProxyObject
    attr_accessor :value, :parts

    def initialize(value, parts) #:nodoc:
      @value, @parts = value, parts
    end
    ...
    private

        def method_missing(method, *args, &amp;block) #:nodoc:
          value.send(method, *args, &amp;block)
        end
  end
end
</code></pre></div></div>

<p><a href="https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/duration.rb">https://github.com/rails/rails/blob/4-1-stable/activesupport/lib/active_support/duration.rb</a></p>

<p>If a method is missing, it calls that method on <code class="language-plaintext highlighter-rouge">@value</code>, which would be a <code class="language-plaintext highlighter-rouge">Fixnum</code>. That makes sense.</p>

<p>So with <code class="language-plaintext highlighter-rouge">1.day</code> (a <code class="language-plaintext highlighter-rouge">Duration</code>), <code class="language-plaintext highlighter-rouge">#class</code> is missing, <code class="language-plaintext highlighter-rouge">#class</code> is called on <code class="language-plaintext highlighter-rouge">@value</code> (<code class="language-plaintext highlighter-rouge">1</code>), and <code class="language-plaintext highlighter-rouge">1.class</code> returns <code class="language-plaintext highlighter-rouge">Fixnum</code>. That’s why <code class="language-plaintext highlighter-rouge">1.day.class</code> returns <code class="language-plaintext highlighter-rouge">Fixnum</code>.</p>

<p>But why would <code class="language-plaintext highlighter-rouge">#class</code> be missing on a <code class="language-plaintext highlighter-rouge">Duration</code>? All objects respond to <code class="language-plaintext highlighter-rouge">#class</code>. Isn’t <code class="language-plaintext highlighter-rouge">Duration</code> an <code class="language-plaintext highlighter-rouge">Object</code>?</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; 1.day.is_a?(Object)
=&gt; true
</code></pre></div></div>

<p>At first glance, yes, but upon closer inspection, not really…</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; ActiveSupport::Duration.superclass
=&gt; ActiveSupport::ProxyObject

&gt; ActiveSupport::ProxyObject.superclass
=&gt; BasicObject

&gt; BasicObject.superclass
=&gt; nil
</code></pre></div></div>

<p>Actually <code class="language-plaintext highlighter-rouge">Duration</code> does not inherit <code class="language-plaintext highlighter-rouge">Object</code>, <code class="language-plaintext highlighter-rouge">@value</code> does, and <code class="language-plaintext highlighter-rouge">Duration#is_a?</code> delegates to <code class="language-plaintext highlighter-rouge">@value.is_a?</code>.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code># rails/activesupport/lib/active_support/duration.rb
module ActiveSupport
  ...
  class Duration &lt; ProxyObject
    ...
    def is_a?(klass) #:nodoc:
      Duration == klass || value.is_a?(klass)
    end
    ...
  end
end
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code> inherits from <code class="language-plaintext highlighter-rouge">ActiveSupport::ProxyObject</code> which inherits from <code class="language-plaintext highlighter-rouge">BasicObject</code> which inherits from nothing. An <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code> is technically not an <code class="language-plaintext highlighter-rouge">Object</code>.</p>

<hr />

<h2 id="basicobject">BasicObject</h2>

<p>So what is a <code class="language-plaintext highlighter-rouge">BasicObject</code>?</p>

<blockquote>
  <p>“BasicObject is the parent class of all classes in Ruby. It’s an explicit blank class.”</p>
</blockquote>

<p><a href="http://ruby-doc.org/core-2.2.0/BasicObject.html">http://ruby-doc.org/core-2.2.0/BasicObject.html</a></p>

<p>It’s a blank class.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>&gt; BasicObject.new.class
NoMethodError: undefined method `class' for #&lt;BasicObject:0x007ff3927616b8&gt;
from (pry):40:in `&lt;main&gt;'
</code></pre></div></div>

<p>Indeed, it doesn’t even respond to <code class="language-plaintext highlighter-rouge">#class</code>. That’s why <code class="language-plaintext highlighter-rouge">Duration#class</code> hits <code class="language-plaintext highlighter-rouge">method_missing</code>.</p>

<p>The End.</p>

<hr />

<p><strong>NOTE</strong>: In Rails 4.2, <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code> does not inherit <code class="language-plaintext highlighter-rouge">ActiveSupport::ProxyObject</code> thus not a <code class="language-plaintext highlighter-rouge">BasicObject</code>, and <code class="language-plaintext highlighter-rouge">1.day.class</code> returns <code class="language-plaintext highlighter-rouge">ActiveSupport::Duration</code>.</p>

<p><em>Published January 14, 2015</em></p>]]></content><author><name></name></author><summary type="html"><![CDATA[]]></summary></entry></feed>