<?xml version="1.0" encoding="UTF-8" ?>
  <rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>24letters</title>
        <link>https://24letters.com</link>
        <description>RSS feed for the posts on 24letters</description>
        <atom:link href="https://24letters.com/rss" rel="self" type="application/rss+xml" />
        <item>
          <title>How I think about my automations like a pyramid</title>
          <link>https://24letters.com/blog/automations-like-a-pyramid</link>
          <guid isPermaLink="true">https://24letters.com/blog/automations-like-a-pyramid</guid>
          
          <pubDate>Thu, 27 Aug 2026 11:57:14 GMT</pubDate>
          <content:encoded><![CDATA[<p class="whitespace-pre-wrap">LLM agents make it possible to automate so much more than before. The reason for this is that they add a level of flexibility possible that wasn&#x27;t possible before. The way I think about this is as a pyramid. When you want to automate a digital task, think about it in these levels:</p><ol start="1"><li> Prompt</li><li> Skill</li><li> Product</li></ol><figure><img alt loading="lazy" width="1444" height="944" decoding="async" data-nimg="1" class="h-auto rounded-lg" style="color:transparent" sizes="(max-width: 768px) 100vw, 656px" srcSet="https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=640&q=75 640w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=750&q=75 750w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=828&q=75 828w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=1080&q=75 1080w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=1200&q=75 1200w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=1920&q=75 1920w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=2048&q=75 2048w, https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=3840&q=75 3840w" src="https://24letters.com/_next/image?url=https%3A%2F%2Fshiitake.us-east.host.bsky.network%2Fxrpc%2Fcom.atproto.sync.getBlob%3Fdid%3Ddid%253Aplc%253Aucgyl53umtlpjplm5vugutbi%26cid%3Dbafkreicpvxl26amu2jaju2nlavdul52hlqwplz4tltducwa7yhjheemw44&w=3840&q=75"></figure><p class="whitespace-pre-wrap"><strong>First level: Prompt</strong></p><p class="whitespace-pre-wrap">When you first want to automate something, just ask the agent. Fire up Codex or Claude Code and ask it to do the thing you want. This is really useful for one-offs. For example, &quot;I have this folder of documents, give them all titles based on the contents&quot;. I use that prompt after I scan paper documents I received. iOS names these &quot;Scanned documents&quot; and prompting is faster than thinking of a name. And the name isn&#x27;t that important other than that it should be good enough to recognize what is in it. But this kind of document I probably only read once.</p><p class="whitespace-pre-wrap"><strong>Second level: Skill</strong></p><p class="whitespace-pre-wrap">You reach this level when you notice yourself writing the same prompt over and over again. A great example of this is <a href="https://github.com/mattpocock/skills/blob/main/skills/productivity/grilling/SKILL.md" target="_blank" rel="noopener noreferrer">Matt Pocock&#x27;s grilling skill</a>. Instead of asking &quot;Ask me questions about this&quot;, you invoke the skill with <code>/grill-me</code> and the result will be a little bit more rigorous and consistently formatted. You don&#x27;t have to use his skill, you can make your own with your own preferred format. The main idea here is that you don&#x27;t have to specify all your preferences every time.</p><p class="whitespace-pre-wrap"><strong>Third level: Product</strong></p><p class="whitespace-pre-wrap">The moment you have a consistent process that needs to be handled deterministically, this is the place to go. Build your own private-first product. This is the level where you get nice UIs, good visualizations and consistency. The product will always work the same way, assuming you spend some time hammering out the bugs. Some of my own examples are: <a href="https://checklister.app/" target="_blank" rel="noopener noreferrer">Checklister</a>, an app I made to really quickly create repeatable checklist templates. <a href="https://4daagse-tijden.24letters.com/" target="_blank" rel="noopener noreferrer">4Daagse tijden</a>, an app to see at what time to spectate the 4Daagse. And <a href="https://omniconverter.dev/" target="_blank" rel="noopener noreferrer">OmniConverter</a>, an app to quickly convert quantities. They are extremely personal products, but creating them is way cheaper than before LLMs.</p><p class="whitespace-pre-wrap"><strong>Bringing it together</strong></p><p class="whitespace-pre-wrap">I&#x27;ve seen a lot of people intuitively navigate these levels already. But I&#x27;ve found the mental model helpful to categorise and structure my own thinking of how to automate digital processes. May it help you too.</p><p class="whitespace-pre-wrap"></p>]]></content:encoded>
        </item>
<item>
          <title>How I use checklister</title>
          <link>https://24letters.com/blog/how-i-use-checklister</link>
          <guid isPermaLink="true">https://24letters.com/blog/how-i-use-checklister</guid>
          
          <pubDate>Thu, 29 Jan 2026 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I&#x27;ve just published <a target="_blank" rel="noopener noreferrer" href="https://checklister.app">checklister</a>, my app to create and execute repeatable checklists. It does nothing more, nothing less. I created it because I had my own need for it.</p>
<p>I used to handle these things in OmniFocus. When the script broke I used for creating new projects, I was in limbo for a while. Since AI agents it is much easier to create personal software. So I did that. I&#x27;ve been using this project for while, but wanted to polish it before publishing. That&#x27;s done now, and you can use it for your repeatable checklists!</p>
<p>I use it for two things: Packing lists and my weekly review checklist.</p>
<h2 id="packing-lists"><a href="https://24letters.com/blog/how-i-use-checklister#packing-lists" class="anchor"></a>Packing lists</h2>
<p>I go on different kinds of trips. These trips usually have shared items and unique items. One thing everyone always needs are toiletries, so that&#x27;s a shared item. That&#x27;s why I created a composable list feature: packs. With packs you make a smaller list that you can then use within the main checklist template.</p>
<p><img alt="Create pack" loading="lazy" width="656" height="298.5117565698479" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FCreate%2520pack.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FCreate%2520pack.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FCreate%2520pack.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>Packs look like this when editing a template:</p>
<p><img alt="Pack within list" loading="lazy" width="656" height="362.7812080536913" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FPack%2520within%2520list.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FPack%2520within%2520list.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FPack%2520within%2520list.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>When you create a checklist from this template, the items in the pack and other items in the template are combined into one coherent checklist. This makes for no-distractions checking off:</p>
<p><img alt="Checklist" loading="lazy" width="656" height="690.5263157894738" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FChecklist.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FChecklist.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FChecklist.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<h2 id="weekly-review"><a href="https://24letters.com/blog/how-i-use-checklister#weekly-review" class="anchor"></a>Weekly review</h2>
<p>This is a much simpler checklist that handles my weekly review. Some of the tasks on there are: looking back, looking to my goals, and planning my week.</p>
<p>The weekly review checklist is inspired by the books Getting Things Done and GRIP. The review keeps me anchored to my goals and allows me to do all the tasks I set for myself.</p>
<p>If you start using checklister too and get some use out of it, let me know!</p>]]></content:encoded>
        </item>
