<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Joep Schuurkes (Posts about programming)</title><link>https://smallsheds.garden/</link><description></description><atom:link href="https://smallsheds.garden/categories/programming.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:site@joep.slmail.me"&gt;Joep Schuurkes&lt;/a&gt; 
&lt;a href="https://creativecommons.org/licenses/by-sa/4.0/" rel="nofollow" target="_blank"&gt;
&lt;img alt="Creative Commons BY-SA License" style="border-width:0;margin: 0px 0px 4px 0px;" src="https://licensebuttons.net/l/by-sa/4.0/80x15.png" /&gt;
&lt;/a&gt;
</copyright><lastBuildDate>Sat, 04 Jul 2026 11:03:00 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>How adding a flex-wrap almost spiraled out of control</title><link>https://smallsheds.garden/blog/2025/how-adding-a-flex-wrap-almost-spiraled-out-of-control/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How does a project get to be a year late?&lt;/em&gt;&lt;br&gt;
&lt;em&gt;...One day at a time.&lt;/em&gt;&lt;br&gt;
- The Mythical Man-Month (anniversary edition), Frederick P. Brooks Jr., p. 153&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Last week I found a minor bug in the &lt;a href="https://github.com/kiesraad/abacus"&gt;application&lt;/a&gt; my team is building. For every election committee, we have a page showing all sessions of that committee. These sessions are shown as a row of cards. Playing around with the number of sessions, I discovered that the row keeps on growing, resulting in a horizontal scroll bar:&lt;/p&gt;
&lt;p&gt;&lt;img alt="screenshot showing five committee session cards in a row, with the last one, all the way to the right, having only a small part of its left side visible" src="https://smallsheds.garden/images/2025/flex-wrap/cards-before.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2025/how-adding-a-flex-wrap-almost-spiraled-out-of-control/"&gt;Read more…&lt;/a&gt; (6 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>agile</category><category>bugs</category><category>programming</category><category>software development</category><guid>https://smallsheds.garden/blog/2025/how-adding-a-flex-wrap-almost-spiraled-out-of-control/</guid><pubDate>Sat, 23 Aug 2025 22:00:00 GMT</pubDate></item><item><title>Benchmarking counterstring implementations in TypeScript</title><link>https://smallsheds.garden/blog/2025/benchmarking-counterstring-implementations-in-typescript/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;Earlier this year I posted about how I &lt;a href="https://smallsheds.garden/blog/2025/using-fake-it-till-you-make-it-to-implement-counterstring/"&gt;implemented a counterstring function&lt;/a&gt; using "fake it till you make it". I also posted about &lt;a href="https://smallsheds.garden/blog/2025/comparing-counterstring-implementations-in-typescript/"&gt;different ways&lt;/a&gt; to implement counterstrings. In this post, I want to share how those different implementations compare performance-wise.&lt;/p&gt;
&lt;p&gt;To do this, I used both &lt;a href="https://github.com/tinylibs/tinybench"&gt;Tinybench&lt;/a&gt; and &lt;a href="https://vitest.dev/guide/features.html#benchmarking"&gt;vitest bench&lt;/a&gt; (which uses Tinybench). The results are basically the same, but their default output is slightly different.&lt;/p&gt;
&lt;h2&gt;The nine implementations&lt;/h2&gt;
&lt;p&gt;Before I present the results, I should describe the different implementations and how they differ from each other. The &lt;a href="https://codeberg.org/joeposaurus/counterstring/src/commit/04883b7bb2f3e99f7be81ffa58e4ac5f934d276b/src/alt-counterstrings.ts"&gt;actual code of each implementation&lt;/a&gt; is available on Codeberg. Here I'll only mention what makes each implementation interesting compared to the others:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2025/benchmarking-counterstring-implementations-in-typescript/"&gt;Read more…&lt;/a&gt; (8 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>counterstring</category><category>programming</category><guid>https://smallsheds.garden/blog/2025/benchmarking-counterstring-implementations-in-typescript/</guid><pubDate>Sat, 22 Feb 2025 23:00:00 GMT</pubDate></item><item><title>Comparing counterstring implementations in TypeScript</title><link>https://smallsheds.garden/blog/2025/comparing-counterstring-implementations-in-typescript/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;In my previous post &lt;a href="https://smallsheds.garden/blog/2025/using-fake-it-till-you-make-it-to-implement-counterstring/"&gt;"Using 'fake it till you make it' to implement counterstring"&lt;/a&gt; I mentioned the implementation I included there, wasn't my initial implementation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I did something less performant with reversing an array, because I had looked at PerlClips's source code. How that came about and what I learned from it, is for another blog post.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is that blog post.&lt;/p&gt;
&lt;p&gt;As a matter of fact, I currently have &lt;a href="https://codeberg.org/joeposaurus/counterstring/src/commit/04883b7bb2f3e99f7be81ffa58e4ac5f934d276b/src/alt-counterstrings.ts"&gt;9 different implementations&lt;/a&gt; of counterstring in TypeScript. Including two that are not mine: one is from &lt;a href="https://www.satisfice.com/download/perlclip"&gt;PerClip&lt;/a&gt; but translated to TypeScript by me, the other is &lt;a href="https://www.eviltester.com/blog/eviltester/chrome-extensions/2019-02-19-counterstring-snippets/#counterstring-generation-function"&gt;EvilTester's implementation&lt;/a&gt;. There are some interesting lessons to take, both from comparing the code of the different implementations, as from comparing the differences in performance. The &lt;a href="https://smallsheds.garden/blog/2025/benchmarking-counterstring-implementations-in-typescript/"&gt;performance-part of the comparison&lt;/a&gt; will have to wait for my next post, though.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2025/comparing-counterstring-implementations-in-typescript/"&gt;Read more…&lt;/a&gt; (10 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>counterstring</category><category>programming</category><guid>https://smallsheds.garden/blog/2025/comparing-counterstring-implementations-in-typescript/</guid><pubDate>Sat, 18 Jan 2025 23:00:00 GMT</pubDate></item><item><title>Using "fake it till you make it" to implement counterstring</title><link>https://smallsheds.garden/blog/2025/using-fake-it-till-you-make-it-to-implement-counterstring/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;Last week I implemented &lt;a href="https://www.satisfice.com/download/perlclip"&gt;PerlClip&lt;/a&gt;'s &lt;a href="https://www.satisfice.com/blog/archives/22"&gt;counterstring&lt;/a&gt; in TypeScript. A counterstring is a string that tells you how long it is. For example a counterstring with length 9 looks like this: &lt;code&gt;*3*5*7*9*&lt;/code&gt;. Each number tells you the position of the asterisk following the number. My main goal with this project is to learn more about front-end development.&lt;/p&gt;
&lt;p&gt;Before I could start doing any front-end stuff, however, I needed to write a function that correctly generates counterstrings. Since I approached it in a way that I really enjoyed, inspired by Llewellyn Falco &lt;a href="https://youtu.be/O1h9ho2G85Q?t=155"&gt;"Fake it till you make it"&lt;/a&gt;, I figured it would make a good first post about this project.&lt;/p&gt;
&lt;p&gt;The idea behind "fake it till you make it" is simple. Start with an implementation covering a single case ("fake it") and then pull it apart little-by-little until it becomes an actual program ("make it"). As Llewellyn explains in the video, the value of this technique is that it's a lot easier to start from a working example and proceed from there than it is to get complete requirements.&lt;/p&gt;
&lt;p&gt;I did approach counterstring from the opposite direction, though, as Llewellyn did with Fizzbuzz in the video. Llewellyn starts with FizzBuzz length 20, so a case covering all the logic. Then he refactors it using different techniques, such as separation and encapsulation. While I started with counterstring length 0, the most simple case, and then worked my way up to larger lengths.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2025/using-fake-it-till-you-make-it-to-implement-counterstring/"&gt;Read more…&lt;/a&gt; (4 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>counterstring</category><category>programming</category><category>small steps</category><guid>https://smallsheds.garden/blog/2025/using-fake-it-till-you-make-it-to-implement-counterstring/</guid><pubDate>Sat, 04 Jan 2025 23:00:00 GMT</pubDate></item><item><title>Your default response should be a safe one</title><link>https://smallsheds.garden/blog/2024/your-default-response-should-be-a-safe-one/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;In his training DVD &lt;em&gt;"&lt;a href="https://edgeworkbooks.com/dvd-ukemi-from-the-ground-up/"&gt;Ukemi from the Ground Up&lt;/a&gt;"&lt;/em&gt;, &lt;a href="https://edgeworkbooks.com/about-ellis-amdur/"&gt;Ellis Amdur&lt;/a&gt; explains how your default response should be a safe one. In the context of the video it's about what your action should be in response to an Aikido technique like &lt;em&gt;kote gaeshi&lt;/em&gt;: Do you jump and do a breakfall? Or do you roll? The breakfall is the safe option. The roll is the comfortable one, except for the times you should have done the breakfall. Then you break your wrist...&lt;/p&gt;
&lt;p&gt;Unfortunately, the choice between breakfall and roll is not up to you. &lt;em&gt;Kote gaeshi&lt;/em&gt; is a throw executed through a wrist lock&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://smallsheds.garden/blog/2024/your-default-response-should-be-a-safe-one/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; and it's up to the person applying the technique what kind of throw it will be. Either they gently apply the wrist lock, guide you to the ground, and you can roll. Or they apply the technique more dynamically and there's no time to roll. In that case you have to jump and turn over your arm to fall safely on your side/back. That's what's called a breakfall.&lt;/p&gt;
&lt;p&gt;As you can imagine, there's not always a lot of time to think and decide between roll and breakfall. And if there's no time to think, whatever your default response is, that's what your body will do. That's why your default response to &lt;em&gt;kote gaeshi&lt;/em&gt; should be the breakfall, the response that's safe in both circumstances. The worst case scenario is that you take a breakfall you didn't need to. While the alternative, defaulting to the role even when you should have done the breakfall, comes with significantly worse consequences.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2024/your-default-response-should-be-a-safe-one/"&gt;Read more…&lt;/a&gt; (4 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>communication</category><category>note-taking</category><category>programming</category><guid>https://smallsheds.garden/blog/2024/your-default-response-should-be-a-safe-one/</guid><pubDate>Sat, 28 Dec 2024 23:00:00 GMT</pubDate></item><item><title>Tackling test automation in a new language</title><link>https://smallsheds.garden/blog/2024/tackling-test-automation-in-a-new-language/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;While there's value in learning all the ins-and-outs of one particular language, its ecosystem and its testing libraries, I think there's also a lot of value in having experience in several. Or at least, in two. If you only know one, you don't really know what's essential and what's incidental to the one set of tools you know. You don't know from experience in what ways things could be different.&lt;/p&gt;
&lt;p&gt;Picking up a new language is not trivial though, especially if it's your second one. There will be a lot to learn. You will notice similarities between the new language and the one(s) you already know. Sometimes those similarities will help you, sometimes they will mislead you.&lt;/p&gt;
&lt;p&gt;Also, it's more than picking up a new language. There are also the testing libraries you will use
and the language's ecosystem (e.g. how to install those libraries&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://smallsheds.garden/blog/2024/tackling-test-automation-in-a-new-language/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; or how to set up a pre-commit hook with a linter). That's quite a package.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2024/tackling-test-automation-in-a-new-language/"&gt;Read more…&lt;/a&gt; (3 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>agile</category><category>communication</category><category>programming</category><category>test automation</category><guid>https://smallsheds.garden/blog/2024/tackling-test-automation-in-a-new-language/</guid><pubDate>Sat, 10 Feb 2024 23:00:00 GMT</pubDate></item><item><title>A lesson from every language I've used</title><link>https://smallsheds.garden/blog/2023/a-lesson-from-every-language-ive-used/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;Throughout the years I've used quite a few different languages - if you allow me to be somewhat generous with the word "use". One language I actually feel proficient in. Some languages I've only done coding exercises in. And one language I've only used for a total of maybe 10 minutes, but that did really help out a whole team.&lt;/p&gt;
&lt;p&gt;I do believe we should be that generous, both towards ourselves and to others. For an industry that likes to talk about imposter syndrome, we seem to enjoy saying &lt;em&gt;"You're not a real ... unless..."&lt;/em&gt; a bit too much.&lt;/p&gt;
&lt;p&gt;Listing these languages for myself made me wonder what I had learned from them. Did I just learn how to write some code in each of them? Or did I also pick up some more general lessons? Turns out I did.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/2023/a-lesson-from-every-language-ive-used/"&gt;Read more…&lt;/a&gt; (5 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>clojure</category><category>programming</category><category>python</category><guid>https://smallsheds.garden/blog/2023/a-lesson-from-every-language-ive-used/</guid><pubDate>Thu, 28 Dec 2023 23:00:00 GMT</pubDate></item><item><title>(clj 12) Core concepts of functional programming with Peg Thing</title><link>https://smallsheds.garden/blog/clojure/2023/clj-12-core-concepts-of-functional-programming-with-peg-thing/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;Chapter 5 of "&lt;a href="https://www.braveclojure.com/clojure-for-the-brave-and-true/"&gt;Clojure for the Brave and True&lt;/a&gt;" explores two core concepts of functional programming: pure functions and immutable data structures. It wraps up with walking you through the &lt;a href="https://github.com/flyingmachine/pegthing"&gt;code&lt;/a&gt;&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://smallsheds.garden/blog/clojure/2023/clj-12-core-concepts-of-functional-programming-with-peg-thing/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; of a game called &lt;a href="https://www.braveclojure.com/functional-programming/#Peg_Thing"&gt;"Peg Thing"&lt;/a&gt;&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="https://smallsheds.garden/blog/clojure/2023/clj-12-core-concepts-of-functional-programming-with-peg-thing/#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;, which uses &lt;em&gt;"everything you've learned so far: immutable data structures, lazy sequences, pure functions, recursion - everything!"&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;So what is this Peg Thing?&lt;/h2&gt;
&lt;p&gt;For a full description and the code of Peg Thing, you can follow the links in the paragraph above. In this post I'll only share what's relevant to describe what I learned about functional programming through Peg Thing.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/clojure/2023/clj-12-core-concepts-of-functional-programming-with-peg-thing/"&gt;Read more…&lt;/a&gt; (8 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>brave clojure</category><category>clojure</category><category>programming</category><guid>https://smallsheds.garden/blog/clojure/2023/clj-12-core-concepts-of-functional-programming-with-peg-thing/</guid><pubDate>Sun, 06 Aug 2023 13:13:36 GMT</pubDate></item><item><title>(clj 11) Moving on from some unsolved exercises</title><link>https://smallsheds.garden/blog/clojure/2022/clj-11-moving-on-from-some-unsolved-exercises/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;At the end of chapter 4 of &lt;a href="https://www.braveclojure.com/clojure-for-the-brave-and-true/"&gt;"Clojure for the Brave and True"&lt;/a&gt; there are &lt;a href="https://www.braveclojure.com/core-functions-in-depth/#Exercises"&gt;four exercises&lt;/a&gt;. This post is about me deciding to move on to chapter 5 after solving 1.5 of these exercises.&lt;/p&gt;
&lt;h2&gt;The challenge with these exercises&lt;/h2&gt;
&lt;p&gt;A good example to talk about the challenge with these exercises is the second one:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a function, append, which will append a new suspect to your list of suspects.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/clojure/2022/clj-11-moving-on-from-some-unsolved-exercises/"&gt;Read more…&lt;/a&gt; (2 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>brave clojure</category><category>clojure</category><category>programming</category><guid>https://smallsheds.garden/blog/clojure/2022/clj-11-moving-on-from-some-unsolved-exercises/</guid><pubDate>Thu, 29 Dec 2022 15:08:36 GMT</pubDate></item><item><title>(clj 10) The mapify function of (clj 9) revisited</title><link>https://smallsheds.garden/blog/clojure/2022/clj-10-the-mapify-function-of-clj-9-revisited/</link><dc:creator>Joep Schuurkes</dc:creator><description>&lt;div&gt;&lt;p&gt;In my &lt;a href="https://smallsheds.garden/blog/clojure/2022/clj-9-how-to-figure-out-what-a-function-does/"&gt;previous Clojure post&lt;/a&gt; I covered the code example at the end of Chapter 4 of &lt;a href="https://www.braveclojure.com/clojure-for-the-brave-and-true/"&gt;"Clojure for the Brave and True"&lt;/a&gt;. Or rather, I focused on a single function in the example, called &lt;code&gt;mapify&lt;/code&gt;, and described how I figured out what it does. At the &lt;a href="https://smallsheds.garden/blog/clojure/2022/clj-9-how-to-figure-out-what-a-function-does/#the-mapify-function"&gt;end of that post&lt;/a&gt; I shared my disappointment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Having done all this work (and writing) I can't help but feel a bit disappointed. In the end, the function does not seem to do a lot.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And I shared a Python version of that same function, claiming that it basically does the same thing&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="https://smallsheds.garden/blog/clojure/2022/clj-10-the-mapify-function-of-clj-9-revisited/#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;However, less than a week after publishing the post, I got a very kind email by someone named Martin. And &lt;em&gt;"in defence of clojure and to maybe reduce your disappointment"&lt;/em&gt; Martin pointed out the two versions are not as equivalent as I claimed, because the Clojure version is more general than the Python version. So I looked at the two version again and the way they are different turned out to be quite interesting - interesting enough to write a follow-up post.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://smallsheds.garden/blog/clojure/2022/clj-10-the-mapify-function-of-clj-9-revisited/"&gt;Read more…&lt;/a&gt; (4 min remaining to read)&lt;/p&gt;&lt;/div&gt;</description><category>brave clojure</category><category>clojure</category><category>programming</category><guid>https://smallsheds.garden/blog/clojure/2022/clj-10-the-mapify-function-of-clj-9-revisited/</guid><pubDate>Wed, 13 Jul 2022 19:02:36 GMT</pubDate></item></channel></rss>