~mrlee/www.leemeichin.com

cac4c27c5e67e4f83933755d877a72fec6202d51 — Lee Meichin 13 days ago f0b9db4
Update footnote refs
M posts/a-damn-good-listen.org => posts/a-damn-good-listen.org +2 -2
@@ 6,7 6,7 @@
:CATEGORY: personal
:END:

A couple of years ago I enrolled in a coaching course with Coaching Development◊^[1]. It cost a fair whack and, even though I was unable to complete the course due to the severe decline in my mental health at the time, I still maintain that it's the best money I've ever spent, and what I learned has stuck with me and essentially guided me towards a different path through life that I would otherwise never have taken. The lack of credential means I can't practice as a coach, but the skills acquired are a great benefit to any communication driven profession, especially ones where conflict can easily brew up.
A couple of years ago I enrolled in a coaching course with Coaching Development[fn:1]. It cost a fair whack and, even though I was unable to complete the course due to the severe decline in my mental health at the time, I still maintain that it's the best money I've ever spent, and what I learned has stuck with me and essentially guided me towards a different path through life that I would otherwise never have taken. The lack of credential means I can't practice as a coach, but the skills acquired are a great benefit to any communication driven profession, especially ones where conflict can easily brew up.

Out of all of the things I picked up during those five months, one of a few quotes still stands out with me:



@@ 31,7 31,7 @@ To bring it back to now: the beauty of listening, the sheer magnificence of it, 
To wrap this up; sometimes the best, most enlightening conversations are the ones where you don't say anything.

◊footnotes{
  ◊^[1]{◊<>["https://coachingdevelopment.com/"]}
  [fn:1]{◊<>["https://coachingdevelopment.com/"]}
}



M posts/a-decade-of-work.org => posts/a-decade-of-work.org +7 -7
@@ 10,9 10,9 @@ I first 'got into' programming back in 2003, or 2004. I can't remember the exact

What also existed back then was the 'dotTk' domain, which allowed you to point all kinds of things to it, typically from weird free hosts like 20M. Of course, they would be ad-riddled too. This was before the time that even popup blockers were mainstream, and most of the ads were, in retrospect, quite goofy. You could 'spank the monkey' or fire darts and stuff in weird ad-based minigames. While you could easily get scammed or end up with a virus that dialled into a premium hotline using your landline, they feel a lot more innocent than the kind of misleading crap you get today, which is sometimes difficult to distinguish from genuine content.

But, I digress, I'm not here to talk about advertising, social media, and the slow descent into madness of the modern internet, I'm here to talk about what it's been like as a software developer (or engineer or whatever) over the past ten years of professional work with a good four or five on top as a newbie learning the ropes. This was the time when CSS was barely even standard, and building a website involved pirating a copy of Macromedia Dreamweaver (long since bought out by Adobe) and dragging layers of boxes around a window to get a layout you want. The HTML it would generate was truly awful and it was still common to do all of your layout with tables. It was generally easier to build for IE6 back then since the Mac didn't enjoy the ubiquity it now does, and Firefox was still in its early-ish days. Javascript wasn't really a thing back then, but DHTML◊^[1] was all the rage and was what allowed you to put flaming cursors or snowflakes all over your page.
But, I digress, I'm not here to talk about advertising, social media, and the slow descent into madness of the modern internet, I'm here to talk about what it's been like as a software developer (or engineer or whatever) over the past ten years of professional work with a good four or five on top as a newbie learning the ropes. This was the time when CSS was barely even standard, and building a website involved pirating a copy of Macromedia Dreamweaver (long since bought out by Adobe) and dragging layers of boxes around a window to get a layout you want. The HTML it would generate was truly awful and it was still common to do all of your layout with tables. It was generally easier to build for IE6 back then since the Mac didn't enjoy the ubiquity it now does, and Firefox was still in its early-ish days. Javascript wasn't really a thing back then, but DHTML[fn:1] was all the rage and was what allowed you to put flaming cursors or snowflakes all over your page.

I fondly remember that day when I dared to look at the generated HTML from my own example sites, and decided to learn how to write things by hand instead. It was all HTML4 back then, with XHTML on the horizon which promised a bit more strictness. Before then, I'd browse through Albino Blacksheep◊^[2] and it's collection of funny things that we now call 'memes', only back then they were a lot more creative and unique and only occasionally memetic, as in the case of Joel Veitch/rathergood.com and his being commissioned to do a TV ad in his style◊^[3].
I fondly remember that day when I dared to look at the generated HTML from my own example sites, and decided to learn how to write things by hand instead. It was all HTML4 back then, with XHTML on the horizon which promised a bit more strictness. Before then, I'd browse through Albino Blacksheep[fn:2] and it's collection of funny things that we now call 'memes', only back then they were a lot more creative and unique and only occasionally memetic, as in the case of Joel Veitch/rathergood.com and his being commissioned to do a TV ad in his style[fn:3].

Back on topic, before I drown in nostalgia. The reason I mention Albino Blacksheep is because I *still*, after all this time, remember one post the author made about how he built the site. He said he did it all in Notepad. *Notepad*! If you're not aware, Notepad is a barebones text editor on Windows, not unlike TextEdit on Mac. I was fascinated because, to my 15/16-year old mind, that seemed like an impossible feat.



@@ 24,7 24,7 @@ Before I even knew it I had 'PHP4 for dummies' and 'MySQL for dummies' on the de

That was literally my first line of dynamic code.

Skip ahead a few years, all the way to 2012 when I moved to London. I'd worked a PHP job full time before then but it was only at New Bamboo where I would find my form. They wrote everything in Ruby on Rails, and my experience in that was extremely minimal. Somehow I'd set up a redis server and had ruby communicating with it on my own hardware, but it didn't do much and I couldn't really figure out the code a few months after I wrote it. This required learning a lot of new things in terms of building application servers, and deploying code. Capistrano◊^[4] was the tool of choice in Ruby-land for deploying to a VPS and it was essentially a DSL over a bunch of shell scripts. In all honesty this DSL was great but were I not made to use it, I would be a lot more intimate with the power of SSH and tools like ~scp~, and understanding the issue with things like forwarding your SSH agent because you pull from a private git repo on your server.
Skip ahead a few years, all the way to 2012 when I moved to London. I'd worked a PHP job full time before then but it was only at New Bamboo where I would find my form. They wrote everything in Ruby on Rails, and my experience in that was extremely minimal. Somehow I'd set up a redis server and had ruby communicating with it on my own hardware, but it didn't do much and I couldn't really figure out the code a few months after I wrote it. This required learning a lot of new things in terms of building application servers, and deploying code. Capistrano[fn:4] was the tool of choice in Ruby-land for deploying to a VPS and it was essentially a DSL over a bunch of shell scripts. In all honesty this DSL was great but were I not made to use it, I would be a lot more intimate with the power of SSH and tools like ~scp~, and understanding the issue with things like forwarding your SSH agent because you pull from a private git repo on your server.

I won't talk much about the code, although my years at New Bamboo were truly formative. One thing has stuck with me since then though, over the 8 years since I was told it. My boss at the time saw I was struggling with managing the expectations of the client I was working with, and I was trying too hard to do things alone and hoping for the best instead of reaching out for the help that was readily available. I must have only been about 5 months into the job at that point. My boss took me into our boardroom, the table of which doubled up as a pingpong table, asked if I was alright, and then said something I've never forgotten since:



@@ 39,8 39,8 @@ Now it's 2020, and not much has changed except that I enjoy the programming side
The world has changed /a lot/ in all that time, and I now find myself in my thirties. Not once in my life did I ever think or believe I would be doing this kind of thing as a career, and I've grown to love it. If I were to tell my teenage self anything, knowing all of this... I would keep my mouth shut. He managed to figure it out just fine.

◊footnotes{
  ◊^[1]{◊<>["https://en.wikipedia.org/wiki/Dynamic_HTML"]}
  ◊^[2]{◊<>["https://www.albinoblacksheep.com/archive/"]}
  ◊^[3]{◊<>["https://www.youtube.com/watch?v=3AoNKGwBB74"]}
  ◊^[4]{◊<>["https://capistranorb.com/"]}
  [fn:1]{◊<>["https://en.wikipedia.org/wiki/Dynamic_HTML"]}
  [fn:2]{◊<>["https://www.albinoblacksheep.com/archive/"]}
  [fn:3]{◊<>["https://www.youtube.com/watch?v=3AoNKGwBB74"]}
  [fn:4]{◊<>["https://capistranorb.com/"]}
}
\ No newline at end of file

M posts/agile-lipstick.org => posts/agile-lipstick.org +9 -9
@@ 6,15 6,15 @@
:CATEGORY: programming
:END:

Over the past decade of my wondrous career I've rather haphazardly stumbled in and out of the realm of agile leadership. I've been a scrum master and an agile coach, even got the PSM1 certification which is nice but not really worth the PDF it's written on these days. My best investment was in a coaching course◊^[1] where I learned experientially all of the things I didn't learn as part of my job: active listening, rapport, acknowledgment and recognition, transactional anaylisis, I'm Ok/You're Ok, etc.
Over the past decade of my wondrous career I've rather haphazardly stumbled in and out of the realm of agile leadership. I've been a scrum master and an agile coach, even got the PSM1 certification which is nice but not really worth the PDF it's written on these days. My best investment was in a coaching course[fn:1] where I learned experientially all of the things I didn't learn as part of my job: active listening, rapport, acknowledgment and recognition, transactional anaylisis, I'm Ok/You're Ok, etc.

In the old-days of the internet, and the world wide web, there was a period of time known as Eternal September◊^[2]. Basically, the shadow-of-its-former-self that is AOL used to be an ISP that gave its browser away on free CDs. You'd find them _everywhere_. So many adverts at the time would give you an 'AOL keyword' to look for to get to their site, and there was an instant messenger and essentially a predecessor to the walled gardens you see so often today with Facebook, Google, Apple, and so on. They made Usenet accessible to the masses and so the existing Usenet users at the time had to basically deal with onboarding a constant influx of new users, many of whom didn't understand the rules or etiquette of the groups they found themselves joining.
In the old-days of the internet, and the world wide web, there was a period of time known as Eternal September[fn:2]. Basically, the shadow-of-its-former-self that is AOL used to be an ISP that gave its browser away on free CDs. You'd find them _everywhere_. So many adverts at the time would give you an 'AOL keyword' to look for to get to their site, and there was an instant messenger and essentially a predecessor to the walled gardens you see so often today with Facebook, Google, Apple, and so on. They made Usenet accessible to the masses and so the existing Usenet users at the time had to basically deal with onboarding a constant influx of new users, many of whom didn't understand the rules or etiquette of the groups they found themselves joining.

I think agile, or perhaps scrum more specifically, has something of an Eternal September itself. The crux of it is that 'agile development' has become increasingly popular, practically a buzzword, and therefore the discourse has never really evolved beyond the experience of that introduction. Either it works for you after an initial period and you stick with it, or you become one of the legion who writes it off. I think this is the same reason why books like Peopleware◊^[3] and The Five Dysfunctions of a Team◊^[4] still feel prophetic: forty years and the world has still not caught up with them.
I think agile, or perhaps scrum more specifically, has something of an Eternal September itself. The crux of it is that 'agile development' has become increasingly popular, practically a buzzword, and therefore the discourse has never really evolved beyond the experience of that introduction. Either it works for you after an initial period and you stick with it, or you become one of the legion who writes it off. I think this is the same reason why books like Peopleware[fn:3] and The Five Dysfunctions of a Team[fn:4] still feel prophetic: forty years and the world has still not caught up with them.

To be agile, in my mind, is to reject mediocrity and foster a mindset that encourages excellency; actually being excellent with each other and not just writing excellent software. This comes in many forms and the exact manifestation will change from one organisation to another, one culture to another, one team to another.

The subject of this topic, the Agile Manifesto◊^[5], was penned in 2001, twenty years ago, and has remained frozen in time since. Let's take a look in more detail.
The subject of this topic, the Agile Manifesto[fn:5], was penned in 2001, twenty years ago, and has remained frozen in time since. Let's take a look in more detail.

The first line of the manifesto is this:



@@ 53,9 53,9 @@ Fuck that noise, just put the agile lipstick on your pig instead! Throw in a dai
Now /that/ is easy.