<item>
          <title>On the resonant computing manifesto</title>
          <link>https://24letters.com/blog/resonant-computing-manifesto</link>
          <guid isPermaLink="true">https://24letters.com/blog/resonant-computing-manifesto</guid>
          
          <pubDate>Fri, 12 Dec 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>The <a target="_blank" rel="noopener noreferrer" href="https://resonantcomputing.org">Resonant Computing Manifesto</a> lacks soul. The humanness it professes to want it itself actually lacks. When I read it I felt emptyness inside.</p>
<p>At first I only noticed superficial details. Why is it using Times New Roman? Why does the design feel lifeless? Why does the text feel corporate-y?</p>
<p>I started looking inward to see if the problem was me. So I decided I should look at a manifesto that I actually like, <a target="_blank" rel="noopener noreferrer" href="https://agilemanifesto.org">The Agile Manifesto</a>. And my first reaction was, I think I know where they go their inspiration from.</p>
<p>And I think what bothers me about the resonant computing manifesto is that it lacks taste. I don&#x27;t profess to be an amazing writer, but I also don&#x27;t write a high-in-the-sky manifesto that aims to change the world.</p>
<p>One thing I love about the agile manifesto is this quote:</p>
<blockquote>
<p>We are uncovering better ways of developing software by doing it and helping others do it.</p>
</blockquote>
<p>This is a statement of the now. A vision is only valid if you live it. The resonant computing manifesto doesn&#x27;t feel lived to me.</p>
<p>So I&#x27;m left with: Just do it. Instead of writing this manifesto, live it. A great example of a group of people that lives it are the people at 37signals <sup><a href="https://24letters.com/blog/resonant-computing-manifesto#user-content-fn-1" id="user-content-fnref-1" data-footnote-ref="true" aria-describedby="footnote-label">1</a></sup>. They just released <a target="_blank" rel="noopener noreferrer" href="https://www.fizzy.do">Fizzy</a> and it feels thorougly &quot;great human software&quot; to me.</p>
<p>So built something that people actually want and is great tech!</p>
<hr>
<section data-footnotes="true" class="footnotes"><h2 id="footnotes"><a href="https://24letters.com/blog/resonant-computing-manifesto#footnotes" class="anchor"></a>Footnotes</h2>
<ol>
<li id="user-content-fn-1">
<p>I do want to distance myself from some of the politics surrounding some of
the people. I disagree vehemently with some opinions. But oh do they make great
human software. So on that specific area, they are a huge inspiration. <a href="https://24letters.com/blog/resonant-computing-manifesto#user-content-fnref-1" data-footnote-backref aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p>
</li>
</ol>
</section>]]></content:encoded>
        </item>
<item>
          <title>The Power of Models</title>
          <link>https://24letters.com/blog/power-of-models</link>
          <guid isPermaLink="true">https://24letters.com/blog/power-of-models</guid>
          
          <pubDate>Thu, 04 Dec 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I used to be allergic to models. Driven by models such as MBTI that sometimes are like horoscopes, I developed a big skepticism toward models.</p>
<p>That started to change for me when I heard about the quote “Every model is wrong, but some are useful.” It didn’t land for me completely then. But the moment I realized every model is by necessity wrong is when it clicked.</p>
<p>The moment I realized the true weight of the first part: “Every model is wrong”. Even models that I hold in super high regard: The atomic models, the model for electricity (V, A and R), and even my own models for software.</p>
<p>They are all true on some level, but they are also all wrong. And that’s because of the other quote: “The map is not the territory”. Because if it were, it would be the territory by necessity. It can only be a map by omitting details. And that makes it wrong in some sense.</p>
<p>So thus, the next time you hear about models, think: “Every model is wrong, but some are useful.”</p>]]></content:encoded>
        </item>