◊footnotes{
  ◊^[1]{◊<>["https://www.coachingdevelopment.com/"] - if you're based in London or Ireland this is /so/ worth it./
  ◊^[2]{◊<>["https://en.wikipedia.org/wiki/Eternal_September"]}
  ◊^[3]{◊<>["https://uk.bookshop.org/a/6865/9780321934116"]}
  ◊^[4]{◊<>["https://uk.bookshop.org/a/6865/9781118127308"]}
  ◊^[5]{◊<>["https://agilemanifesto.org/"]}
  [fn:1]{◊<>["https://www.coachingdevelopment.com/"] - if you're based in London or Ireland this is /so/ worth it./
  [fn:2]{◊<>["https://en.wikipedia.org/wiki/Eternal_September"]}
  [fn:3]{◊<>["https://uk.bookshop.org/a/6865/9780321934116"]}
  [fn:4]{◊<>["https://uk.bookshop.org/a/6865/9781118127308"]}
  [fn:5]{◊<>["https://agilemanifesto.org/"]}
}

M posts/blogging-in-haskell.org => posts/blogging-in-haskell.org +8 -8
@@ 6,9 6,9 @@
:CATEGORY: programming
:END:

It's taken me quite a while to settle on a particular look and feel for this blog. Rather than just having an outlet for writing, I wanted the creation of it to be a learning experience too. Hugo◊^[1], Gatsby◊^[2] and Zola◊^[3], with Netlify CMS◊^[4] as a fancy interface for writing posts on top of it all. Each attempt left me feeling less inspired than the last.
It's taken me quite a while to settle on a particular look and feel for this blog. Rather than just having an outlet for writing, I wanted the creation of it to be a learning experience too. Hugo[fn:1], Gatsby[fn:2] and Zola[fn:3], with Netlify CMS[fn:4] as a fancy interface for writing posts on top of it all. Each attempt left me feeling less inspired than the last.

Eventually I stumbled across Hakyll◊^[5] and, after finding a CSS 'framework' that gave the appearance of a terminal UI◊^[6], I felt like I had enough to get things off the ground.
Eventually I stumbled across Hakyll[fn:5] and, after finding a CSS 'framework' that gave the appearance of a terminal UI[fn:6], I felt like I had enough to get things off the ground.

The major appeal so far has been the immense ease of customisation. Hakyll itself isn't a static site generator in the same sense that others are, and as a result it offers a layer of customisation that other generators generally defer to templating languages for.



@@ 61,10 61,10 @@ The result of adding this code, and then inserting it into the template context,
In any case, I've enjoyed playing around with Haskell in order to deploy this site, and I'm looking forward to seeing what else I can build with the language. It's truly fascinating.

◊footnotes{
  ◊^[1]{◊<>["https://gohugo.io"]}
  ◊^[2]{◊<>["https://www.gatsbyjs.org"]}
  ◊^[3]{◊<>["https://www.netlifycms.org"]}
  ◊^[4]{◊<>["https://jaspervdj.be/hakyll"]}
  ◊^[5]{◊<>["https://www.getzola.org"]}
  ◊^[6]{◊<>["https://terminalcss.xyz"]}
  [fn:1]{◊<>["https://gohugo.io"]}
  [fn:2]{◊<>["https://www.gatsbyjs.org"]}
  [fn:3]{◊<>["https://www.netlifycms.org"]}
  [fn:4]{◊<>["https://jaspervdj.be/hakyll"]}
  [fn:5]{◊<>["https://www.getzola.org"]}
  [fn:6]{◊<>["https://terminalcss.xyz"]}
}

M posts/can-you-crack-the-code.org => posts/can-you-crack-the-code.org +12 -12
@@ 40,7 40,7 @@ I presume you've seen this kind of puzzle before: there is a lock that requires 
If you're unaware of Prolog, it's a /logical progamming/ language that, in its most simplest terms, takes a bunch of facts and rules and then gives you the tools to query them to get the outcome you want. In more complicated terms, a cursory search on the intertubes will lead you to a vast collection of academic papers that explain more. This is not the kind of language that is casually blogged about by the masses, as with more mainstream ones like CSS, HTML, or ColdFusion.

◊q["Dennis Merritt" 2017]{
  Programming in Prolog is significantly different from conventional procedural programming and requires a readjustment in the way one things about programming. Logical relationships are asserted, and Prolog is used to determine whether or not certain statements are true, and if true, what variable bindings make them true. This leads to a very declarative style of programming.◊^[1]
  Programming in Prolog is significantly different from conventional procedural programming and requires a readjustment in the way one things about programming. Logical relationships are asserted, and Prolog is used to determine whether or not certain statements are true, and if true, what variable bindings make them true. This leads to a very declarative style of programming.[fn:1]
}

Mr Merritt is, to put it professionally, *god damn right*. Here's a valid Prolog program:


@@ 60,7 60,7 @@ Mr Merritt is, to put it professionally, *god damn right*. Here's a valid Prolog

What we have here are some facts, both true and technically true. It's a fact that Obama is a president, as is Trump. It's also a fact that there is a brand of cheese in the UK called President. This is quite ambiguous as a result so some extra facts are supplied, namely that brie is a cheese as much as it is a President-brand cheese, and that Wensleydale is also a cheese. It goes without saying that Trump and Obama are people, so with those facts we should be able to do some querying.

If you're doing this on your own machine, you can save those facts into a file (say, ~example.pl~) and then importing it inside a console, like so: ~[example].~. Otherwise, you can load up the Swish notebook◊^[2] and follow along using an online console, no installation needed!
If you're doing this on your own machine, you can save those facts into a file (say, ~example.pl~) and then importing it inside a console, like so: ~[example].~. Otherwise, you can load up the Swish notebook[fn:2] and follow along using an online console, no installation needed!

Let's do some querying then, which will show you how Prolog might seem a bit back to front compared to what you're used to.



@@ 125,7 125,7 @@ Here's the puzzle again, for reference:

-----

According to Leon Sterling and Ehud Shapiro in /The Art of Prolog/ ◊^[3], this type of problem falls quite neatly under the umbrella of non-deterministic programming. This is because we're essentially going to build an algorithm that will use what they describe as a ~generate and test~ solution. We're going to write something that will take our clues and run through all the possible answers until it lands on the only one that fits. We're not aiming for beautiful optimisation here so this good enough, although the code we write will be tightly coupled to the exact puzzle provided.
According to Leon Sterling and Ehud Shapiro in /The Art of Prolog/ [fn:3], this type of problem falls quite neatly under the umbrella of non-deterministic programming. This is because we're essentially going to build an algorithm that will use what they describe as a ~generate and test~ solution. We're going to write something that will take our clues and run through all the possible answers until it lands on the only one that fits. We're not aiming for beautiful optimisation here so this good enough, although the code we write will be tightly coupled to the exact puzzle provided.

So, let's begin with our set of rules:



@@ 140,7 140,7 @@ So, let's begin with our set of rules:
#+end_src

◊aside{
  If you're curious about the first `use_module` statement, beyond knowing that it makes things easier, check out the docs on /Constraint Logic Programming over Finite Domains/.◊^[4]
  If you're curious about the first `use_module` statement, beyond knowing that it makes things easier, check out the docs on /Constraint Logic Programming over Finite Domains/.[fn:4]
}

These clues don't really mean anything by themselves, they're simple facts in Prolog terms, so we need to add a bit more to give these some meaning. All of this will go into the same file, as we're not ready to query yet.


@@ 253,7 253,7 @@ The next bit is quite long, but this query is where we make the sausage. Comment
    Code = [A, B, C, D].
#+end_src

Did you solve the puzzle yourself? Do you remember the answer? If you don't care to copy and paste all of that, you can open up this ready made notebook◊^[5], and then run the following:
Did you solve the puzzle yourself? Do you remember the answer? If you don't care to copy and paste all of that, you can open up this ready made notebook[fn:5], and then run the following:

#+begin_src prolog
  crack_code([A, B, C, D]),


@@ 269,13 269,13 @@ The exercise of writing that in a less brute-force manner is left to you, my bel

So ends 2020, so ends this post. Did your brain-grown answer match the one this Prolog program gave you? What do you think about logic programming in general now you've seen some of it? Why not share it with your friends or whoever, if they're interested, and see what they think?

Mad propz to the Prolog community on Reddit also, whose example solutions helped point me in the right direction◊^[6].
Mad propz to the Prolog community on Reddit also, whose example solutions helped point me in the right direction[fn:6].

◊footnotes{
  ◊^[1]{◊<>["https://amzi.com/AdventureInProlog/a1start.php"] - (buy the book, srlsy...)}
  ◊^[2]{◊<>["https://swish.swi-prolog.org/p/KfdGtcJr.swinb"]}
  ◊^[3]{◊<>["https://uk.bookshop.org/books/the-art-of-prolog-advanced-programming-techniques/9780262691635"]}
  ◊^[4]{◊<>["https://www.swi-prolog.org/man/clpfd.html"]}
  ◊^[5]{◊<>["https://swish.swi-prolog.org/p/MgtEUnSv.swinb"]}
  ◊^[6]{◊<>["https://www.reddit.com/r/prolog/comments/fzww7m/cracking_this_puzzle_with_prolog/"]}
  [fn:1]{◊<>["https://amzi.com/AdventureInProlog/a1start.php"] - (buy the book, srlsy...)}
  [fn:2]{◊<>["https://swish.swi-prolog.org/p/KfdGtcJr.swinb"]}
  [fn:3]{◊<>["https://uk.bookshop.org/books/the-art-of-prolog-advanced-programming-techniques/9780262691635"]}
  [fn:4]{◊<>["https://www.swi-prolog.org/man/clpfd.html"]}
  [fn:5]{◊<>["https://swish.swi-prolog.org/p/MgtEUnSv.swinb"]}
  [fn:6]{◊<>["https://www.reddit.com/r/prolog/comments/fzww7m/cracking_this_puzzle_with_prolog/"]}
}

M posts/celebrate-each-other.org => posts/celebrate-each-other.org +7 -7
@@ 6,7 6,7 @@
:CATEGORY: personal
:END:

Back when I worked at Typeform◊^[1], it really surprised me that they casually used a system of extrinsic motivation to reward good work, and to appreciate and recognise others. That's a long-handed way of saying that they used a service called Bonusly◊^[2] and integrated it with the company chat app, which at the time was HipChat◊^[3] (hands up if you remember *not* using Slack?). We had an internal currency called Typecoin (TC)◊^[4] and you had a budget of 250 a month to offer to your fellow colleagues as an extra way of saying thanks, or shouting out. Those coins actually converted to cash or Amazon gift vouchers, it was actually pretty cool. Most of the time I'd cash out, go to the Mercadona by the beach at La Vila Olimpica (which was around the corner from our office), and buy around 10€ worth of Haribo sweets for the whole office to enjoy. It was usually either a 'ThursYAY' or 'TuesYAY' depending on what mood I was in.
Back when I worked at Typeform[fn:1], it really surprised me that they casually used a system of extrinsic motivation to reward good work, and to appreciate and recognise others. That's a long-handed way of saying that they used a service called Bonusly[fn:2] and integrated it with the company chat app, which at the time was HipChat[fn:3] (hands up if you remember *not* using Slack?). We had an internal currency called Typecoin (TC)[fn:4] and you had a budget of 250 a month to offer to your fellow colleagues as an extra way of saying thanks, or shouting out. Those coins actually converted to cash or Amazon gift vouchers, it was actually pretty cool. Most of the time I'd cash out, go to the Mercadona by the beach at La Vila Olimpica (which was around the corner from our office), and buy around 10€ worth of Haribo sweets for the whole office to enjoy. It was usually either a 'ThursYAY' or 'TuesYAY' depending on what mood I was in.

Of course, I've just linked recognition to financial reward. That wasn't really the main goal of the system though, although it operated as a nice side-effect. You couldn't gift some of your TC without connecting it to a company value and also explaining what they did to earn it, and all of this would be posted publically to a special channel in HipChat (as well as the application itself) for everyone to see. There was even a leaderboard and it was quite exciting to see who made it to the top each month (I held the record for a few months, it was a nice ego boost).



@@ 16,7 16,7 @@ The reason I bring this up so fondly is because the act of recognition was an ac

The difference is that, for the majority of my career, recognition, acknowledgement and appreciation are not typically given in such an active way. The abundance of what you provide, that they appreciate, leads to it becoming an expectation. Only when it is time for you to leave or move on does the scarcity mindset kick in and you are showered with love and support. Because they won't be able to enjoy your presence or benefit from your skills any more.

Before I continue, I'm sure some people (particularly current colleagues) reading this at the time of writing will think... is Lee talking about Babylon◊^[5] and why he's leaving? Is he airing his laundry? Emphatically, I am not :) but let's talk about Babylon anyway.
Before I continue, I'm sure some people (particularly current colleagues) reading this at the time of writing will think... is Lee talking about Babylon[fn:5] and why he's leaving? Is he airing his laundry? Emphatically, I am not :) but let's talk about Babylon anyway.

When I joined Babylon in early 2019, one of the first things I did after settling in was, well, 'make a name for myself' on Slack. Because of course, HipChat stopped being a thing in 2019. I wanted to take that positivity I experienced at Typeform, in Barcelona, and everything I'd learned and loved since, and see if something similar could happen in London. I made a gratitude channel and basically name-dropped colleagues, explaining what I appreciated through the onboarding process and the initial few weeks. Little did I know a similar scheme was being built in parallel and not long after, we got our Feel Good Fridays, which accompanied a huge list of collated messages of gratitude, recognition, and acknowledgement from one colleague to another.



@@ 29,9 29,9 @@ What I'm saying in a pretty long-winded way is that we people make the success w
Celebrate each other, celebrate yourselves. 🥳

◊footnotes{
  ◊^[1]{◊<>["https://www.typeform.com"]}
  ◊^[2]{◊<>["https://bonus.ly"]}
  ◊^[3]{◊<>["https://en.wikipedia.org/wiki/HipChat"]}
  ◊^[4]{◊<>["https://www.kamelasa.dev/images/tc.jpg"]}
  ◊^[5]{◊<>["https://www.babylonhealth.com"]}
  [fn:1]{◊<>["https://www.typeform.com"]}
  [fn:2]{◊<>["https://bonus.ly"]}
  [fn:3]{◊<>["https://en.wikipedia.org/wiki/HipChat"]}
  [fn:4]{◊<>["https://www.kamelasa.dev/images/tc.jpg"]}
  [fn:5]{◊<>["https://www.babylonhealth.com"]}
}
\ No newline at end of file

M posts/devops.org => posts/devops.org +2 -2
@@ 12,7 12,7 @@ Enterprise might not be the best word, but it's the only one I have right now, a

Let's talk about bugs and production servers. Before Bablyon I had not worked in a single place that restricted access to production. As a developer working primarily with Ruby on Rails applications, getting prod access on Heroku or AWS was practically an onboarding step, and that meant I could easily boot up a console and modify the application runtime on the fly. This is an amazingly powerful tool in development and testing environments, it's basically just a boostrapped REPL, but expose that in production and a malicious actor could wreak all sorts of havoc without leaving a trace. This is even worse if your rails app is running under `root` for some reason (e.g. through a poor Docker setup), as you can quite easily jump into a shell from there.

You couldn't do any of this at Bablyon because production and preprod were locked down /tight/, and even seeing production logs required a background check. This didn't really make debugging worse, because instead there was a huge investment in tooling (internal and external) and developer experience to balance it out. One of my favourite outcomes of this is the creation of an open source tool for managing a Kubernetes cluster, called `shipcat`◊^[1]. You know it's good when it has its own cute logo.
You couldn't do any of this at Bablyon because production and preprod were locked down /tight/, and even seeing production logs required a background check. This didn't really make debugging worse, because instead there was a huge investment in tooling (internal and external) and developer experience to balance it out. One of my favourite outcomes of this is the creation of an open source tool for managing a Kubernetes cluster, called `shipcat`[fn:1]. You know it's good when it has its own cute logo.

What I've since realised is that this particular crutch (live debugging in production) prevents the business from properly investing in safer and more compliant tools for engineers to investigate issues. You want proper structured logging, good alerting on error conditions, and a whole slew of observability (o11y) tools that can help you diagnose the system from the outside-in without compromising it. You want to have a team of impassioned engineers who enjoy working on internal productivity/efficacy, creating new tools to address pain-points in the organisation's development and support lifecycle.



@@ 31,5 31,5 @@ Before I proselytise too much, I think this is really important because you can 
And if I had one suggestion for any budding project finding itself in the hands of real life users in production, consider what you want your devops culture to be like and, if you can, see how early you can encourage your team without depending on offering wide-scale production access. And maybe even consider what kind of internal tooling you can build to improve the productivity and efficacy of your engineers.

◊footnotes{
  ◊^[1]{◊<>["https://github.com/babylonhealth/shipcat"]}
  [fn:1]{◊<>["https://github.com/babylonhealth/shipcat"]}
}
\ No newline at end of file

M posts/do-you-really-need-those-microservices.org => posts/do-you-really-need-those-microservices.org +5 -5
@@ 12,7 12,7 @@ I'm not really for-or-against them, and in fact I find it a little strange and f

Anyway, I love being asked this question because after a good five years of working with distributed systems orchestrated by Kubernetes, almost entirely in the form of 'migrating away from the monolith', I've had plenty of time to formulate and adapt my thinking around it.

First and foremost, I believe the main benefit of a business switching to microservices is the manifestation of Conway's Law◊^[1] in practice. Prior to making the switch in architecture, the business most likely decided on an organisational structure that exchanges large, difficult to manage teams for a distributed collection of smaller, self-sufficient, self-empowered teams. More often than not these follow a squad and chapter model, otherwise reduced to 'the Spotify model', and a hierarchy of cross-functional teams is established. Once that structure is put in place and people are shuffled around a bit, the question of ownership in a mixed-responsibility, mixed-domain codebase becomes apparent. Microservices are thus the solution to a perceived conflict between squads and they shift a lot of that conflict from the teams themselves, to the channels in-between them.
First and foremost, I believe the main benefit of a business switching to microservices is the manifestation of Conway's Law[fn:1] in practice. Prior to making the switch in architecture, the business most likely decided on an organisational structure that exchanges large, difficult to manage teams for a distributed collection of smaller, self-sufficient, self-empowered teams. More often than not these follow a squad and chapter model, otherwise reduced to 'the Spotify model', and a hierarchy of cross-functional teams is established. Once that structure is put in place and people are shuffled around a bit, the question of ownership in a mixed-responsibility, mixed-domain codebase becomes apparent. Microservices are thus the solution to a perceived conflict between squads and they shift a lot of that conflict from the teams themselves, to the channels in-between them.

Under no circumstance is the technical implication of such a change considered, particularly in older codebases for which this change would introduce a significant level of disruption. The organisational benefits of distributing teams and workload are substantial, but the drawbacks of distributing /code/ are similarly worthy of consideration too, and it often becomes a gateway to extra complexity as once-simple tasks find themselves spread apart over various network calls and machines.



@@ 20,7 20,7 @@ I don't consider this a dealbreaker, but in my experience I've always felt like 

In those situations, you are investing primarily in the work required to understand the different domains in your codebase and how they speak to each other, with much lower risk than fundamentally changing your infrastructure as well as your architecture. In Ruby, you can abstract code into gems and provide solid, public APIs that other parts of the code can use. In Java you have modules and packages. Every language you care to use has the concept of packaging code into bundles or libraries that can be shared as a dependency.

Adopting this workflow introduces much lower risk because, in the event of failure, you can easily adjust your expectations around the domains and how they communicate and fix them in a singular release. It may not be perfect, but if you refactor enough of your code in SOA◊^[2]/DDD◊^[3] style then you'll have a much easier time turning those domains into proper microservices further down the line.
Adopting this workflow introduces much lower risk because, in the event of failure, you can easily adjust your expectations around the domains and how they communicate and fix them in a singular release. It may not be perfect, but if you refactor enough of your code in SOA[fn:2]/DDD[fn:3] style then you'll have a much easier time turning those domains into proper microservices further down the line.

This is where a microservice architecture truly shines. If you have clear, well bounded domains, and you've developed solid API contracts as well as standards for versioning, logging, etc to allow for centralised aggregation of useful resources (for debugging or auditing, for example), and if the team in charge of the domain can essentially treat that service as a full-blown product with documentation, support, and its own priorities and backlog, then that is where the power of that infrastructure comes into play.



@@ 31,7 31,7 @@ I believe that's a lot easier to do when you start early, but if you come to it 
If I was to offer anybody advice about how to make all of this happen successfully, I'd say to stop thinking in terms of the existing monolith, and instead look at what individual products you could separate or extract, or even spin-off into their own businesses if the idea was unique enough to sell individually. And don't jump to solutions like Kubernetes until you're dealing with enough of these services that your existing deployment setup is too hard to manage.

◊footnotes{
  ◊^[1]{◊<>["https://en.wikipedia.org/wiki/Conway%27s_law"]}
  ◊^[2]{◊<>["https://en.wikipedia.org/wiki/Service-oriented_architecture"]}
  ◊^[3]{◊<>["https://en.wikipedia.org/wiki/Domain-driven_design"]}
  [fn:1]{◊<>["https://en.wikipedia.org/wiki/Conway%27s_law"]}
  [fn:2]{◊<>["https://en.wikipedia.org/wiki/Service-oriented_architecture"]}
  [fn:3]{◊<>["https://en.wikipedia.org/wiki/Domain-driven_design"]}
}
\ No newline at end of file

M posts/enough.org => posts/enough.org +2 -2
@@ 16,7 16,7 @@ It's not as healthy as stepping back and getting back into it the next day. The 

And... it's the classic case of doing less with more. The prospect of doing something instead of nothing is a seductive one.

I'm reminded of the Politician's Syllogism◊^[1], which in this case would sound like this:
I'm reminded of the Politician's Syllogism[fn:1], which in this case would sound like this:

#+begin_quote
  To be productive, I must do something


@@ 61,5 61,5 @@ Am I enough? Certainly!
Do I behave like I am? Not at all.

◊footnotes{
  ◊^[1]{◊<>["https://en.wikipedia.org/wiki/Politician%27s_syllogism"]}
  [fn:1]{◊<>["https://en.wikipedia.org/wiki/Politician%27s_syllogism"]}
}

M posts/floc-off.org => posts/floc-off.org +9 -9
@@ 10,20 10,20 @@ FLoC (Federated Learning of Cohorts) is Google's answer to the diminishing utili

Browsing this site will not opt you into this latest experiment in large-scale privacy violation.

You might notice that the site does gather analytics using plausible.io◊^[1], who themselves go into some more detail about this and how to opt-out◊^[2].
You might notice that the site does gather analytics using plausible.io[fn:1], who themselves go into some more detail about this and how to opt-out[fn:2].

You can see the analytics for yourself, as I have made them public - you and I see the same thing on that page. It's a glorified hit-counter that lets me see what posts land better than others and it is very easily adblockable. In fact, go ahead and block Javascript on this site - if there's any feature I ever add that depends on it, there will always be an accessible ~<noscript>~◊^[3] fallback if it actually matters to me.
You can see the analytics for yourself, as I have made them public - you and I see the same thing on that page. It's a glorified hit-counter that lets me see what posts land better than others and it is very easily adblockable. In fact, go ahead and block Javascript on this site - if there's any feature I ever add that depends on it, there will always be an accessible ~<noscript>~[fn:3] fallback if it actually matters to me.

I don't have any issue with that kind of technology, for what it's worth. You're only seeing how people use your site so you can figure out how you might tweak things, or understand what you need to do less of if you're scaring people away. It has practically nothing in common with the invasive tracking and advertising that follows you all across the internet, the likes that Google and Facebook involve themselves with at a scale beyond human comprehension.

Anyway, every page here is served with the ~Permissions-Policy: interest-cohort=()~ header set. There is a valid argument that this still presents a datapoint that can be tracked, but since the change is happening server-side, it is less useful than if you sent the same thing from your browser in every request, adding to your unique fingerprint (as with ~Do-Not-Track~, an abject failure of a standard◊^[4]).
Anyway, every page here is served with the ~Permissions-Policy: interest-cohort=()~ header set. There is a valid argument that this still presents a datapoint that can be tracked, but since the change is happening server-side, it is less useful than if you sent the same thing from your browser in every request, adding to your unique fingerprint (as with ~Do-Not-Track~, an abject failure of a standard[fn:4]).

If you're curious, you can also check out the Security Headers report for this site◊^[5].
If you're curious, you can also check out the Security Headers report for this site[fn:5].

◊footnotes{
  ◊^[1]{◊<>["https://plausible.io/kamelasa.dev"]}
  ◊^[2]{◊<>["https://plausible.io/blog/google-floc"]}
  ◊^[3]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript"]}
  ◊^[4]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT"]}
  ◊^[5]{◊<>["https://securityheaders.com/?q=www.kamelasa.dev&followRedirects=on"]}
  [fn:1]{◊<>["https://plausible.io/kamelasa.dev"]}
  [fn:2]{◊<>["https://plausible.io/blog/google-floc"]}
  [fn:3]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTML/Element/noscript"]}
  [fn:4]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/DNT"]}
  [fn:5]{◊<>["https://securityheaders.com/?q=www.kamelasa.dev&followRedirects=on"]}
}
\ No newline at end of file

M posts/gettin-ziggy-with-it-pi-zero.org => posts/gettin-ziggy-with-it-pi-zero.org +26 -26
@@ 8,15 8,15 @@

Alright, you can read the article first and shoot me later for a title like that, and what will inevitably become a series of Zig-based puns.

Zig, for the unaware, is a fancy language that looks to be to C what Rust is to C++. Honestly, I recommend you read the summary on the main page◊^[1] to find out more yourself, as the best I can do is to just parrot what has already been written. However, you can see it as a valid /alternative/ to C and Zig itself has claimed that it wants to be a better version of C than C itself. An ambitious challenge, for sure. To that end, Zig itself ships its own C compiler.
Zig, for the unaware, is a fancy language that looks to be to C what Rust is to C++. Honestly, I recommend you read the summary on the main page[fn:1] to find out more yourself, as the best I can do is to just parrot what has already been written. However, you can see it as a valid /alternative/ to C and Zig itself has claimed that it wants to be a better version of C than C itself. An ambitious challenge, for sure. To that end, Zig itself ships its own C compiler.