<item>
          <title>WordPress has earned its security reputation</title>
          <link>https://24letters.com/blog/wordpress-deserves-its-security-reputation</link>
          <guid isPermaLink="true">https://24letters.com/blog/wordpress-deserves-its-security-reputation</guid>
          
          <pubDate>Thu, 26 Jun 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I read a post recently about the security reputation of WordPress. In it, the author argues that WordPress mostly has a security reputation problem, not an actual security problem. I disagree, I would argue there are actual problems with the way WordPress is built technically.</p>
<p>My argument for that is simple:</p>
<ol>
<li>Defaults matter</li>
<li>WordPress has insecure defaults.</li>
</ol>
<p>Let&#x27;s get into the details.</p>
<h2 id="wordpress-has-insecure-defaults"><a href="https://24letters.com/blog/wordpress-deserves-its-security-reputation#wordpress-has-insecure-defaults" class="anchor"></a>WordPress has insecure defaults</h2>
<p>When a new WordPress developer writes a theme or plugin, they might write the following:</p>
<pre class="not-prose"><code><pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF">h1</span><span style="color:#F97583">>&#x3C;?</span><span style="color:#79B8FF">php</span><span style="color:#79B8FF"> echo</span><span style="color:#E1E4E8"> $post_custom_title </span><span style="color:#F97583">?>&#x3C;/</span><span style="color:#79B8FF">h1</span><span style="color:#F97583">></span></span></code></pre>
<p>It&#x27;s a natural piece of code to write. I want to put this on the page, <code>echo</code> puts thing on the page, I write <code>echo</code>. And now you&#x27;ve made insecure code. Through no fault of your own, you just didn&#x27;t know of the existence of <code>esc_html</code>. The secure way to write the same code is this:</p>
<pre class="not-prose"><code><pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#F97583">&#x3C;</span><span style="color:#79B8FF">h1</span><span style="color:#F97583">>&#x3C;?</span><span style="color:#79B8FF">php</span><span style="color:#79B8FF"> echo</span><span style="color:#B392F0"> esc_html</span><span style="color:#E1E4E8">( $post_custom_title ) </span><span style="color:#F97583">?>&#x3C;/</span><span style="color:#79B8FF">h1</span><span style="color:#F97583">></span></span></code></pre>
<p>Even if you know what the secure version looks like, it is easy to still write the insecure version. It is like walking in a minefield, any misstep will result in an insecure code path.</p>
<h3 id="what-does-a-secure-default-look-like"><a href="https://24letters.com/blog/wordpress-deserves-its-security-reputation#what-does-a-secure-default-look-like" class="anchor"></a>What does a secure default look like?</h3>
<p>In <a target="_blank" rel="noopener noreferrer" href="https://react.dev">React</a> the naïve code looks like this:</p>
<pre class="not-prose"><code><pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">>{postCustomTitle}&#x3C;/</span><span style="color:#85E89D">h1</span><span style="color:#E1E4E8">></span></span></code></pre>
<p>That is secure by default. Because React escapes everything, any <code>&gt;</code>, <code>&quot;</code> in the content will become an HTML entity. The insecure version of the above example looks like this:</p>
<pre class="not-prose"><code><pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" tabindex="0"><code><span class="line"><span style="color:#E1E4E8">&#x3C;</span><span style="color:#85E89D">h1</span><span style="color:#B392F0"> dangerouslySetInnerHTML</span><span style="color:#F97583">=</span><span style="color:#E1E4E8">{{ __html: postCustomTitle }} /></span></span></code></pre>
<p>Everything about the above signals to the user that they <strong>should</strong> know what they are doing before endevour in this direction. <code>dangerouslySetInnerHTML</code> instantly signals that this is some kind of dangerous operation. Having to put <code>__html</code> as a key in a object, makes it hard to do accidently and the ergonomics of it are annoying. It is friction in the direction of doing the secure thing.</p>
<p>Another benefit of the signal is that the user is more likely to stumble on the educational materials on how to make <code>dangerouslySetInnerHTML</code> secure when you use it. The React docs have this paragraph at the top of the explanation for this prop:</p>
<blockquote>
<p>This is dangerous. As with the underlying DOM <a target="_blank" rel="noopener noreferrer" href="https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML">innerHTML</a> property, you must exercise extreme caution! Unless the markup is coming from a completely trusted source, it is trivial to introduce an <a target="_blank" rel="noopener noreferrer" href="https://en.wikipedia.org/wiki/Cross-site_scripting">XSS</a> vulnerability this way.</p>
</blockquote>
<p>It links out to the wikipedia article about XSS, so the developer has a chance to learn about this term and the dangers of it.</p>
<p>WordPress is full of these &quot;insecure by default&quot; paths. So it is no wonder that many plugins are insecure. Solving this by fixing security incidents is a game of whack-a-mole.</p>
<h2 id="defaults-matter"><a href="https://24letters.com/blog/wordpress-deserves-its-security-reputation#defaults-matter" class="anchor"></a>Defaults matter</h2>
<p>Everyone starts out as a beginner. Literally, when you are born, you know nothing about anything. Gradually you learn about the world. The same is true for new WordPress developers.</p>
<p>Time and time again when companies publish statistics about settings, it turns out 90-95% of people left the setting in the default.</p>
<p>Combined with the above, for WordPress I imagine it to be something like the following picture. Taking the hard path is walking the stairs. The easy path is more akin to sliding down a slide. And when you are at insecure code, usually the only way to get to secure code is by climbing the &quot;security incident&quot; ladder.</p>
<p><img alt="Sketched paths" loading="lazy" width="656" height="470.75083056478405" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FSketched%2520paths.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FSketched%2520paths.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FSketched%2520paths.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>For the more visually inclined people, I&#x27;ve asked an LLM to make an illustrated version.</p>
<p><img alt="ChatGPT version" loading="lazy" width="656" height="656" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FChatGPT%2520version.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FChatGPT%2520version.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FChatGPT%2520version.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<h2 id="what-to-do-about-it"><a href="https://24letters.com/blog/wordpress-deserves-its-security-reputation#what-to-do-about-it" class="anchor"></a>What to do about it?</h2>
<p>It&#x27;s hard to know what to do about this. The WordPress code is vast and it is hard to change defaults now. Implementing something like <a target="_blank" rel="noopener noreferrer" href="https://laravel.com/docs/12.x/blade">Blade</a> will alienate a ton of WordPress developers. What we shouldn&#x27;t do is imply it&#x27;s merely a reputational or perception problem. While the noise is likely overselling the security issues of WordPress, there are actually problems with WordPress&#x27;s security hygiene.</p>]]></content:encoded>
        </item>