I've been interested in giving Zig a spin for quite a while, and once my Raspberry Pi Zero W◊^[2] and OLED display◊^[3] arrived in the post, I decided that this would be my best opportunity to try it out. I'm not really going to cover the process of wiring up the hardware, suffice to say that once you've got your Pi Zero you'll need to be able to SSH into it, and that you'll need a [solderless] GPIO header◊^[4] to plug the OLED display into. I recommend the Zero *W* because the W means 'WiFi', which means that if you connect it to your network you can SSH in without faffing around with USB cables and what not. It's not a requirement, though.
I've been interested in giving Zig a spin for quite a while, and once my Raspberry Pi Zero W[fn:2] and OLED display[fn:3] arrived in the post, I decided that this would be my best opportunity to try it out. I'm not really going to cover the process of wiring up the hardware, suffice to say that once you've got your Pi Zero you'll need to be able to SSH into it, and that you'll need a [solderless] GPIO header[fn:4] to plug the OLED display into. I recommend the Zero *W* because the W means 'WiFi', which means that if you connect it to your network you can SSH in without faffing around with USB cables and what not. It's not a requirement, though.

With that out of the way, let's see if we can write something in Zig to power this little display. It's going to be a simple program that simply fills the entire screen by turning the pixels from black (off) to white (on). As an extra challenge, we will do this without pulling in dependencies like WiringPi◊^[5], or relying on existing drivers, as lovely as they are.
With that out of the way, let's see if we can write something in Zig to power this little display. It's going to be a simple program that simply fills the entire screen by turning the pixels from black (off) to white (on). As an extra challenge, we will do this without pulling in dependencies like WiringPi[fn:5], or relying on existing drivers, as lovely as they are.

Instead, we will be directly using the i◊sup{2}c dev interface◊^[6]. If you're using Debian and/or Ubuntu on your Pi and your own machine, you can grab these libraries with a simple ~sudo apt install i2c-dev~. You will need to enable i◊sup{2}c on your Pi separately though, through ~sudo raspi-config~◊^[7].
Instead, we will be directly using the i◊sup{2}c dev interface[fn:6]. If you're using Debian and/or Ubuntu on your Pi and your own machine, you can grab these libraries with a simple ~sudo apt install i2c-dev~. You will need to enable i◊sup{2}c on your Pi separately though, through ~sudo raspi-config~[fn:7].

Ready to... get Ziggy with it? Oh, I bet you are. 😋 If you want to skip to the end and just grab the code, though, you can find this all on GitHub◊^[8]. I called it Stardust, like /Zig/gy Stardust. Get it?
Ready to... get Ziggy with it? Oh, I bet you are. 😋 If you want to skip to the end and just grab the code, though, you can find this all on GitHub[fn:8]. I called it Stardust, like /Zig/gy Stardust. Get it?

🥁



@@ 97,7 97,7 @@ Let's build a binary, then. Save your code into a file, say, ~stardust.zig~ and 
  zig build-exe stardust.zig  -target arm-linux-musleabihf -mcpu arm1176jzf_s -O ReleaseSafe -lc
#+end_src

To unpack that a little, the ~target~ is a triplet stating that we want to build this using the musl◊^[9] libc ABI, on a 32bit ARM architecture. ~mcpu~ goes along with that to make sure the resulting binary will work on our Pi Zero. I grabbed these values from an issue on Zig's github repo◊^[10], so credit goes to the author of that issue for unintentionally guiding me forward.
To unpack that a little, the ~target~ is a triplet stating that we want to build this using the musl[fn:9] libc ABI, on a 32bit ARM architecture. ~mcpu~ goes along with that to make sure the resulting binary will work on our Pi Zero. I grabbed these values from an issue on Zig's github repo[fn:10], so credit goes to the author of that issue for unintentionally guiding me forward.

Passing the optimiser flag (~-O~) isn't strictly necessary, so you can omit this if you require a debug build and stack traces with errors.



@@ 121,7 121,7 @@ Let's move on and make this kitten purr. Meow 🐈.

** Getting this show on the road

In true /draw the rest of the fucking owl/ fashion◊^[11], what follows is a bit of a code-dump since the primary method of communicating with your OLED display is to, literally, write a few bytes to a file. The registers available and what can be written to them are often described in a meticulously detailed datasheet◊^[12], but they're not exactly light reading and we can save a bit of time by grabbing the info from elsewhere. A lot of the constants that follow are gracefully derived from those listed in a certain ~owenosborn~'s wiringPi-based driver.◊^[13]. Credit where credit's due, eh.
In true /draw the rest of the fucking owl/ fashion[fn:11], what follows is a bit of a code-dump since the primary method of communicating with your OLED display is to, literally, write a few bytes to a file. The registers available and what can be written to them are often described in a meticulously detailed datasheet[fn:12], but they're not exactly light reading and we can save a bit of time by grabbing the info from elsewhere. A lot of the constants that follow are gracefully derived from those listed in a certain ~owenosborn~'s wiringPi-based driver.[fn:13]. Credit where credit's due, eh.

#+begin_src zig
  const SET_CONTRAST = 0x81;


@@ 150,7 150,7 @@ In true /draw the rest of the fucking owl/ fashion◊^[11], what follows is a bi
  const SET_CHARGE_PUMP = 0x8D;
#+end_src

The registers available to an i◊sup{2}c compatible device will depend on the device itself, so it's not really safe to copy and paste these without knowing exactly what you're dealing with. This is driver level code so it's not like you'll get some fancy validation error if you write the wrong bytes, you'll more likely fuck it up and burn down your house◊^[14].
The registers available to an i◊sup{2}c compatible device will depend on the device itself, so it's not really safe to copy and paste these without knowing exactly what you're dealing with. This is driver level code so it's not like you'll get some fancy validation error if you write the wrong bytes, you'll more likely fuck it up and burn down your house[fn:14].

Next we'll want to init the display and get it into a clean state, with the cursor pointing at the first pixel.



@@ 196,7 196,7 @@ Next we'll want to init the display and get it into a clean state, with the curs

Wow, actual Zig code! The formatting may look a little odd because that's what ~zig fmt~ decides is appropriate.

~init_display~ is quite a complex beast that issues a whole series of commands that sets up the display for further use. A more detailed explanation of that will be in another post, for the sake of brevity, but in essence it was adapted from AdaFruit's CircuitPi driver, written in Python◊^[15].
~init_display~ is quite a complex beast that issues a whole series of commands that sets up the display for further use. A more detailed explanation of that will be in another post, for the sake of brevity, but in essence it was adapted from AdaFruit's CircuitPi driver, written in Python[fn:15].

The recurring theme in all of these new functions is that the entire basis of their existence is to create an array of two bytes, and then write them to file descriptor we opened right at the start. The data structure looks something like this:



@@ 230,7 230,7 @@ This ~fill~ function will (rather quickly) turn the display solid white, updatin
  Reach out to me at pleasemakeitstop@mrlee.dev if this is too much for you.
}

Zig has some nice language features intended to replace and improve upon C/C++ preprocessor macros. The ~inline~ keyword is one such thing, and when applied to a ~for~ or ~while~ loop it'll unroll it at compile time. A simple optimisation but a useful one. We don't use it, but you also have ~comptime~, which is powerful enough to be able to implement generics, if you so desire. We're not going to go into that here though, and you can read more about it from a certain Loris Cro◊^[16].
Zig has some nice language features intended to replace and improve upon C/C++ preprocessor macros. The ~inline~ keyword is one such thing, and when applied to a ~for~ or ~while~ loop it'll unroll it at compile time. A simple optimisation but a useful one. We don't use it, but you also have ~comptime~, which is powerful enough to be able to implement generics, if you so desire. We're not going to go into that here though, and you can read more about it from a certain Loris Cro[fn:16].

-----



@@ 268,20 268,20 @@ Once you're done, rebuild the binary and ~scp~ it over, like you did the first t
Hopefully that worked, but if it didn't, get in touch with your feedback at wtf@mrlee.dev and help contribute to this post being a better, more informative read. After all, /works on my machine!/ can only go so far.

◊footnotes{
  ◊^[1]{◊<>["https://ziglang.org"]}
  ◊^[2]{◊<>["https://thepihut.com/products/raspberry-pi-zero-w"]}
  ◊^[3]{◊<>["https://thepihut.com/products/adafruit-pioled-128x32-monochrome-oled-add-on-for-raspberry-pi-ada3527"]}
  ◊^[4]{◊<>["https://thepihut.com/products/gpio-hammer-header-solderless"]}
  ◊^[5]{◊<>["http://wiringpi.com"]}
  ◊^[6]{◊<>["https://www.kernel.org/doc/Documentation/i2c/dev-interface"]}
  ◊^[7]{◊<>["https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c"]}
  ◊^[8]{◊<>["https://github.com/mrleedev/stardust"]}
  ◊^[9]{◊<>["https://musl.libc.org"]}
  ◊^[10]{◊<>["https://github.com/ziglang/zig/issues/4875"]}
  ◊^[11]{◊<>["https://knowyourmeme.com/memes/how-to-draw-an-owl"]}
  ◊^[12]{◊<>["https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf"]}
  ◊^[13]{◊<>["https://github.com/owenosborn/SSD1306-OLED-WiringPi/blob/master/ssd1306.h"]}
  ◊^[14]{Possibly exaggerated for effect. Possibly.}
  ◊^[15]{◊<>["https://github.com/adafruit/Adafruit_CircuitPython_SSD1306/blob/master/adafruit_ssd1306.py"]}
  ◊^[16]{◊<>["https://kristoff.it/blog/what-is-zig-comptime/"]}
  [fn:1]{◊<>["https://ziglang.org"]}
  [fn:2]{◊<>["https://thepihut.com/products/raspberry-pi-zero-w"]}
  [fn:3]{◊<>["https://thepihut.com/products/adafruit-pioled-128x32-monochrome-oled-add-on-for-raspberry-pi-ada3527"]}
  [fn:4]{◊<>["https://thepihut.com/products/gpio-hammer-header-solderless"]}
  [fn:5]{◊<>["http://wiringpi.com"]}
  [fn:6]{◊<>["https://www.kernel.org/doc/Documentation/i2c/dev-interface"]}
  [fn:7]{◊<>["https://learn.adafruit.com/adafruits-raspberry-pi-lesson-4-gpio-setup/configuring-i2c"]}
  [fn:8]{◊<>["https://github.com/mrleedev/stardust"]}
  [fn:9]{◊<>["https://musl.libc.org"]}
  [fn:10]{◊<>["https://github.com/ziglang/zig/issues/4875"]}
  [fn:11]{◊<>["https://knowyourmeme.com/memes/how-to-draw-an-owl"]}
  [fn:12]{◊<>["https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf"]}
  [fn:13]{◊<>["https://github.com/owenosborn/SSD1306-OLED-WiringPi/blob/master/ssd1306.h"]}
  [fn:14]{Possibly exaggerated for effect. Possibly.}
  [fn:15]{◊<>["https://github.com/adafruit/Adafruit_CircuitPython_SSD1306/blob/master/adafruit_ssd1306.py"]}
  [fn:16]{◊<>["https://kristoff.it/blog/what-is-zig-comptime/"]}
}

M posts/hakyll-on-devops-pipelines.org => posts/hakyll-on-devops-pipelines.org +9 -9
@@ 15,11 15,11 @@ In a way, this is total overkill for a static site. If I have the repo cloned on

It's flawed compared to using ~rsync~, as it won't remove existing files, but it does the job in less than a second or two.

The thing is, this isn't so quick if I want to publish a post from a different computer that doesn't have any programming tools installed. I would have to install ~stack~ ◊^[1], which is a build tool for Haskell, and then I would have to run ~stack build~. This can take at least half an hour as the command will pull down the correct version of ~GHC~ and a 'snapshot' (basically a huge collection of all the Hackage◊^[2] libraries available for that build) before it even /thinks/ about compiling my ~site.hs~ file. It also means to committing a few gigs of storage space for all of that.
The thing is, this isn't so quick if I want to publish a post from a different computer that doesn't have any programming tools installed. I would have to install ~stack~ [fn:1], which is a build tool for Haskell, and then I would have to run ~stack build~. This can take at least half an hour as the command will pull down the correct version of ~GHC~ and a 'snapshot' (basically a huge collection of all the Hackage[fn:2] libraries available for that build) before it even /thinks/ about compiling my ~site.hs~ file. It also means to committing a few gigs of storage space for all of that.

I like to write from my little Surface Pro when I'm out and about, so I'd rather not do a full-blown compilation on that for the sake of my battery. Enter Azure DevOps Pipelines◊^[3].
I like to write from my little Surface Pro when I'm out and about, so I'd rather not do a full-blown compilation on that for the sake of my battery. Enter Azure DevOps Pipelines[fn:3].

I've been keen on playing with these pipelines for a while, and much like any dev-tool, it has a free tier for open source repos. So does Github Actions◊^[4], which actually shares some of the underlying architecture of DevOps Pipelines, but I wanted to play with something different.
I've been keen on playing with these pipelines for a while, and much like any dev-tool, it has a free tier for open source repos. So does Github Actions[fn:4], which actually shares some of the underlying architecture of DevOps Pipelines, but I wanted to play with something different.

Let's do a step-by-step walk through my setup.



@@ 175,12 175,12 @@ This is similar to running ~rsync~ to deploy, except that it knows where to get 

To solve the issue I first mentioned, ~cleanTargetFolder~ makes sure to delete the previous deployment before copying the new one over. Problem solved!

To see the pipeline in full, you can check out the full YAML file◊^[5]. I've been using it with success for the past couple of weeks now.
To see the pipeline in full, you can check out the full YAML file[fn:5]. I've been using it with success for the past couple of weeks now.