<item>
          <title>Archived HTML5 RTS</title>
          <link>https://24letters.com/blog/archived-html5-rts</link>
          <guid isPermaLink="true">https://24letters.com/blog/archived-html5-rts</guid>
          
          <pubDate>Sat, 12 Apr 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>Back in 2010, in highschool, I worked on trying to make an RTS in the browser. Canvas was fairly new and I was super excited about the potential there. &quot;<a target="_blank" rel="noopener noreferrer" href="https://youtu.be/S5aJAaGZIvk?t=1124">How do you draw a diagonal line in the browser?</a>&quot;. Don&#x27;t skip out on the intro video for Google IO 2009, it was great, a celebration of the web.</p>
<p>This is an archived log of some development blogs I wrote for the project. Newest first.</p>
<h2 id="may-2-2010-html5-rts"><a href="https://24letters.com/blog/archived-html5-rts#may-2-2010-html5-rts" class="anchor"></a>May 2, 2010 - HTML5 RTS</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="http://www.youtube.com/watch?v=Ir4xZa__FEY">http://www.youtube.com/watch?v=Ir4xZa__FEY</a></p></small>
<p>This is a huge update in case you didn’t notice, the updates in a list:</p>
<p>Canvas goes full-browser, and when the browser goes full screen the canvas goes full screen.
A background, a scrollable map.
A minimap, easy to make. Nice to have.
Performance, performance, performance. Wow, I had some redundant code, can’t believe it.
If you have suggestions for me, add them in the comments of the YouTube video.</p>
<h2 id="march-21-2010-html5-rts"><a href="https://24letters.com/blog/archived-html5-rts#march-21-2010-html5-rts" class="anchor"></a>March 21, 2010 - HTML5 RTS</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="http://www.youtube.com/watch?v=b3m2-gF3xwI">http://www.youtube.com/watch?v=b3m2-gF3xwI</a></p></small>
<p>I’m so happy, I figured out the problem that I had with the maths I did and now my formation system works. Yay.</p>
<h2 id="march-16-2010-html5-test-314-pi-day-turnings"><a href="https://24letters.com/blog/archived-html5-rts#march-16-2010-html5-test-314-pi-day-turnings" class="anchor"></a>March 16, 2010 - HTML5 test 3.14 (PI day) Turnings</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="http://www.youtube.com/watch?v=5OXKUWwKhBE">http://www.youtube.com/watch?v=5OXKUWwKhBE</a></p></small>
<p>Been busy again, a task system is in place. I could do path finding, which is still above my head but it would be easy to make once I have an algorithm to calculate the path.</p>
<p>But now I use the task system to make the units turn and then move and then turn again.</p>
<h2 id="march-14-2010-html5-rts-test-infantry"><a href="https://24letters.com/blog/archived-html5-rts#march-14-2010-html5-rts-test-infantry" class="anchor"></a>March 14, 2010 - HTML5 RTS test infantry</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="https://www.youtube.com/watch?v=IaX_S1wmAUo">https://www.youtube.com/watch?v=IaX_S1wmAUo</a></p></small>
<p>I have tested the extension of my GameObject object by making Infantry, now GameObject has a tank sprite but that will be a white box.</p>
<p>I have also added the basics of an arrow for the flag which will define what direction the units will stand.</p>
<h2 id="march-13-2010-html-rts-test-3"><a href="https://24letters.com/blog/archived-html5-rts#march-13-2010-html-rts-test-3" class="anchor"></a>March 13, 2010 - HTML RTS test 3</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="http://www.youtube.com/watch?v=cI6gCfJ0-VA">http://www.youtube.com/watch?v=cI6gCfJ0-VA</a></p></small>
<p>I added sprites now, NIIICEE</p>
<h2 id="march-13-2010-html-rts-test-2"><a href="https://24letters.com/blog/archived-html5-rts#march-13-2010-html-rts-test-2" class="anchor"></a>March 13, 2010 - HTML RTS test 2</h2>
<p>I proceeded on the idea of the HTML5 and JavaScript RTS. I am surprised how clean the code is.</p>
<p>I had to think about my math skillz before I could make the objects turn and stuff but now I did it. I should add collision of sorts.</p>
<h2 id="march-13-2010-html5-rts-test-1"><a href="https://24letters.com/blog/archived-html5-rts#march-13-2010-html5-rts-test-1" class="anchor"></a>March 13, 2010 - HTML5 RTS test 1</h2>
<small><p><a target="_blank" rel="noopener noreferrer" href="http://www.youtube.com/watch?v=8hZgS5E4KvU">http://www.youtube.com/watch?v=8hZgS5E4KvU</a></p></small>]]></content:encoded>
        </item>
<item>
          <title>How to create a period selector, a comparison</title>
          <link>https://24letters.com/blog/how-to-create-a-period-selector-a-comparison</link>
          <guid isPermaLink="true">https://24letters.com/blog/how-to-create-a-period-selector-a-comparison</guid>
          
          <pubDate>Sat, 04 Jan 2025 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>As software builders we have tremendous choice in how to present our user inputs. I recently was talking about how I think Moneybird is a 9-9.5 as a product and Exact Online is a 6. <a target="_blank" rel="noopener noreferrer" href="https://better-software.ghost.io/exact-online-back-button/">I&#x27;ve written before about Exact breaking the browser back button</a>. One of the UX examples I cited was the period selector. Let&#x27;s start with showing you the Exact Online one:</p>
<p><img alt="Exact Year Selector" loading="lazy" width="656" height="296.49717514124296" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Year%2520Selector.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Year%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Year%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p><img alt="Exact Online Period Selector" loading="lazy" width="656" height="310.831955922865" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Period%2520Selector.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Period%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Period%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p><img alt="Exact Online Month Selector" loading="lazy" width="656" height="367.43169398907105" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Month%2520Selector.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Month%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Month%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p><img alt="Exact Online Quarter Selector" loading="lazy" width="656" height="430.6703601108033" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Quarter%2520Selector.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Quarter%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FExact%2520Online%2520Quarter%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>It is super basic. You click the period and a dropdown opens. At least the dropdown only closes when you click somewhere else on the page.</p>
<p>If I had no point of comparison, I would say this is ok. I don&#x27;t regularly think about period selectors, so one could assume this is the best software has to offer. Having said that, the problems start when you want to select multiple quarters. A basic bug is that the dropdown doesn&#x27;t show the year when it is closed. More fundamentally, I have to consciously think fairly intensely about which quarters I want. Then I realize that I need to put in 1-4 in the text fields. I first tried to write &quot;Q3&quot; and &quot;Q4&quot;, but good thing is that the fields are number fields.</p>
<p>Let&#x27;s compare this to how Moneybird does this same task:</p>
<p><img alt="Moneybird Period Selector" loading="lazy" width="656" height="209.7824377457405" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>It is instantly clear to me that the current selection is Q1 of 2025. Because of the visual nature of the timeline I don&#x27;t even have to think about which months are contained in a quarter. While using this selector, I have never had confusion about the period I have selected. It is always clear. The one thing I had to learn is how to change the years that are displayed. The green arrows next to year change the year, which was non-obvious to me when I first learned this selector:</p>
<p><img alt="Moneybird Period Selector 2" loading="lazy" width="656" height="208.56992084432719" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector%25202.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FMoneybird%2520Period%2520Selector%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>The advantage of this setup is that it also makes arbitrary ranges possible.</p>
<p>Neither control offers support for arbitrary dates. This is expected, because in bookkeeping software reports you really never need arbitrary dates. Nearly all reporting is done on either a monthly, quarterly or yearly basis.</p>
<p>The one drawback I can immediately see of the Moneybird period selector is accessibility. It&#x27;s exceedingly likely that that one is harder to make accessible. If it even is accessible. I would love to know whether it is accessible, or how an accessible period selector would work.</p>]]></content:encoded>
        </item>
<item>
          <title>Context Unaware Notifications</title>
          <link>https://24letters.com/blog/context-unaware-notifications</link>
          <guid isPermaLink="true">https://24letters.com/blog/context-unaware-notifications</guid>
          
          <pubDate>Wed, 18 Dec 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I got the following notification: &quot;<strong>🚨 DAILY DEAL: FREE HOTDOG Because you&#x27;re a member, valid today!</strong>&quot; Now, in general I am not a fan of random notifications to sell me stuff. Even though it&#x27;s great for the bottom line of the company, it&#x27;s horrible as a user experience.</p>
<p>I have a bigger issue with this notification though. I&#x27;m a vegetarian, so I will never be interested in a hotdog. As a food company, it shouldn&#x27;t be too hard to glean my dietary preferences, given the amount of total lifetime orders I have.</p>
<p><img alt="NYP Notification" loading="lazy" width="368" height="351" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FNYP%2520Notification.PNG&w=384&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FNYP%2520Notification.PNG&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FNYP%2520Notification.PNG&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>]]></content:encoded>
        </item>
<item>
          <title>Better Defaults</title>
          <link>https://24letters.com/blog/better-defaults</link>
          <guid isPermaLink="true">https://24letters.com/blog/better-defaults</guid>
          
          <pubDate>Wed, 11 Dec 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I&#x27;ve just been annoyed for the umpteenth time by Google Calendar&#x27;s default to Sunday as the start of the week. When I started looking at the settings, I noticed it got the time &amp; date format wrong too:</p>