◊footnotes{
  ◊^[1]{◊<>["https://docs.haskellstack.org/en/stable/README"]}
  ◊^[2]{◊<>["https://hackage.haskell.org"]}
  ◊^[3]{◊<>["https://dev.azure.com"]}
  ◊^[4]{◊<>["https://github.com/features/actions"]}
  ◊^[5]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/9decaf4732dc7bc4510fcf23979af8657bdb01bd/item/azure/pipeline.yml"]}
  [fn:1]{◊<>["https://docs.haskellstack.org/en/stable/README"]}
  [fn:2]{◊<>["https://hackage.haskell.org"]}
  [fn:3]{◊<>["https://dev.azure.com"]}
  [fn:4]{◊<>["https://github.com/features/actions"]}
  [fn:5]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/9decaf4732dc7bc4510fcf23979af8657bdb01bd/item/azure/pipeline.yml"]}
}

M posts/human-after-all.org => posts/human-after-all.org +5 -5
@@ 10,7 10,7 @@ I'm sure everybody goes through the 'angry programmer' phase at some point in th

What I've just done is create an archetype of a person that reduces them to a few simplistic characteristics that might sound amusing if you relate to it, but can just as easily be used as a label to diminish your opinion of someone.

The internet is full of articles that treat interpersonal dynamics at work like a game of Dungeons and Dragons; and the goal of the campaign is for you and your colleages to seek out and destroy the toxic elements in your team. God forbid you identify The Two-Face◊^[1], or The Sociopath◊^[2].
The internet is full of articles that treat interpersonal dynamics at work like a game of Dungeons and Dragons; and the goal of the campaign is for you and your colleages to seek out and destroy the toxic elements in your team. God forbid you identify The Two-Face[fn:1], or The Sociopath[fn:2].

You might say that these shouldn't be taken so seriously, and to an extent you'd be correct. They're just puff pieces, after all, right? And you can have a self-deprecating chuckle about if you feel like you identify with the archetypes described.



@@ 30,7 30,7 @@ Whew, better bring this back to the topic I had in mind before I go off on anoth

*Compassion*.

I like compassion. Actually no, I /love/ it. It's an amazing word that can mean many things to many people, but I like to think it's what gives this world the soul it has; it's certainly responsible for a lot of good. The Compassionate Mind◊^[3] dedicates over 500 pages and thousands upon thousands of words to this, and this is what the blurb has to say about it:
I like compassion. Actually no, I /love/ it. It's an amazing word that can mean many things to many people, but I like to think it's what gives this world the soul it has; it's certainly responsible for a lot of good. The Compassionate Mind[fn:3] dedicates over 500 pages and thousands upon thousands of words to this, and this is what the blurb has to say about it:

> Not only does compassion help to soothe distressing emotions, it actually increases feelings of contentment and well-being.



@@ 47,7 47,7 @@ Of course, it doesn't work out for everyone. It's not about protecting people or
Just, no reason to be so needlessly unkind to each other. We're all human, after all.

◊footnotes{
  ◊^[1]{◊<>["https://twentytentalent.com/8-types-of-toxic-managers/"]}
  ◊^[2]{◊<>["https://getvoip.com/blog/2015/02/24/toxic-employees/"]}
  ◊^[3]{◊<>["https://uk.bookshop.org/a/6865/9781849010986"]}
  [fn:1]{◊<>["https://twentytentalent.com/8-types-of-toxic-managers/"]}
  [fn:2]{◊<>["https://getvoip.com/blog/2015/02/24/toxic-employees/"]}
  [fn:3]{◊<>["https://uk.bookshop.org/a/6865/9781849010986"]}
}
\ No newline at end of file

M posts/i-am-here.org => posts/i-am-here.org +7 -7
@@ 14,13 14,13 @@ It's funny, really. Life for me really began in 2012 when I moved from my parent

Three years later, in 2015, and I found myself moving to Barcelona for a new job at what was then a tiny startup called Typeform, truly embracing this independence I'd established earlier in the decade. It was a fantastic place to work, with fantastic people from all walks of life, and that was before you got to the weekly beach volleyball, sailing, and the general way of life in that little corner of Spain. It was around this time that my mental health began to diminish further, so I sought out a psychotherapist who could continue the work the one I had in the UK started, and so I had an impartial output for the things I was going through.

You might notice I said 'psychotherapist' and not just 'therapist'. In the UK I booked time with a private therapist who offered breathwork◊^[1] as a way to dive into past trauma and help heal it, along side the usual talking and listening, and some mild CBT (Cognitive Behavioural Therapy)◊^[2]. After some retreats in both Trakai, Lithuania and Lesvos, Greece, I moved on to something known as somatic experiencing◊^[3]. I'm aware that someone reading this might think "what on earth was this guy getting himself into?" or possibly be dismissive of these alternative aspects of therapy. I think that's fine and I accept that it can seem unusual or strange to some, and they are certainly not all-healing panaceas like many alternative medicines are perceived to be: they can very easily cause more harm than good if you place your trust into the wrong hands. I think that is a major issue with many New Age therapies, when they offer simplistic solutions to difficult problems.
You might notice I said 'psychotherapist' and not just 'therapist'. In the UK I booked time with a private therapist who offered breathwork[fn:1] as a way to dive into past trauma and help heal it, along side the usual talking and listening, and some mild CBT (Cognitive Behavioural Therapy)[fn:2]. After some retreats in both Trakai, Lithuania and Lesvos, Greece, I moved on to something known as somatic experiencing[fn:3]. I'm aware that someone reading this might think "what on earth was this guy getting himself into?" or possibly be dismissive of these alternative aspects of therapy. I think that's fine and I accept that it can seem unusual or strange to some, and they are certainly not all-healing panaceas like many alternative medicines are perceived to be: they can very easily cause more harm than good if you place your trust into the wrong hands. I think that is a major issue with many New Age therapies, when they offer simplistic solutions to difficult problems.

This kind of work continued for a couple of years until I decided I wanted a quieter life than what Barcelona had to offer. If you think that sounds utterly insane, because who would want to leave such a beautiful city? Well, I offer you the chance of working full-time for a week during Festa de Gracia, a week long festival in the village of Gracia that doesn't start to wrap up until 4 or 5am each morning. Something that is immensely fun, and is an incredible display of Catalunya's culture of celebration, wears a little thin when you desperately want to catch some proper shut-eye before work. So, I moved to Latvia, or Jurmala in Latvia more precisely, and enjoyed a solid year of working remotely besides the beautiful Baltic coast, which is genuinely a sight to behold.

In that time on the continent I travelled to more places, and experienced more new things, than I ever had before. Latvia itself was (and still is) utterly gorgeous. Meanwhile, I enjoyed visiting places as varied as Naxos, Istanbul, Croatia, Estonia, Lisbon, Verona, Vienna, and a few other places I struggle to remember. I was close to the epicentre of the Barcelona terror attack in 2017, and experienced the city in a much more sombre, mournful light. I was close to being in Ankara while Erdogan's coup took place until I was encouraged to cancel the journey. I had a genuine thirst for adventure that was hugely facilitated by being able to work remotely. 

Not long after that, I moved back to London. Partly due to redundancies in the (London based) startup I was working for out in Latvia, but also due to isolation and missing the people I loved back in London, feeling like I was ready to get closer to the action again. The focus on my health narrowed a little and this time I chose a therapist who specialised in complex PTSD◊^[4] and post-traumatic growth◊^[5], and later one who dealt with relationship issues specifically (as a single man, there are still things in that area to explore).
Not long after that, I moved back to London. Partly due to redundancies in the (London based) startup I was working for out in Latvia, but also due to isolation and missing the people I loved back in London, feeling like I was ready to get closer to the action again. The focus on my health narrowed a little and this time I chose a therapist who specialised in complex PTSD[fn:4] and post-traumatic growth[fn:5], and later one who dealt with relationship issues specifically (as a single man, there are still things in that area to explore).

I'm going to skip a head a bit because I do actually have a point here, besides being autobiographical. Those eight years so far have been almost consistently tinged with the hue of mental illness. We often use the colour blue to represent a low mood, but I'd really see it as a burning greyness...smouldering embers glowing in charred remains like a burned out log fire. Occasionally a new breath of life will bring the fire back to its original ferocity but, almost inevitably, you'll run out of logs to keep it aflame. Thus starts the slow but necessary process of tending to yourself so that you can refuel that fire after a period of recovery.



@@ 31,9 31,9 @@ To bring this back to the start, I would not change a thing as my place in this 
This isn't to say all my problems are solved, or I'm finished with my process. That's *pure BS* as this stuff lasts an entire lifetime as you learn, grow and adapt to new situations. But the track record I have must be pretty good if I'm /here/, right?

◊footnotes{
  ◊^[1]{◊<>["https://en.wikipedia.org/wiki/Breathwork"]}
  ◊^[2]{◊<>["https://en.wikipedia.org/wiki/Cognitive_behavioral_therapy"]}
  ◊^[3]{◊<>["https://en.wikipedia.org/wiki/Somatic_experiencing"]}
  ◊^[4]{◊<>["https://en.wikipedia.org/wiki/Complex_post-traumatic_stress_disorder"]}
  ◊^[5]{◊<>["https://en.wikipedia.org/wiki/Posttraumatic_growth"]}
  [fn:1]{◊<>["https://en.wikipedia.org/wiki/Breathwork"]}
  [fn:2]{◊<>["https://en.wikipedia.org/wiki/Cognitive_behavioral_therapy"]}
  [fn:3]{◊<>["https://en.wikipedia.org/wiki/Somatic_experiencing"]}
  [fn:4]{◊<>["https://en.wikipedia.org/wiki/Complex_post-traumatic_stress_disorder"]}
  [fn:5]{◊<>["https://en.wikipedia.org/wiki/Posttraumatic_growth"]}
}
\ No newline at end of file

M posts/my-favourite-thing-about-programming.org => posts/my-favourite-thing-about-programming.org +2 -2
@@ 57,7 57,7 @@ With emacs in particular, it made it trivially easy for me to launch a Rails con

I mapped it to a certain keybinding and a panel would open to the side within a second, ready for me to use. I don't think I'd have the patience to try and reproduce that in, say, VS Code, without using a task runner. Emacs itself is entirely programmable so you don't need to worry about setting up extension boilerplate to make minor modifications.

I should round this post off with an even three examples, so my final two are Smalltalk and Prolog. I haven't managed to build anything in it yet, as the learning curve is quite unlike any other. However, aspects of Smalltalk live on in languages like Ruby, where everything is an object and everything is defined in terms of message passing. I think Objective-C can be counted there too, although both languages diverge from some of Smalltalk's ideals in the name of pragmatism. My short term goal with Smalltalk is to (attempt to) implement a raytracer as described in /The Ray Tracer Challenge/ by James Buck◊^[1], so I can better understand the language and apply what I've learned elsewhere. 
I should round this post off with an even three examples, so my final two are Smalltalk and Prolog. I haven't managed to build anything in it yet, as the learning curve is quite unlike any other. However, aspects of Smalltalk live on in languages like Ruby, where everything is an object and everything is defined in terms of message passing. I think Objective-C can be counted there too, although both languages diverge from some of Smalltalk's ideals in the name of pragmatism. My short term goal with Smalltalk is to (attempt to) implement a raytracer as described in /The Ray Tracer Challenge/ by James Buck[fn:1], so I can better understand the language and apply what I've learned elsewhere. 

Prolog is on my list, and I keep coming back to it every couple of months to see what I'm inspired to do. I find it, and logic programming, intriguing, and I wonder how many problems would be solved a bit more easily that way compared to how we usually smush things together in procedural or OOP languages. Watch this space for more insight, I guess.



@@ 66,5 66,5 @@ One thing I've noticed while writing this is that each language selected has lit
For as long as that remains true I think I'll always have new toys to play with.

◊footnotes{
  ◊^[1]{◊<>["http://raytracerchallenge.com"]}
  [fn:1]{◊<>["http://raytracerchallenge.com"]}
}

M posts/on-sharing-vulnerability.org => posts/on-sharing-vulnerability.org +3 -3
@@ 6,7 6,7 @@
:CATEGORY: personal
:END:

One of the most wonderful things I've seen on the internet in recent years is our growing willingness to be vulnerable, together. I have to admit that this wasn't really a 'thing' for me until I'd read Daring Greatly by Brene Brown◊^[1], as well as Rising Strong◊^[2]. As far as self help books go, I don't think I would actually categorise them as such.
One of the most wonderful things I've seen on the internet in recent years is our growing willingness to be vulnerable, together. I have to admit that this wasn't really a 'thing' for me until I'd read Daring Greatly by Brene Brown[fn:1], as well as Rising Strong[fn:2]. As far as self help books go, I don't think I would actually categorise them as such.

Even LinkedIn these days is full of posts of people sharing their failures, admitting mistakes, dropping their egos, and being authentic with each other in favour of presenting a flawless persona. It's a beautiful thing, to allow yourself to be yourself, your very human self, without the pressure of having to appear flawless.