<p><img alt="Google Calendar Defaults" loading="lazy" width="384" height="288" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FGoogle%2520Calendar%2520Defaults.png&w=384&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FGoogle%2520Calendar%2520Defaults.png&w=828&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FGoogle%2520Calendar%2520Defaults.png&w=828&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>The vast majority of Dutch people use: day/month/year, 24:00 notation and Monday as the start of their week. It already knows I am in the Netherlands. How hard is it to default to these settings?</p>]]></content:encoded>
        </item>
<item>
          <title>Podimo hates iOS apparently</title>
          <link>https://24letters.com/blog/podimo-hates-ios-apparently</link>
          <guid isPermaLink="true">https://24letters.com/blog/podimo-hates-ios-apparently</guid>
          
          <pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>When you walk around with your AirPods, Siri will announce your incoming messages. It&#x27;s super useful, as I have a very strict notification policy. I curate my notifications to prevent unimportant ones. So every message that&#x27;s announced is important.</p>
<p>Podimo has made the baffling choice to keep playing your Podcast while the announcement is happening. Allowing you to understand exactly zero of the two sounds that are being pumped in your ear.</p>]]></content:encoded>
        </item>
<item>
          <title>Structure vs no-structure</title>
          <link>https://24letters.com/blog/structure-vs-no-structure</link>
          <guid isPermaLink="true">https://24letters.com/blog/structure-vs-no-structure</guid>
          
          <pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I&#x27;m trying to use Cursor, as it is supposed to be awesome. Recently I had a simple request: Find the version of TypeScript I was using. So I opened the VSCode settings:
<img alt="Cursor Settings" loading="lazy" width="656" height="406.064" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>And searched for TypeScript:
<img alt="Cursor Settings 2" loading="lazy" width="656" height="384.744" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings%25202.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FCursor%2520Settings%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>Every time I try to do anything in the VSCode settings I become disoriented. The sheer amount of settings overwhelms me every time. Contrast this to the structure in the PyCharm (which includes WebStorm) settings:
<img alt="WebStorm Settings" loading="lazy" width="656" height="496.4972577696526" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>As an extra bonus, it highlights the word that you searched. Making it super obvious why this is a result for the query:
<img alt="WebStorm Settings 2" loading="lazy" width="656" height="496.4972577696526" decoding="async" data-nimg="1" class="rounded-lg" style="color:transparent" srcSet="https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings%25202.png&w=750&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 1x, https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW 2x" src="https://24letters.com/_next/image?url=%2Fimages%2FWebStorm%2520Settings%25202.png&w=1920&q=75&dpl=dpl_AkG61yuLNG3R4hteDcvQtxR1ComW"></p>
<p>I would love to use Cursor as a daily driver, but so far I&#x27;ve been struggling with the fact that it is VSCode in the end.</p>]]></content:encoded>
        </item>
<item>
          <title>Exact Online Back button</title>
          <link>https://24letters.com/blog/exact-online-back-button</link>
          <guid isPermaLink="true">https://24letters.com/blog/exact-online-back-button</guid>
          
          <pubDate>Wed, 04 Dec 2024 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>In Exact Online, when you click the browser back button you do not end up at the screen you saw previously. This breaks a fundamental expectation of how browsers work. I regularly lose my place when trying to navigate within Exact Online.</p>]]></content:encoded>
        </item>
<item>
          <title>The software metaphor</title>
          <link>https://24letters.com/blog/the-software-metaphor</link>
          <guid isPermaLink="true">https://24letters.com/blog/the-software-metaphor</guid>
          <description>The software metaphor</description>
          <pubDate>Wed, 01 Dec 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>A good senior Software Engineer fullfills the roles of a writer, an engineer and a gardener. What are these metaphors and what do they speak of?</p>
<p>As a writer, they have to weigh every word, every sentence and every paragraph.</p>
<p>As an engineer, they have to consider the cost, the structure and the architecture.</p>
<p>And as a gardener, they need to consider the future prospects of the project, how the software grows and how much weed has grown over time.</p>
<p>I consider each of these perspectives equally valid metaphors for software. All have limitations, drawbacks and benefits. However, together they can speak loudly about how we should consider Software Development. So what are these metaphors and what do they speak to?</p>
<h2 id="the-writing-metaphor"><a href="https://24letters.com/blog/the-software-metaphor#the-writing-metaphor" class="anchor"></a>The writing metaphor</h2>
<p>Write code for the reader, not the writer. You could easily replace code with another word and the advice could equally apply to a distinguished author. But it goes further. Being a writer comes with inherent tradeoffs.</p>
<h3 id="software-engineers-are-authors"><a href="https://24letters.com/blog/the-software-metaphor#software-engineers-are-authors" class="anchor"></a>software engineers are authors</h3>
<p>Writing something elegant could deter from its accessibility. For example, consider the previous sentence. If you don’t know the words “elegant” or “accessibility”, it’s hard to read. So writing harder words makes the text harder to read.</p>
<p>The same goes for code: using an obscure language feature makes your code harder to read. However, jargon is good actually! It’s what DHH calls “Conceptual Compression”: Because you compress a complex system into one concept, it makes taking further steps simpler.</p>
<p>In that way, coding is like philosophy, which is mostly writing, which is mostly <a target="_blank" rel="noopener noreferrer" href="https://daringfireball.net/2014/07/two_memos">thinking clearly</a>. You are modelling the world. As a writer with words, as a coder with code.</p>
<h2 id="the-engineer-metaphor"><a href="https://24letters.com/blog/the-software-metaphor#the-engineer-metaphor" class="anchor"></a>The engineer metaphor</h2>
<p>Software engineers are engineers, that one is the easiest. <a target="_blank" rel="noopener noreferrer" href="https://mobile.twitter.com/danluu/status/1250241924725739520">I don’t have to make this point again, because other people make it very compellingly</a>.</p>
<h3 id="cost-vs-time-vs-quality"><a href="https://24letters.com/blog/the-software-metaphor#cost-vs-time-vs-quality" class="anchor"></a>Cost vs Time vs Quality</h3>
<p>The real of the standard engineering tradeoffs is: Costs vs Time vs Quality. We see this tradeoff in every engineering domain and software isn’t different. However, because software is so abstract to so many people, we can use this metaphor more often.</p>
<p>Instead of your issue becoming an abstract “programmer says no”, you can use the engineer metaphor to make a problem visceral. For example, I once used the description: “What you are asking me is to take a stone in the middle of the wall and put it on top.” The project manager in this case understood immediately.</p>
<p>The metaphor works equally as well when describing the “DIY vs professional”-tradeoff. DIY being do-it-yourself here. Nobody should object to a DIY shed in your garden. And everyone should object to a DIY skyscraper. The problem with some DIY sheds in software is that they turn into skyscrapers over time. If the underlying methodology doesn’t improve, you get very unstable buildings[1]. And as long as this isn’t acknowledged by leadership, you get very jaded engineers. Because those engineers can see the DIY mess for what it is.</p>
<h2 id="the-gardening-metaphor"><a href="https://24letters.com/blog/the-software-metaphor#the-gardening-metaphor" class="anchor"></a>The gardening metaphor</h2>
<p>I’ve been familiar with the meme “boyscout principle”. Boyscout principle means that you leave the software better than you found it. The gardening metaphor works on more levels. The one difference from a real garden and a software garden is this: if software isn’t worked on, the garden is frozen.</p>
<p>With this in mind, the gardening metaphor works as follows:</p>
<ul>
<li>Features of the garden are features in software.</li>
<li>Technical debt is like weeds in the garden.</li>
<li>Planned gardens will not work without maintenance, and it’s better to let them grow organically with a vision. (Agile)</li>
<li>Adding new features to a garden without ever weeding, will eventually make your garden a complete mess.</li>
<li>A small garden can do without signs, but a park needs maps. The same is true for software documentation.</li>
<li>The complexity grows exponentially with size and scope.</li>
<li>Beauty is in the eye of the beholder.</li>
<li>When you plant the first seed, you will never know what it will become.</li>
</ul>
<p>I think the garden metaphor is especially useful for explaining agile. Agile was a giant breakthrough and it’s where software is distinct from other engineering disciplines.</p>
<h2 id="conclusion"><a href="https://24letters.com/blog/the-software-metaphor#conclusion" class="anchor"></a>Conclusion</h2>
<p>These metaphors help you think and help explain software to a layperson. I believe we have a responsibility to make software accessible to everyone. And that begins by having language that bridges the gap between jargon and broader speech.</p>]]></content:encoded>
        </item>
<item>
          <title>How to create a Trix custom toolbar</title>
          <link>https://24letters.com/blog/how-to-create-a-trix-custom-toolbar</link>
          <guid isPermaLink="true">https://24letters.com/blog/how-to-create-a-trix-custom-toolbar</guid>
          <description>How to create a Trix custom toolbar</description>
          <pubDate>Wed, 05 May 2021 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<h2 id="trix"><a href="https://24letters.com/blog/how-to-create-a-trix-custom-toolbar#trix" class="anchor"></a>Trix</h2>
<p><a target="_blank" rel="noopener noreferrer" href="https://trix-editor.org">Trix is a WYSIWYG editor</a> that seems to use the same architectural model as <a target="_blank" rel="noopener noreferrer" href="https://draftjs.org">DraftJS</a>. Instead of working around quirks in <code>contenteditable</code> they both have an internal model of content and apply transformations on this content. I’m currently working on a project in Rails and wanted to use idiomatic tools, so decided to use Trix for my WYSIWYG needs.</p>
<p>The default styling of Trix didn’t fit the styles I wanted and I also wanted to hide certain formatting tools and maybe add some. The obvious solution is to hide buttons with CSS, but this didn’t feel correct. <a target="_blank" rel="noopener noreferrer" href="https://atimmer.com/foundational-ideas/">I like my software to have internal quality</a>. It also isn’t a solution for adding buttons. So what’s the solution instead?</p>
<h2 id="trix-apis"><a href="https://24letters.com/blog/how-to-create-a-trix-custom-toolbar#trix-apis" class="anchor"></a>Trix APIs</h2>
<p>When solving such a problem you are always limited by the APIs provided by the library you’re using. In this case after searching for it I found a <a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/issues/28#issuecomment-149342650">clarifying comment in the Trix issues</a>:</p>
<blockquote>
<p><a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/blob/master/test/src/test_helpers/fixtures/editor_with_toolbar_and_input.jst.eco#L3">Set the <code>toolbar</code> attribute</a> to your own element and <a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/blob/8dad233e008cf0f1612db4a6bb7e303dce396ada/src/trix/elements/trix_editor_element.coffee#L76-L79">Trix will use it rather than providing its own</a>: <code>&lt;trix-editor toolbar=&quot;toolbar-dom-id&quot;&gt;&lt;/trix-editor&gt;</code></p>
</blockquote>
<p>So I provided <code>toolbar</code> attribute and and copied the <a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/blob/8dad233e008cf0f1612db4a6bb7e303dce396ada/src/trix/config/toolbar.coffee">default toolbar HTML</a> to my toolbar container. If you wrap all this in a <code>trix-toolbar</code> element you even get the styles that trix has built-in. So that would look like this:</p>
<pre class="not-prose"><code>&lt;trix-toolbar id=&quot;trix-toolbar&quot;&gt;\[Default Trix Toolbar HTML\]&lt;/trix-toolbar&gt;
&lt;trix-editor input=&quot;input\_id&quot; toolbar=&quot;trix-toolbar&quot;&gt;&lt;/trix-editor&gt;