@@ 27,6 27,6 @@ So, considering that we see ourselves becoming more distant as a result of the C

◊footnotes{
  ◊^[1]{◊<>["https://uk.bookshop.org/a/6865/9780241257401"]}
  ◊^[2]{◊<>["https://uk.bookshop.org/a/6865/9780091955038"]}
  [fn:1]{◊<>["https://uk.bookshop.org/a/6865/9780241257401"]}
  [fn:2]{◊<>["https://uk.bookshop.org/a/6865/9780091955038"]}
}
\ No newline at end of file

M posts/permanent-solutions-to-temporary-problems.org => posts/permanent-solutions-to-temporary-problems.org +6 -6
@@ 36,7 36,7 @@ There are loads of resources out there that could help out if you're starting to
    Ask your closest friends if you can confide in them (try not to treat a 'no' as a rejection, some people are better equipped to support than others)
  }
  ◊li{
    If you can chat to people you don't know over the phone, then Samaritans◊^[1] is a good place to start but not the only place
    If you can chat to people you don't know over the phone, then Samaritans[fn:1] is a good place to start but not the only place
  }
  ◊li{
    Talk to your doctor about Cognitive Behavioural Therapy (CBT)


@@ 47,10 47,10 @@ A friend has also suggested some other options:

◊ul{
  ◊li{
    If you prefer to text rather than call, then shout◊^[2] might work for you
    If you prefer to text rather than call, then shout[fn:2] might work for you
  }
  ◊li{
    And if you're younger, still in school maybe, then Papyrus◊^[3] can help
    And if you're younger, still in school maybe, then Papyrus[fn:3] can help
  }
}



@@ 59,7 59,7 @@ I'm aware that these options are very specific to the UK and you might not have 
Take care of yourself.

◊footnotes{
  ◊^[1]{◊<>["https://www.samaritans.org"]}
  ◊^[2]{◊<>["https://giveusashout.org"]}
  ◊^[3]{◊<>["https://www.papyrus-uk.org"]}
  [fn:1]{◊<>["https://www.samaritans.org"]}
  [fn:2]{◊<>["https://giveusashout.org"]}
  [fn:3]{◊<>["https://www.papyrus-uk.org"]}
}
\ No newline at end of file

M posts/rewrite-it-in-lisp.org => posts/rewrite-it-in-lisp.org +9 -9
@@ 6,11 6,11 @@
:END:
:PUBLISHED: t

I finally got around to paying some money for /Beautiful Racket}◊^[1], a fantastic online-only book that introduces you to Racket by guiding you through building a few language implementations. In that sense it's not unlike ◊em{Write Yourself a Scheme in 48 Hours/◊^[2], except that book focusses on Haskell and (through no fault of its own) lacks the wonderful presentation of Beautiful Racket.
I finally got around to paying some money for /Beautiful Racket}[fn:1], a fantastic online-only book that introduces you to Racket by guiding you through building a few language implementations. In that sense it's not unlike ◊em{Write Yourself a Scheme in 48 Hours/[fn:2], except that book focusses on Haskell and (through no fault of its own) lacks the wonderful presentation of Beautiful Racket.

Developing a soft spot for a lisp feels like a rite of passage for a typical programmer, alongside owning a copy of SICP and using emacs as your main editor. And for good reason, really; the beauty of writing lisp is that you are basically working directly with an AST instead of dealing with a special syntax that is parsed into one. It's powerfully expressive, if not a little intimidating at first.

Going through Beautiful Racket led me to the technology used to build the book, Pollen◊^[3]. Reading through the introduction and some of the reasoning behind Pollen's design decisions, I felt like I agreed enough with the premise of the system that I wanted to try it out. It also helped that I've been in the process of outlining a book I'd like to write, and this helped me settle on what I could use to accomplish that.
Going through Beautiful Racket led me to the technology used to build the book, Pollen[fn:3]. Reading through the introduction and some of the reasoning behind Pollen's design decisions, I felt like I agreed enough with the premise of the system that I wanted to try it out. It also helped that I've been in the process of outlining a book I'd like to write, and this helped me settle on what I could use to accomplish that.

The best way to evaluate a solution is to build a proof of concept, and soon enough I'd embarked on a several-week long project to convert this blog to be a Pollen-based publication. The end result is what you're reading now: a successful rewrite, operating in production without a hitch.



@@ 79,16 79,16 @@ The estimated reading time was also easier to implement than I first thought. Ra
     words-per-minute)))
#+end_src

You can see the rest of what I scripted in the main `pollen.rkt` file for this site◊^[4]
You can see the rest of what I scripted in the main `pollen.rkt` file for this site[fn:4]

In the end, the only thing I traded-off was the post category in the URL itself. Hakyll made that easy to add, but ultimately it wasn't that important a feature to add. There was no point getting clever as there are only a dozen or so published posts, so I constructed a flat list of permanent redirects for Caddy to serve◊^[5].
In the end, the only thing I traded-off was the post category in the URL itself. Hakyll made that easy to add, but ultimately it wasn't that important a feature to add. There was no point getting clever as there are only a dozen or so published posts, so I constructed a flat list of permanent redirects for Caddy to serve[fn:5].

Overall, I'm happy with the change. It doesn't take 45 minutes to re-compile my Hakyll build if I add new functionality, and I don't have to host my own Debian repo to install the compiled binary through ~apt~. Rendering the site and publishing it is still completed in a matter of seconds. And, now I've gone through the effort of deploying it for this site, I'm a lot more confident about using Pollen again for other projects.

◊footnotes{
  ◊^[1]{◊<>["https://beautifulracket.com/"]}
  ◊^[2]{◊<>["https://en.m.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours"]}
  ◊^[3]{◊<>["https://docs.racket-lang.org/pollen"]}
  ◊^[4]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/main/item/pollen.rkt"]}
  ◊^[5]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/main/item/redirs.caddy"]}
  [fn:1]{◊<>["https://beautifulracket.com/"]}
  [fn:2]{◊<>["https://en.m.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_Hours"]}
  [fn:3]{◊<>["https://docs.racket-lang.org/pollen"]}
  [fn:4]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/main/item/pollen.rkt"]}
  [fn:5]{◊<>["https://git.sr.ht/~mrlee/www.kamelasa.dev/tree/main/item/redirs.caddy"]}
}

M posts/ruby-sorcery-ractor-2.org => posts/ruby-sorcery-ractor-2.org +8 -8
@@ 6,7 6,7 @@
:CATEGORY: ruby
:END:

In the previous chapter of this excurusion into Ractor◊^[1], the concept of the actor model was introduced and a toy TCP server was created. It was a naive implementation that created new Ractors for every TCP connection made to the server, and it looked like this:
In the previous chapter of this excurusion into Ractor[fn:1], the concept of the actor model was introduced and a toy TCP server was created. It was a naive implementation that created new Ractors for every TCP connection made to the server, and it looked like this:

◊codeblock['ruby]{
  require 'socket'


@@ 57,7 57,7 @@ The anatomy of an HTTP request is divided, essentially, into three parts:

The first line states the request method (i.e. ~GET~, ~POST~, etc.) and the target of the request, which will often be a relative path on the server but can also be a full URL.

◊aside{Note that there is also a concept of HTTP 'trailers', which are headers that appear /after} the body◊^[2]. These are only used for certain kinds of chunked requests and are way out of scope for this post./
◊aside{Note that there is also a concept of HTTP 'trailers', which are headers that appear /after} the body[fn:2]. These are only used for certain kinds of chunked requests and are way out of scope for this post./

What follows is a list of headers, which are key/value pairs used to provide extra information about the request. For the sake of simplicity, most of these will be ignored, and there are /many/ of them.



@@ 65,7 65,7 @@ Finally, there is a place for the body of the request. This is optional, but it 

In this chapter, the main focus is on the first section, and some of the second. And since the purpose of the post is to demonstrate Ractor, and because in the Actor model, everything is an actor... the thing that parses the request will be an Actor too.

◊aside{This is also a good time to use some of that pattern matching knowledge from the first part of this series.◊^[3]}
◊aside{This is also a good time to use some of that pattern matching knowledge from the first part of this series.[fn:3]}

Something like this should do the trick, and provide a foundation to build on.



@@ 108,7 108,7 @@ Something like this should do the trick, and provide a foundation to build on.
  end
}

◊aside{Why all the ~loop~s and ~while~ loops? Ractors behave a bit like Enumerators◊^[5], which means that if they stop yielding values or actually return a value, the Ractors close and can no longer be used.}
◊aside{Why all the ~loop~s and ~while~ loops? Ractors behave a bit like Enumerators[fn:5], which means that if they stop yielding values or actually return a value, the Ractors close and can no longer be used.}

What we have here is a Ractor that waits for incoming HTTP request messages, and then parses them into something that the server can more easily work with by pulling out important info like the request location, the HTTP method, the content type, and the body. In this example, Ruby's pattern matching features are liberally employed to handle the parsing in some places; this is more for the sake of demonstration to show that it /can} be done, not necessarily that it always ◊em{should/ be.



@@ 211,8 211,8 @@ It's gonna take a little bit more work to turn this into a workable HTTP server,
The next chapter will focus on creating a valid response, something that ~curl~ will like. Keep in mind that the primarily goal is to get something that works, warts and all, and later on it will be revisited, having learned more.