&lt;input type=&quot;hidden&quot; id=&quot;input\_id&quot; /&gt;
</code></pre>
<p>You can then go ahead and modify this HTML any way you would like. For my use case I’ve removed all buttons except “Bold”, “Italic” and “Link”.</p>
<h2 id="underline"><a href="https://24letters.com/blog/how-to-create-a-trix-custom-toolbar#underline" class="anchor"></a>Underline</h2>
<p>There are <a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/issues/31#issuecomment-166930690">good</a> <a target="_blank" rel="noopener noreferrer" href="https://github.com/basecamp/trix/pull/188#issuecomment-187817960">arguments</a> not to add an underline button. That’s why the Trix toolbar doesn’t come with an underline button. However, sometimes you have to do something which isn’t ideal. Adding an underline button is a two step process. The first step is registering a new text attribute:</p>
<pre class="not-prose"><code>import Trix from &quot;trix&quot;;

Trix.config.textAttributes.underline = {
style: {
textDecoration: &quot;underline&quot;,
},
inheritable: true,
parser: (element) =&gt; {
const style = window.getComputedStyle(element);

        return style.textDecoration === &quot;underline&quot; ||
            style.textDecorationLine === &quot;underline&quot;;
    }
}
</code></pre>
<p>This will let Trix know how the <code>underline</code> text attribute is going to work. Then, you can add a new button to your toolbar with <code>data-trix-attribute=&quot;underline&quot;</code> and it will work:</p>
<pre class="not-prose"><code>&lt;button type=&quot;button&quot; class=&quot;trix-button trix-button--icon trix-button--icon-underline&quot; data-trix-attribute=&quot;underline&quot; tabindex=&quot;-1&quot;&gt;
    &lt;span class=&quot;screen-reader-text&quot;&gt;&lt;%= t(&#x27;trix.buttons.underline&#x27;) %&gt;&lt;/span&gt;
&lt;/button&gt;
</code></pre>
<h2 id="concluding"><a href="https://24letters.com/blog/how-to-create-a-trix-custom-toolbar#concluding" class="anchor"></a>Concluding</h2>
<p>Trix is a nice, simple editor that’s easy to style and make your own.</p>]]></content:encoded>
        </item>
<item>
          <title>Cycle superhighways</title>
          <link>https://24letters.com/blog/cycle-superhighways</link>
          <guid isPermaLink="true">https://24letters.com/blog/cycle-superhighways</guid>
          
          <pubDate>Mon, 13 Nov 2017 00:00:00 GMT</pubDate>
          <content:encoded><![CDATA[<p>I’m in London. As a dutchie I love my bicycle and riding it. So what do you do? You ride a bike in London.</p>
<p>The software of the renting poles can be improved qute a bit. I tried to rent 2 bikes. One for me and one for my girlfriend. Everytime I tried that the software crashed on me. Luckily renting one bike worked. then I used <a target="_blank" rel="noopener noreferrer" href="https://itunes.apple.com/nl/app/santander-cycles/id974792287?l=en&mt=8">the app to rent another bike</a>.</p>
<p>So we went on our merry way. The pricing is very fair. You rent one bike for 24 hours. This costs £2. Every trip with it within 30 minutes is free. Any longer and you pay £2 per 30 minutes. So as good tourists we kept all our trips within 30 minutes and could ride our bikes for £4 all day.</p>
<p>London isn’t Amsterdam. It feels really dangerous to step on a bike here, but it is getting better. There are now these things called cycle superhighways. In the Netherlands these ‘highways’ would be called regular bike paths. And the highways end fairly randomly and then you have no idea where you are going. It feels too much like an expedition. Which is a good for a tourist, bad for a commuter.</p>
<p>But while the tourist sees all the streets for the first time. The commuter can explore all roads and plan a safe route. Then biking becomes a real option. As an alternative to the hot London underground. But the best thing is to combine those two. The cycle superhighways have good signage where underground stations are.</p>
<p>I think cycling in London is here to stay. I really look forward to visiting London again in a few years to see how everything had improved. In the Netherlands we are spoiled, and it is really exciting to see other countries following the lead.</p>]]></content:encoded>
        </item>
    </channel>
  </rss>