◊footnotes{
  ◊^[1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery-ractor.html"]}
  ◊^[2]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer"]}
  ◊^[3]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery.html"]}
  ◊^[4]{◊<>["https://ruby-doc.com/core-3.0.0/Enumerator.html"]}
  [fn:1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery-ractor.html"]}
  [fn:2]{◊<>["https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Trailer"]}
  [fn:3]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery.html"]}
  [fn:4]{◊<>["https://ruby-doc.com/core-3.0.0/Enumerator.html"]}
}
\ No newline at end of file

M posts/ruby-sorcery-ractor-3.org => posts/ruby-sorcery-ractor-3.org +5 -5
@@ 6,7 6,7 @@
:CATEGORY: ruby
:END:

In chapter 2 of this exploration, a basic Ractor-based TCP server was refactored into a partly-functional HTTP server.◊^[1] It can handle super-basic requests, but it doesn't send back a valid HTTP response. That means that it's difficult to use tools like ~curl~ to interact with the server and, thanks to that, previous demonstrations have depended on hand-crafting requests inside a ~telnet~ session.
In chapter 2 of this exploration, a basic Ractor-based TCP server was refactored into a partly-functional HTTP server.[fn:1] It can handle super-basic requests, but it doesn't send back a valid HTTP response. That means that it's difficult to use tools like ~curl~ to interact with the server and, thanks to that, previous demonstrations have depended on hand-crafting requests inside a ~telnet~ session.

This chapter is therefore going to focus on constructing a valid HTTP response. As with the previous chapter, it won't cover 100% of the spec, but it will lay down some decent foundations.



@@ 68,14 68,14 @@ For the sake of simplicity, the server will handle only the simple case for now:

** Building an HTTP Response in Ractor

This is the example HTTP request from the previous chapter◊^[1].
This is the example HTTP request from the previous chapter[fn:1].

◊codeblock['text]{
  GET /hello-world HTTP/1.1
  Host: localhost:1337
}

It doesn't do very much, and the server can respond with whatever it likes. At a higher level of abstraction, this is typically the part where you define your 'routes' and then add controllers or handlers to perform some action, and then the underlying framework will convert all of that into something that can be sent back over the wire to the browser. In Ruby, Rack◊^[2] is a popular library that does achieves the same thing.
It doesn't do very much, and the server can respond with whatever it likes. At a higher level of abstraction, this is typically the part where you define your 'routes' and then add controllers or handlers to perform some action, and then the underlying framework will convert all of that into something that can be sent back over the wire to the browser. In Ruby, Rack[fn:2] is a popular library that does achieves the same thing.

For our sake, the server will just take the location provided in the request and then echo it back in an HTML document, perhaps something like this:



@@ 97,6 97,6 @@ Of course, this will be done in another Ractor, but we'll also address the probl


◊footnotes{
  ◊^[1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery-ractor-2.html"]}
  ◊^[2]{◊<>["https://github.com/rack/rack"]}
  [fn:1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery-ractor-2.html"]}
  [fn:2]{◊<>["https://github.com/rack/rack"]}
}
\ No newline at end of file

M posts/ruby-sorcery-ractor.org => posts/ruby-sorcery-ractor.org +18 -18
@@ 6,9 6,9 @@
:CATEGORY: ruby
:END:

This is part two of a series of posts about Ruby and its more experimental features. The first part is about pattern matching.◊^[1]
This is part two of a series of posts about Ruby and its more experimental features. The first part is about pattern matching.[fn:1]

Ractor is a new addition to Ruby's core library, and it is essentially an implementation of the Actor model. More importantly, it offers a more lightweight approach to concurrency that might feel more at home to those familiar with Go's channels, or have perhaps worked with Elixir. Note that this isn't a wholesale replacement of Ruby's existing multithreading implementations, namely ~Thread~◊^[2] and ~Fiber~◊^[3], and is still highly experimental. As such, there is no guarantee that it would remain stable across future Ruby versions.
Ractor is a new addition to Ruby's core library, and it is essentially an implementation of the Actor model. More importantly, it offers a more lightweight approach to concurrency that might feel more at home to those familiar with Go's channels, or have perhaps worked with Elixir. Note that this isn't a wholesale replacement of Ruby's existing multithreading implementations, namely ~Thread~[fn:2] and ~Fiber~[fn:3], and is still highly experimental. As such, there is no guarantee that it would remain stable across future Ruby versions.

◊aside{Speaking of ~Fiber~s, they've received some upgrades in Ruby 3 too. You can now create non-blocking fibers and provide your own scheduler to run them automatically.}



@@ 31,9 31,9 @@ How does it fit together? An actor can have multiple addresses, and many actors 

** Actor languages

If you've used Erlang◊^[4] or Elixir◊^[5] to any degree, you have already built something around this model and, perhaps, were not aware of it. This is because the actor model is an intuitive aspect of those languages that makes them what they are, and therefore everything is implemented in terms of it.
If you've used Erlang[fn:4] or Elixir[fn:5] to any degree, you have already built something around this model and, perhaps, were not aware of it. This is because the actor model is an intuitive aspect of those languages that makes them what they are, and therefore everything is implemented in terms of it.

◊aside{Pony◊^[6] is a another more recent language that is designed around the actor model.}
◊aside{Pony[fn:6] is a another more recent language that is designed around the actor model.}

In Erlang/Elixir, one key feature is that programs are designed to be fault-tolerant by being able to crash. If a crash happens, a new actor will be spun up in its place and processing will continue as normal. This is handled by another actor, known as a supervisor, which has the sole responsibility of doing something when other actors crash.



@@ 41,7 41,7 @@ In Erlang/Elixir, one key feature is that programs are designed to be fault-tole

** Actors in the wild

You can consider technologies like Kubernetes◊^[7], Kafka◊^[8], and Web Workers◊^[9] in the browser to each be an application of the actor model in some form. This doesn't necessarily mean they were built with that in mind, just that you can find that they share many characteristics in common.
You can consider technologies like Kubernetes[fn:7], Kafka[fn:8], and Web Workers[fn:9] in the browser to each be an application of the actor model in some form. This doesn't necessarily mean they were built with that in mind, just that you can find that they share many characteristics in common.

For example, in Kubernetes (K8S), the orchestrator is an actor that behaves as a supervisor. It is responsible for monitoring all of the other services that are deployed in the cluster and ensuring that they're kept alive. If a service goes down, it will attempt to reboot it. The service is also an actor, as it can talk to other services. It can change its own state (e.g. in-memory or with a database), but it cannot (or should not) reach into other services to do the same.



@@ 53,7 53,7 @@ Similarly, an email inbox is another application of the pattern. Your email inbo

Ruby's implementation of the actor model is called Ractor. There's a little bit of history here: it was originally called Guilds, and it's been in the making for a good few years now.

Ruby 3.0 introduces Ractor to the general community, however it is still marked as experimental. This means that the API may change in later versions, or behaviours may change based on feedback, and while you might be fine to run Ractors in production...well... /caveat emptor/◊^[10].
Ruby 3.0 introduces Ractor to the general community, however it is still marked as experimental. This means that the API may change in later versions, or behaviours may change based on feedback, and while you might be fine to run Ractors in production...well... /caveat emptor/[fn:10].

First things first, quick recap on Actors:



@@ 65,7 65,7 @@ First things first, quick recap on Actors:

In order to guarantee thread-safety, some aspects of the language have had to change. Most objects in Ruby are unshareable by default, which is different to how a ~Thread~ behaves, and this means that code inside a ractor essentially cannot read /anything/ outside of its own scope, which includes global variables and constants.

Rather than rewording the Ruby manual on Ractors◊^[11], let's dig into a practical example and build a basic echo server over TCP.
Rather than rewording the Ruby manual on Ractors[fn:11], let's dig into a practical example and build a basic echo server over TCP.

◊aside{If you're following along, make sure you're using Ruby 3!}



@@ 113,15 113,15 @@ Try it for yourself by running that code in an IRB console, and then open up ~te
The problem with this code is that it's too simple: it shows how one Ractor can spawn other ractors, but it's not taking advantage of the communication channels they have and how objects are shared between Ractors. Keep an eye out for the next part of Ruby Sorcery, where there'll be a much deeper dive into Ractor's capabilities.

◊footnotes{
  ◊^[1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery.html"]}
  ◊^[2]{◊<>["https://ruby-doc.org/core-3.0.2/Thread.html"]}
  ◊^[3]{◊<>["https://ruby-doc.org/core-3.0.2/Fiber.html"]}
  ◊^[4]{◊<>["https://www.erlang.org"]}
  ◊^[5]{◊<>["https://elixir-lang.org"]}
  ◊^[6]{◊<>["https://www.ponylang.io"]}
  ◊^[7]{◊<>["https://kubernetes.io"]}
  ◊^[8]{◊<>["https://kafka.apache.org"]}
  ◊^[9]{◊<>["https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers"]}
  ◊^[10]{Let the buyer beware.}
  ◊^[11]{◊<>["https://docs.ruby-lang.org/en/master/doc/ractor_md.html"]}
  [fn:1]{◊<>["https://www.kamelasa.dev/posts/ruby-sorcery.html"]}
  [fn:2]{◊<>["https://ruby-doc.org/core-3.0.2/Thread.html"]}
  [fn:3]{◊<>["https://ruby-doc.org/core-3.0.2/Fiber.html"]}
  [fn:4]{◊<>["https://www.erlang.org"]}
  [fn:5]{◊<>["https://elixir-lang.org"]}
  [fn:6]{◊<>["https://www.ponylang.io"]}
  [fn:7]{◊<>["https://kubernetes.io"]}
  [fn:8]{◊<>["https://kafka.apache.org"]}
  [fn:9]{◊<>["https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers"]}
  [fn:10]{Let the buyer beware.}
  [fn:11]{◊<>["https://docs.ruby-lang.org/en/master/doc/ractor_md.html"]}
}
\ No newline at end of file

M posts/ruby-sorcery.org => posts/ruby-sorcery.org +2 -2
@@ 199,12 199,12 @@ If you recall earlier examples, I defined ~destructure_keys(*)~, which meant tha

-----

Well, this doesn't cover the entirety of Ruby's pattern matching fun, but it should at least show you the various things you're now able to do with the feature. If in doubt, RTFM◊^[1]; Ruby's documentation is absolutely fantastic.
Well, this doesn't cover the entirety of Ruby's pattern matching fun, but it should at least show you the various things you're now able to do with the feature. If in doubt, RTFM[fn:1]; Ruby's documentation is absolutely fantastic.

◊aside{Specifying 'rubydoc' in your Google searches should reveal Ruby's official documentation and not the SEO spam that is ApiDock.}

Check in soon to see another deep-dive into Ruby Sorcery.

◊footnotes{
  ◊^[1]{◊<>["https://docs.ruby-lang.org/en/3.0.0/doc/syntax/pattern_matching_rdoc.html"]}
  [fn:1]{◊<>["https://docs.ruby-lang.org/en/3.0.0/doc/syntax/pattern_matching_rdoc.html"]}
}

M posts/the-bookshelf.org => posts/the-bookshelf.org +8 -8
@@ 17,13 17,13 @@ Programming books are almost disappointingly over-represented as they're possibl
I added a few new books to the collection this year, and have been reading them as they arrive. Here's a quick run-through of what 2021 has looked like for me:


** The Phoenix Project◊^[1]
** The Phoenix Project[fn:1]

As far as Corporate Thrillers for Business Analysts go, The Phoenix Project easy enough to read and has some entertainment value. The cast is awash with wholly unsympathetic characters, the corporate setting oozes with toxicity, and it's impossible to turn a page without /someone/ being an asshole.

I didn't rate it and if its follow up /The Unicorn Project/ is any similar, then I think I'll give that one a pass. The Devops Handbook is still on my list, though.

** Extreme Ownership◊^[2]
** Extreme Ownership[fn:2]

I had to keep an open mind when starting this one, but eventually I gave up because I could no longer ignore what I was really feeling while reading this. Extreme Ownership glorifies the war in Iraq and the US's role in it and uses those experiences to teach you about accountability. 



@@ 33,13 33,13 @@ Let's be fair, the advice is sound if you can put the chest beating and the hoo-

This one just wasn't for me.

** Metro 2033◊^[3]
** Metro 2033[fn:3]

Like many people, my first intro to Metro 2033 was the game that was released a decade ago, and I've greatly enjoyed Artyom's journey from then up until the release of Metro Exodus. The post-apocalyptic setting is remarkable and I was sucked into the world in a way that Fallout could never manage. The stories feel more grounded in humanity and they're not quite as distracted by commentary about the excesses of capitalism.

I decided to revisit the world again this year, except this time by reading the books. I haven't finished this yet, as I've wanted to take my time with it. 

** Crucial Conversations◊^[4]
** Crucial Conversations[fn:4]

I revisit this book every now and then, although it has the dual purpose of selling consultation along with offering a taste of advice. Once you get used to the relentless sales pitch for Crucial Conversations™ you can actually take away a great deal of learning from this.



@@ 50,8 50,8 @@ It's the kind of book I'm happy to lend to someone without an expectation of the
Beyond these, I have both Dune and Systemic Coaching & Constellations on the backburner. I'm looking forward to getting stuck into both.

◊footnotes{
  ◊^[1]{◊<>["https://uk.bookshop.org/a/6865/9781942788294"]}
  ◊^[2]{◊<>["https://uk.bookshop.org/a/6865/9781250183866"]}
  ◊^[3]{◊<>["https://uk.bookshop.org/a/6865/9780575086258"]}
  ◊^[4]{◊<>["https://uk.bookshop.org/a/6865/9780071771320"]}
  [fn:1]{◊<>["https://uk.bookshop.org/a/6865/9781942788294"]}
  [fn:2]{◊<>["https://uk.bookshop.org/a/6865/9781250183866"]}
  [fn:3]{◊<>["https://uk.bookshop.org/a/6865/9780575086258"]}
  [fn:4]{◊<>["https://uk.bookshop.org/a/6865/9780071771320"]}
}
\ No newline at end of file

M posts/the-goose-is-out.org => posts/the-goose-is-out.org +9 -9
@@ 6,7 6,7 @@
:CATEGORY: personal
:END:

I was browsing HN over my coffee this morning and came across an interesting thread◊^[1] linking to a post titled /When Buddhism Goes Bad}◊^[2]. That wasn't actually the title I read, as on HN the more neutral subtitle -- ◊em{How My Mindfulness Practice Led Me To Meltdown/ -- was chosen instead.
I was browsing HN over my coffee this morning and came across an interesting thread[fn:1] linking to a post titled /When Buddhism Goes Bad}[fn:2]. That wasn't actually the title I read, as on HN the more neutral subtitle -- ◊em{How My Mindfulness Practice Led Me To Meltdown/ -- was chosen instead.

This is one of those posts I can relate to, at least in part, because of the experiences I've had myself. The thread on HN was itself interesting too, although ironically quite judgmental for a conversation about buddhism, meditation, and mindfulness. I'm not going to talk about those though--you can read those for yourself after all. Instead I'm going to offer my own perspective, now I can safely look back on the whole several-year long ◊del{ordeal}journey.



@@ 16,7 16,7 @@ Let's get one thing out of the way before I continue: yes, there are people out 

-----

For some reason I was reminded of an Osho story◊^[3] that was told at one of the retreats I went to. It was probably in one of the Osho books I read too but I couldn't tell you which one. It adapted the riddle of the goose in the bottle and turned it into an allegory of sorts.
For some reason I was reminded of an Osho story[fn:3] that was told at one of the retreats I went to. It was probably in one of the Osho books I read too but I couldn't tell you which one. It adapted the riddle of the goose in the bottle and turned it into an allegory of sorts.

#+begin_quote
If a man puts a gosling into a bottle and feeds him until it is fully grown, how can the man get the goose out without killing it or breaking the bottle?


@@ 26,7 26,7 @@ The story describes the obvious paradox of the situation: you can either break t

But that's not /really/ it, not for me anyway. Taking a grown-ass goose out of a glass bottle is difficult, but so is stuffing that goose back inside it. Geese are assholes; it's an easy way to get your ass kicked.

My journey took me through several therapists who specialised in the kind of approaches you won't find through the NHS. There was Reichian Breathwork◊^[4] at first, but then I mostly found specialists in Somatic Experiencing◊^[5]. At the same time I went on a few retreats that built their foundation on tantric practices◊^[6]. In a nutshell, basically a combination of meditative practice and therapy.
My journey took me through several therapists who specialised in the kind of approaches you won't find through the NHS. There was Reichian Breathwork[fn:4] at first, but then I mostly found specialists in Somatic Experiencing[fn:5]. At the same time I went on a few retreats that built their foundation on tantric practices[fn:6]. In a nutshell, basically a combination of meditative practice and therapy.

Had I not had the support of a therapist, a remarkably close friend, and all of my other closest frients, I would be another person writing about how meditation fucked me up--How Tantra Went Tits Up. I ended up in an absolutely /terrible/ state: depression out of control, suicidal, self-harming, and barely functional without breaking down or spending hours soaking my pillow with my face.



@@ 47,10 47,10 @@ I've more or less closed the book on that chapter of my life, but that's not to 
So, would I recommend this kind of thing to anyone? Honestly, no. It's not the kind of thing you casually recommend. Someone who wants to go on this journey will find the way that works for them, but that doesn't make it a decision to be taken lightly.

◊footnotes{
  ◊^[1]{◊<>["https://news.ycombinator.com/item?id=27890790"]}
  ◊^[2]{◊<>["https://danlawton.substack.com/p/when-buddhism-goes-bad"]}
  ◊^[3]{◊<>["https://oshostories.wordpress.com/2012/05/17/goose-is-out/"]}
  ◊^[4]{◊<>["https://en.wikipedia.org/wiki/Breathwork"]}
  ◊^[5]{◊<>["https://en.wikipedia.org/wiki/Somatic_experiencing"]}
  ◊^[6]{◊<>["https://en.wikipedia.org/wiki/Tantra"]}
  [fn:1]{◊<>["https://news.ycombinator.com/item?id=27890790"]}
  [fn:2]{◊<>["https://danlawton.substack.com/p/when-buddhism-goes-bad"]}
  [fn:3]{◊<>["https://oshostories.wordpress.com/2012/05/17/goose-is-out/"]}
  [fn:4]{◊<>["https://en.wikipedia.org/wiki/Breathwork"]}
  [fn:5]{◊<>["https://en.wikipedia.org/wiki/Somatic_experiencing"]}
  [fn:6]{◊<>["https://en.wikipedia.org/wiki/Tantra"]}
}

M posts/things-ive-changed-my-mind-on.org => posts/things-ive-changed-my-mind-on.org +4 -4
@@ 6,7 6,7 @@
:CATEGORY: programming
:END:

Earlier today I read a blog post titled /Software development topics I've changed my mind on after 6 years in the industry/◊^[1] and it made me reflect on how my own thinking has (hopefully) evolved over my decade long career. I'm not going to discuss the content of the linked post, except to say that as much as I empathise with the author and have been an angry programmer myself, the overly aggressive tone that occasionally slips out isn't really my cup of tea.
Earlier today I read a blog post titled /Software development topics I've changed my mind on after 6 years in the industry/[fn:1] and it made me reflect on how my own thinking has (hopefully) evolved over my decade long career. I'm not going to discuss the content of the linked post, except to say that as much as I empathise with the author and have been an angry programmer myself, the overly aggressive tone that occasionally slips out isn't really my cup of tea.

A lot of things can happen in ten years, and if nothing else my 30-something year old self feels substantially less enlightened than his more youthful counterpart. I look forward to reading this again in future and seeing what I think about it then.



@@ 18,7 18,7 @@ Corrollary: Having an answer to everything (or being overconfident) is a hard ha

*** A boring tech stack is nice, but you have to allow some scope for innovation if you don't want to stagnate.

One idea is to set up an innovation budget◊^[2], but it's not the only one.
One idea is to set up an innovation budget[fn:2], but it's not the only one.

*** PHP is a legitimate--if not /superior--option to start a new project with and it deserves another chance./



@@ 69,6 69,6 @@ Rejection isn't always so easy to handle, but there's always another time and a 
It feels a bit weird to write out a series of arbitrary maxims the way I just have, so I'll aim to explore each one in more depth throughout this year. With that said, it was an interesting exercise if ultimately a rather shallow one. Watch this space for more details.

◊footnotes{
  ◊^[1]{◊<>["https://chriskiehl.com/article/thoughts-after-6-years"]}
  ◊^[2]{◊<>["https://mcfunley.com/choose-boring-technology"]}
  [fn:1]{◊<>["https://chriskiehl.com/article/thoughts-after-6-years"]}
  [fn:2]{◊<>["https://mcfunley.com/choose-boring-technology"]}
}
\ No newline at end of file

M posts/to-simpler-times.org => posts/to-simpler-times.org +15 -15
@@ 6,9 6,9 @@
:CATEGORY: programming
:END:

I previously wrote about how this site was built◊^[1] and then deployed◊^[2]. I'm quite happy experimenting with how I set up this whole shebang because I can learn a lot from it and the worst that happens is that the site goes down for half an hour. The stakes are low.
I previously wrote about how this site was built[fn:1] and then deployed[fn:2]. I'm quite happy experimenting with how I set up this whole shebang because I can learn a lot from it and the worst that happens is that the site goes down for half an hour. The stakes are low.

You might tell from the design that I'm trying to keep things basic. The most outrageous things on the entire site are two SVG icons, a CSS animation, an embedded font, and poor mobile responsiveness. Oh, and one added script for site stats that you are able to look at yourself◊^[3] (I just want to see which posts get more traction than others, is all).
You might tell from the design that I'm trying to keep things basic. The most outrageous things on the entire site are two SVG icons, a CSS animation, an embedded font, and poor mobile responsiveness. Oh, and one added script for site stats that you are able to look at yourself[fn:3] (I just want to see which posts get more traction than others, is all).

The thing about the build and deploy process, and I was well aware of it at the time, is that it is wildly overcomplicated. In fact, I imagine a lot of our new processes are more complicated than they need to be, as cloud providers and SaaS compete for developer mindshare and hook people into various novel solutions to highly specific problems.



@@ 48,7 48,7 @@ It's not the worst thing ever, except that if I switch to a different computer, 

So, back to basics!

The site has moved once again, back to a VPS hosted somewhere in the UK. Caddy◊^[4] is doing the hard work as an elegant alternative to nginx or apache and for simple setups you can't really go wrong with it if you just want a server with HTTPS by default. Here's how I configured this site:
The site has moved once again, back to a VPS hosted somewhere in the UK. Caddy[fn:4] is doing the hard work as an elegant alternative to nginx or apache and for simple setups you can't really go wrong with it if you just want a server with HTTPS by default. Here's how I configured this site:

#+begin_src caddy
  kamelasa.dev {


@@ 61,11 61,11 @@ The site has moved once again, back to a VPS hosted somewhere in the UK. Caddy
  }
#+end_src

Deploying to this server is a case of firing off a couple of ~ssh~, ~scp~ or ~rsync~ requests using a separate user with its own SSH key, and as soon as the command is finished running the changes are visible online.◊^[5]
Deploying to this server is a case of firing off a couple of ~ssh~, ~scp~ or ~rsync~ requests using a separate user with its own SSH key, and as soon as the command is finished running the changes are visible online.[fn:5]

This leads me to the final bit. Modern tech feels more complicated as it tends towards distributed solutions: put thing /x/ here, deploy service /y/ there, sync them up with webhooks, and hope the network holds up to the task. Earlier tech feels more complicated because the documentation is intricate and detailed and requires some fidgeting around with.

It took me just about a day to figure out how to host my own ~apt~ repository for Debian◊^[6], compiling information from various manuals, blog posts and examples. It was mostly a case of creating a GPG key and setting up a correct directory structure for ~apt-ftparchive~◊^[7] to do its business, with a little bit of extra config. I'll go into detail about that another time, but let it be said it does the job tremendously in any Debian-based CI pipeline.
It took me just about a day to figure out how to host my own ~apt~ repository for Debian[fn:6], compiling information from various manuals, blog posts and examples. It was mostly a case of creating a GPG key and setting up a correct directory structure for ~apt-ftparchive~[fn:7] to do its business, with a little bit of extra config. I'll go into detail about that another time, but let it be said it does the job tremendously in any Debian-based CI pipeline.

#+begin_src bash
  cd www.kamelasa.dev


@@ 75,16 75,16 @@ It took me just about a day to figure out how to host my own ~apt~ repository fo

-----

On another note, this site now also left GitHub for Sourcehut◊^[8] and, at risk of being a bit narcissistic, a comments section lives on a mailing list there◊^[9]. Should you feel that the stuff I post is worth talking about, of course. You don't need a Sourcehut account to get involved, although you'll need to join the list (without signing up for Sourcehut) if you want more than read-only access.
On another note, this site now also left GitHub for Sourcehut[fn:8] and, at risk of being a bit narcissistic, a comments section lives on a mailing list there[fn:9]. Should you feel that the stuff I post is worth talking about, of course. You don't need a Sourcehut account to get involved, although you'll need to join the list (without signing up for Sourcehut) if you want more than read-only access.

◊footnotes{
  ◊^[1]{◊<>["https://www.kamelasa.dev/programming/blogging-in-haskell"]}
  ◊^[2]{◊<>["https://www.kamelasa.dev/programming/hakyll-on-devops-pipelines"]}
  ◊^[3]{◊<>["https://plausible.io/kamelasa.dev"]}
  ◊^[4]{◊<>["https://caddyserver.com/v2"]}
  ◊^[5]{◊<>["I should probably sort out proper HTTP caching though..."]}
  ◊^[6]{◊<>["https://pkg.kamelasa.dev"]}
  ◊^[7]{◊<>["https://manpages.debian.org/buster/apt-utils/apt-ftparchive.1.en.html"]}
  ◊^[8]{◊<>["https://sourcehut.org"]}
  ◊^[9]{◊<>["https://lists.sr.ht/~mrlee/kamelasa.dev-discuss"]}
  [fn:1]{◊<>["https://www.kamelasa.dev/programming/blogging-in-haskell"]}
  [fn:2]{◊<>["https://www.kamelasa.dev/programming/hakyll-on-devops-pipelines"]}
  [fn:3]{◊<>["https://plausible.io/kamelasa.dev"]}
  [fn:4]{◊<>["https://caddyserver.com/v2"]}
  [fn:5]{◊<>["I should probably sort out proper HTTP caching though..."]}
  [fn:6]{◊<>["https://pkg.kamelasa.dev"]}
  [fn:7]{◊<>["https://manpages.debian.org/buster/apt-utils/apt-ftparchive.1.en.html"]}
  [fn:8]{◊<>["https://sourcehut.org"]}
  [fn:9]{◊<>["https://lists.sr.ht/~mrlee/kamelasa.dev-discuss"]}
}

M posts/using-ruby-c-in-ruby.org => posts/using-ruby-c-in-ruby.org +6 -6
@@ 8,7 8,7 @@

A thought occurred to me in my mask-wearing, lockdown-addled brain last night: why the hell did I choose /now/ to stop drinking? It's for my own good, I told myself, and so my thoughts shifted further into the absurd with nary a mind-altering substance in sight to stop them.

One of those thoughts stuck out in particular, because of how ridiculous it sounded: could you optimise your Ruby code by using FFI with Ruby's C bindings? I'm not talking about making a native extension in pure C, I'm talking about making Ruby talk to itself through a foreign function interface using the ffi gem◊^[1].
One of those thoughts stuck out in particular, because of how ridiculous it sounded: could you optimise your Ruby code by using FFI with Ruby's C bindings? I'm not talking about making a native extension in pure C, I'm talking about making Ruby talk to itself through a foreign function interface using the ffi gem[fn:1].

Let's apply some method to this madness and set up some bindings, otherwise we're dead in the water. Let's be descriptive and call our FFI module ~LibRuby~. No naming conflicts at all there, /no sirree/!



@@ 41,7 41,7 @@ Anyway this boilerplate should give us enough to do a hello world using Ruby's C
  It should go without saying that at this point, you're not just playing with fire, you're inviting it to burn down your house. Be careful lest the `segfault`s creep up on you.
}

Let's take it from the top and talk through this ungodly incantation. Go ahead and copy that little module into your console! If it fails, make sure you've got the ~ffi~ gem installed◊^[2].
Let's take it from the top and talk through this ungodly incantation. Go ahead and copy that little module into your console! If it fails, make sure you've got the ~ffi~ gem installed[fn:2].

Once you're done, you can save some keystrokes by importing that module.



@@ 75,7 75,7 @@ Now we have all of the ingredients to make the actual call, which syntactically 
  rb_funcall(kernel, puts_method, 1, :value, str)
#+end_src

Run it again a few times, and with different strings. If you're feeling experimental, attach more functions in ~LibRuby~ and see what else you can print out! Ruby's extension documentation should be a good place to start◊^[3].
Run it again a few times, and with different strings. If you're feeling experimental, attach more functions in ~LibRuby~ and see what else you can print out! Ruby's extension documentation should be a good place to start[fn:3].

*** So, why disable the GC?



@@ 90,7 90,7 @@ How would you fix it? Well, now we've found out that we ~can~ write Ruby with it
Until then, I'll see you further into the abyss.

◊footnotes{
  ◊^[1]{◊<>["https://github.com/ffi/ffi"]}
  ◊^[2]{~gem install ffi -- --enable-system-libffi~}
  ◊^[3]{◊<>["https://ruby-doc.org/core-2.7.0/doc/extension_rdoc.html"]}
  [fn:1]{◊<>["https://github.com/ffi/ffi"]}
  [fn:2]{~gem install ffi -- --enable-system-libffi~}
  [fn:3]{◊<>["https://ruby-doc.org/core-2.7.0/doc/extension_rdoc.html"]}
}