tag:pb.co.za,2013:/posts Things. 2024-04-08T20:14:03Z Pieter Breed tag:pb.co.za,2013:Post/2102091 2024-04-08T10:25:16Z 2024-04-08T20:14:03Z Overloading gen-class methods of glorious types with sublime nation of clojure for make great benefit of interop

(Post title shenanigans alert!)

This is a quick note for a few seemingly-simple issues about how clojure interops with java. And how I was surprised by it. Again. I'm also hoping it's useful and/or informative.

With clojure you can create "java" classes using the gen-class macro. With this macro you may construct a class in clojure, specify which interfaces it implements, what state it has and so on. Read more about gen-class at clojuredocs. It's not used often, but it can be necessary in some cases.

I encountered a few problems for which the solutions were not obvious and decided to collect it here in the form of examples.

Creating context; Most basic gen-class example

  • We have one interface, IThing which we will implement in clojure.
  • We have one java class ThingUser.
  • ThingUser uses an instance of IThing.
  • We will instantiate ThingUser from clojure, and give it our implementation of IThing.

To start with; Notice that IThing has two overloads defined on doWithThing1. Each takes a different number of parameters. (I'll use doWithThing2 and doWithThing3 lower down in this article.)

In package test;

public interface IThing {
    void doWithThing1(String a, String b);
    void doWithThing1(String a, String b, String c);

    void doWithThing2(String a, String b);
    void doWithThing2(String a, Integer b);
    void doWithThing2(Integer a, byte[] b);

    default void doWithThing3(String a, String b) {
        doWithThing2(a, b);


public class ThingUser {
    public void doThingOneWithTwo(IThing thing) {
        thing.doWithThing1("A", "B");
    public void doThingOneWithThree(IThing thing) {
        thing.doWithThing1("A", "B", "C");

In test/thingi.clj:

(ns test.thingi
   [test IThing ThingUser]))

(gen-class :name "test.thingi.TestImpl"
           :prefix "impl-"
           :main false
           :implements [test.IThing])

(defn impl-doWithThing1
  ([this a b c]
   (println (str "a: " a 
                 ", b: " b 
                 ", c: " c)))
  ([_this a b]
   (println (str "a: " a 
                 ", b: " b))))

Then in the repl evaluate this: (compile 'test.thingi). At this point everything is ready for the first test.

(let [impl (test.thingi.TestImpl.)
        user (ThingUser.)]
    (.doThingOneWithTwo user impl)
    (.doThingOneWithThree user impl))

a: A, b: B
a: A, b: B, c: C


  • The interface method arity is matched to the correct clojure function arity.
  • We are not "implementing" all of the interface methods on the clojure side yet no errors result.
  • (compile ...) creates a stub .class by using the bytecodes API. This class is loaded once it is referenced in clojure.
  • You can change the clojure code without having to restart the JVM. The stub methods use "reflection" to find the correct clojure functions.
  • By default the class files are only loaded once per JVM. When the class file has to meaningfully change the JVM has to be restarted. There are tools that remove some of this friction.

same arity overloads

In IThing we are now looking at doWithThing2. Add new java class ThingUser2:

public class ThingUser2 {
    public void doThingTwo(IThing thing, String a, String b) {
        thing.doWithThing2(a, b);
    public void doThingTwo(IThing thing, String a, Integer b) {
        thing.doWithThing2(a, b);
    public void doThingTwo(IThing thing, Integer a, byte[] b) {
        thing.doWithThing2(a, b);

In test/thingi.clj add:

(defn impl-doWithThing2
  [_ a b]
  (println (str "a[" (type a) "]: " a "\n"
                "b[" (type b) "]: " b)))

Then eval:

  (let [impl (test.thingi.TestImpl.)
        user (ThingUser2.)]
    (.doThingTwo user impl "a" "b")
    (.doThingTwo user impl "a" (int 66))
    (.doThingTwo user impl (int 65) ^bytes (.getBytes "b")))

I get this result:

a[class java.lang.String]: a
b[class java.lang.String]: b

a[class java.lang.String]: a
b[class java.lang.Integer]: 66

a[class java.lang.Integer]: 65
b[class [B]: [B@3ea69f14


  • Regardless of which type arity overload you call, it will find the same function on the clojure side.
  • Your logic will (probably) require some type inspection on the parameters.
  • If the interface you implement gets a new overload with parameter types you did not anticipate you might get in trouble. Code defensively for this.

Alternative for overloads differing on parameter types

There is a secret (implementation detail) in the clojure compiler that allows you to create specific functions for gen-class methods, when the overloads differ only in the types of the parameters.

;; My recommendation is not to mix the two styles of implementation
;; (defn impl-doWithThing2
;;   [_ a b]
;;   (println (str "a[" (type a) "]: " a "\n"
;;                 "b[" (type b) "]: " b)))

(defn impl-doWithThing2-String-String
  [_ ^String a ^String b]
  (println "impl-doWithThing2-String-String: a=" a ", b=" b))

(defn impl-doWithThing2-String-Integer
  [_ ^String a ^Integer b]
  (println "impl-doWithThing2-String-Integer a=" a ", b=" b))

(defn impl-doWithThing2-Integer-byte<>
  [_ ^Integer a ^bytes b]
  (println "impl-doWithThing2-Integer-byte<> a=" a ", b=" (String. b)))

With the same test in the repl:

(let [impl (test.thingi.TestImpl.)
        user (ThingUser2.)]
    (.doThingTwo user impl "a" "b")
    (.doThingTwo user impl "a" (int 66))
    (.doThingTwo user impl (int 65) ^bytes (.getBytes "b")))

I get:

impl-doWithThing2-String-String: a= a , b= b
impl-doWithThing2-String-Integer a= a , b= 66
impl-doWithThing2-Integer-byte<> a= 65 , b= b


  • The short names of the parameter types are taken, interleaved with -, and appended to the function name to specify the specific overload that is being defined.
  • The short-name of an array is type[], for example int[], byte[] &c. [ and ] are special characters in clojure and cannot be used for the name of functions. They become <> in this case.
  • In my opinion mixing the two ways of defining the implementation will most likely lead to bad results so I recommend you choose between inspecting the types at runtime, or specifying multiple functions one for each overload.

Default interface implementations

In java you can add a default implementation to an interface, which allows you to add backwards compatible updates to an interface. However ... (foreboding)

One more java class:

package test;

public class ThingUser3 {
    public void doThingThree(IThing thing, String a, String b) {
    thing.doWithThing3(a, b);

Then I run this in the repl:

  (let [impl (test.thingi.TestImpl.)
        user (ThingUser3.)]
    (.doThingThree user impl "a" "b"))

For me, on clojure version 1.11.1 this fails with:

1. Unhandled java.lang.UnsupportedOperationException
   doWithThing3 (test.thingi/impl-doWithThing3 not defined?)

           ThingUser3.java:    5  test.ThingUser3/doThingThree
                      REPL:   55  test.thingi/eval11229
                      REPL:   53  test.thingi/eval11229

Whoops! There is a bug in the clojure compiler where it will implement doWithThing3 on the stub implementation and then look for the impl- function and not find it and then give this error.

This is on the clojure team's radar for maybe inclusion in 1.13.

I found this in kafka client code. We implement org.apache.kafka.common.serialization.Deserializer which in the most recent release (3.6) received a new interface method with a default implementation. If you do the same in clojure you will probably find it sooner or later too.

// old interface method
deserialize(String topic, Headers headers, byte[] data)
// new interface method overload
deserialize(String topic, Headers headers, ByteBuffer data)

What was a backwards compatible change in other JVM languages was a breaking change in clojure.


  • I suspect the technique of runtime-type-inspecting the arguments is probably the one you are "supposed" to be using.
  • The mechanism for naming a function after the types of parameters was added for a reason (which can only be guessed at).
  • For better or for worse people (like myself) now depend on it.
  • This mechanism is considered an implementation detail and the clojure team does not promise not to break it in the future.
  • For particularly hairy interop situations, you are encouraged to use Java implementations. (I don't have a source reference for this)


Pieter Breed
tag:pb.co.za,2013:Post/1999926 2023-07-14T15:21:25Z 2023-07-15T09:08:29Z keyboards and web apps, my post/rant for the year

Did you know?

The keyboard as a concept for operating a machine was around for at least 35 years before the mouse came to be. And I'm actually talking nonsense, because it was longer than that. (quick mafs)

And yet.

And yet.

Building a web application to be used by a mouse is easy, reliable and expected. Building a web application so that it can be driven by a keyboard is at best a character building affair.

If you are a web developer in 2023 and your boss/friend/archenemy sweetly asks you to Add Keyboard shortcuts to your company's web app, tell them/they to go f**k themselves do it themselves instead.

Here's a short list of things that will mess with your self-confidence as a developer along the way:

  • Most npm libraries that "deal with" keyboard events use the old and deprecated keyCode API. This API gives you a number to represent the key that was pressed. 38, 38, 40, 40, 37, 39, 37, 39, 66, 65.
  • There is a new API that will tell you what key was pressed by using a set of well-define strings: ["ArrowUp", "ArrowUp", "ArrowDown", "ArrowDown", "ArrowLeft", "ArrowRight", "ArrowLeft", "ArrowRight", "KeyB", "KeyA"].
    • This is cool. I approve of this. 🫶
    • This is (only somewhat) useful to you if you want to create keyboard shortcuts that are based on what motion your hand makes as they type those shortcuts.
  • Part of the new API is the attribute called "key", which lets you know what character was produced by the keyboard, by the operating system, by the keyboard layout in the operating system, and by the browser inside this operating system. To quote from the spec:
    The "Digit2" and "KeyQ" keys are writing system keys that generate "2" and "q" when the US locale is active and "é" and "a" when the French locale is active.
  • Keyboard layouts exist:
    • Keyboard layouts are a software feature provided by your operating system, that makes a different character come out from the software than the one that was pressed by your fingers.
    • People in other countries (not the US) speak languages with characters that cannot be found on a US keyboard.
    • They prefer to use their keyboards to type words that are not in an English dictionary. (French, Greek, Hebrew)
    • These are innocently called International Keyboard Layouts.
    • Some nerds like to use US keyboards, but remap the keys to mean something different than what is printed on them. (Dvorak, Colemak)
    • Some nerds build their own keyboards 😱 and I promise you you have no idea what will happen when they press a key on it.
  • There is a javascript API that will tell you what character will come out of the mess that is a modern software stack when a specific, named key is pressed.
    • Just don't have users who use Firefox ...
    • or Safari.
  • There is NOT an API that will tell you which key must be pressed in order to produce a specific character. (As an implementer of technologies, I understand, but as a user of technologies, this seems silly)
  • Keyboards have things called "Modifier keys" on them: Alt, Shift, Control, Meta and some less well-known others AltGr, Hyper, Super, Fn
    • Those things modify the meaning of the key that is pressed. Typically you hold down one (or more) of these keys while simultaneously pressing another key.
    • Ctrl-S (means save in most places), Ctrl-Z (undo)
    • Shift-a (means A in most places) etc.
    • Most operating systems have opinions about what those modifier keys might be used for.
    • On Windows, you cannot use Alt-F4 as a keyboard shortcut for anything. Meta-l (Windows L) is also usually off-limits.
    • On MacOSX, pressing the Alt key changes the character of the key being pressed: Alt-J -> Δ
    • On linux, with some window managers, Alt is reserved for dragging a window around. On other linuxes (linuxii?) anything else is possible.
  • "Keyboard events" come in flavours: KeyDown, KeyPress & KeyUp
    • If you hold a key down, it is the KeyDown event that is repeated. This is useful for panning, zooming or adding horizontal whitespace. (just kidding, do you think I'm a barbarian?!)
    • The KeyPress event comes between the KeyDown and the KeyUp event. Sometimes.
    • KeyUp is a decent choice for creating keychords with. (A keychord is a sequence of key presses that together triggers some action)
    • On MacOSX, when you hold down the Meta key, KeyUp's sometimes/often don't fire.
  • These things exist:
    • Compose keys; quoting from Wikipedia:
      A compose key (sometimes called multi key) is a key on a computer keyboard that indicates that the following (usually 2 or more) keystrokes trigger the insertion of an alternate character, typically a precomposed character or a symbol. For instance, typing Compose followed by ~ and then n will insert ñ.
    • Dead keys:
      A dead key is a special kind of modifier key on a mechanical typewriter, or computer keyboard, that is typically used to attach a specific diacritic to a base letter.
    • Alt-codes (on Windows, I believe). Example: In Afrikaans words such as these exist: wêreld & sēening. To produce those characters on a Windows PC you hold down Alt and press in sequence 136 or 137, as the case may be.
    • There's more in this category, but these are the ones I myself have used.
  • Languages that have nothing to do with ancient Rome and Latin and of which I know nothing:
    • 한글 (Korean)
    • にほん · 日本 (Japan)
    • 汉字 (China)
    • עִברִית (Israel)
    • हिंदी or தமிழ் or മലയാളം (merely 3 picked at random from Indian subcontinent)

    • Ask yourself how people who speak (and type!) in these languages use computers created by white, English-speaking men.
To get back on point. Keyboards are hard. Mice are easy. Even web applications that get it mostly right, also get it really wrong, sometimes. (❤️❤️ linear.app ❤️❤️)

If you are in this situation, I have two pieces of advice:

  • Don't. Go home. Have a beer. Kiss your favourite person. Become a carpenter, be free, roam the land. Counting is hard.
  • Are you making games?
    • Use physical keyboard codes and use the getLayoutMap() to indicate to your gamers which characters to try to type to go up or down or left or right.
    • Stick to chrome.
  • Are you making productivity software?
    • Use mnemonics for keyboard shortcuts.
    • Use key sequences for keyboard shortcuts.
    • Avoid modifiers, especially Shift, Alt & Meta
    • Ask for a raise.

Pieter Breed
tag:pb.co.za,2013:Post/1862683 2022-08-01T15:20:54Z 2022-08-01T15:23:32Z HOWTO set up your programming environment for great fun and glorious profit
I made some notes about how to start an open-source infrastructure-as-code project. Here is the first bit of that in blog form. This post deals with step-0 decisions on how to set up a new programming project, managing the programming language tools, their associated version sprawl and environment variables across different projects or directory trees.

tl;dr -- Introducing the problem for which direnv and asdf-vm is the fix.

What's going on?

Every chicken has an egg problem and every egg has a chicken problem. -- Somebody

The C in IAC stands for Code. It is software and it needs to run or execute. With this interpretation of things something like terraform is an interpreter and it executes *.tf source code files. These files are structured internally in hcl format, which is a type of json. terraform is therefore a programming language that executes json code/data against your cloud fabric.

Every software project targets a specific runtime explicitly and sometimes implicitly. This runtime contains the implementation of the programming language your code is based on, and also the accumulated changes and incompatibilities between released versions. As with any software, updates invalidate assumptions about the program behaviour that nobody knew they held.

Running the code against the wrong version of the tool may result in unknown-unknowable and unexpected problems and error states, so maintaining the execution runtime is important for engineer/operator happiness. Future-you will be happy not to deal with both unexpected upgrade maintenance and regular urgent maintenance at the same time.

This problem exists in programming projects in general. Your specific runtime might be the version of build-essentials that you use, or something else like babashka, terragrunt, ruby, python, ansible, clojure, java etc &c. The point is that as an operator you need to run the software in the git repository against the version(s) of the software it was developed with.

A compounding factor is that you might be working on multiple different codebases at the same time and it's possible and sometimes likely that every repository has its own idea of what version of each tool is required.

We want to stay in the pit of success.

One way to solve this meta-problem is to rely on a tool that will install other tools. This is what a package manager does. Your operating system (ubuntu or arch or something) has a package manager, but usually package managers want to provide you with the version of every package it manages. What you as a software engineer require is a package manager that, 1) doesn't piss off your main package manager and, 2) still allows you to have multiple versions of the multiple tools within easy reach in our work environment. The default system-provided package manager is not good enough, we need something else.

To expand on this a little; Package managers like brew and apt do not understand the concept of having multiple versions of one tool around at the same time. (python and python2 and python3 and python-is-python-2 is dumb...). Yet as a developer you might still have to target both AWS lambda and GCP Cloud functions in one workday. Each of these require a different version of python.

It is to solve this problem that tools like rbenv for ruby and pyenv for python exist. Those tools allow you to have multiple versions of the same tool installed (without requiring sudo...) and make different ones of those available depending on which directory your bash session or process currently is in.

But what about bash or nodejs? What about <insert favourite programming language here>? It is unlikely that every programming community has this custom made tool to solve this problem for its programming environment runtime.

Luckily we can solve the problem by having another standard tool... with plugins for every versioned programming environment you might require.

asdf, that one tool to rule all other tools and their versions

Let's set ourselves up for success.

If you have `asdf` installed, you can use it to install specific versions of programming languages into your programming environment and manage this with a special file called .tool-versions. When your shell, bash or zsh, encounters this file it activates the version of each tool specified in the file.


$ cat .tool-versions  
terraform 1.2.3
terragrunt 0.38.2
babashka 0.8.157

$ asdf install
babashka 0.8.157 is already installed
terraform 1.2.3 is already installed
terragrunt 0.38.2 is already installed

$ terragrunt --version \
&& terraform --version \
&& bb version
terragrunt version v0.38.2
Terraform v1.2.3
on linux_amd64

Your version of Terraform is out of date! The latest version
is 1.2.5. You can update by downloading from https://www.terraform.io/downloads.html
babashka v0.8.157

Oh nice! Look at that friendly warning about another new version of something you need to depend on... /s

So I open nano and change .tool-versions

cat .tool-versions
terraform 1.2.5
terragrunt 0.38.2
babashka 0.8.157

$ asdf install
babashka 0.8.157 is already installed
terraform --version
Terraform v1.2.5
on linux_amd64

Downloading terraform version 1.2.5 from https://releases.hashicorp.com/terraform/1.2.5/terraform_1.2.5_linux_amd64.zip
Verifying signatures and checksums
gpg: keybox '/tmp/asdf_terraform_GLCZwo/pubring.kbx' created
gpg: /tmp/asdf_terraform_GLCZwo/trustdb.gpg: trustdb created
gpg: key 34365D9472D7468F: public key "HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg: Signature made Wed 13 Jul 2022 12:23:52 SAST
gpg:                using RSA key 374EC75B485913604A831CC7C820C6D5CD27AB87
gpg: Good signature from "HashiCorp Security (hashicorp.com/security) <security@hashicorp.com>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: C874 011F 0AB4 0511 0D02  1055 3436 5D94 72D7 468F
    Subkey fingerprint: 374E C75B 4859 1360 4A83  1CC7 C820 C6D5 CD27 AB87
terraform_1.2.5_linux_amd64.zip: OK
Cleaning terraform previous binaries
Creating terraform bin directory
Extracting terraform archive
terragrunt 0.38.2 is already installed

$ terraform --version
Terraform v1.2.5
on linux_amd64

Problem solved.

(OK - I admit, the gpg warning seems like a problem that requires solving too, but that problem exists on a different plane of problems than the one I'm writing so much about.)

direnv and .envrc

Once more.

You actually have this problem in general. There are tools in the world that engineers and operators use that depend on specific environment variables being set, and being set to some specific values on a per-project basis. Sometimes you want to have the same variable with different values depending on the directory even within the same project. terragrunt is like this.

One example is having the value of AWS_PROFILE=company-prod-account-profile-name in one specific directory tree (eg for .../terragrunt/prod/cdn/terragrunt.hcl) in your terragrunt project. Then in another directory tree in the same project (eg .../terragrunt/dev/cdn/terragrunt.hcl) you can set AWS_PROFILE=company-dev-account-profile-name.

This allows you to deploy to dev environment or prod environment in the same bash session/software-ide-project, WITHOUT needing to maintain secrets in the open. IE You can use your aws configure and set up $HOME/.aws/config and $HOME/.aws/credentials to contain credentials for as many accounts or clients or projects as you require and use specific credentials only in certain directory trees and their subtrees.

$ nano .envrc
direnv: error /path/to/iaac/prod/.envrc is blocked. Run `direnv allow` to approve its content                                                                               
$ cat .envrc
export AWS_PROFILE=company-prod-account

$ direnv allow
direnv: loading ~/path/to/iaac/prod/.envrc
direnv: export +AWS_PROFILE

$ env | grep -i aws

$ cd `mktemp -d` # other directory somewhere
direnv: unloading

$ echo 'export AWS_PROFILE=company-dev-account' > .envrc
direnv: error /tmp/tmp.ZFPF6NsWAD/.envrc is blocked. Run `direnv allow` to approve its content

$ direnv allow
direnv: loading /tmp/tmp.ZFPF6NsWAD/.envrc
direnv: export +AWS_PROFILE

$ env | grep -i aws

Problem solved.

Use the same for any other environment variable that may configure any piece of kit that follows the 12-factor app design.

Other variables I like to set like this: AWS_ACCOUNT_ID, VAULT_TOKEN, VAULT_HTTP_ADDR, SMTP_FROM_ADDRESS &c.

## emacs

In your $HOME/.emacs.d/init.el, using quelpa and use-package together:

(use-package quelpa
  (setq quelpa-upgrade-interval 7)
  (add-hook #'after-init-hook #'quelpa-upgrade-all-maybe))

(require 'quelpa-use-package)

(use-package direnv
  :config (direnv-mode))

(use-package asdf-vm
  :quelpa (asdf-vm :fetcher github :repo "delonnewman/asdf-vm.el"))
(require 'asdf-vm)

vim: direnv & asdf.
vscode:direnv. (I could not find a vscode plugin for asdf-vm)


Now that we know how to pin tool versions and environment variables to specific directories we can actually start to set up a infrastructure as code project. More about this next time.
Pieter Breed
tag:pb.co.za,2013:Post/1690660 2021-05-14T15:19:43Z 2021-05-14T15:19:43Z Hello my name is Pieter and …

… today I jumped off of a perfectly good bridge.

Pieter Breed
tag:pb.co.za,2013:Post/1663575 2021-03-09T21:45:29Z 2021-03-09T21:45:30Z Sorry, my two 9’s of uptime ran out around 17:00
Pieter Breed
tag:pb.co.za,2013:Post/1566388 2020-06-29T07:52:11Z 2020-06-29T07:52:11Z Unix rules for life
  • Keep it simple: It's cheaper and easier to carry around.
  • Do one thing at a time: Multitasking is a lie.
  • Network: You were born to connect.
  • Say what you mean; nothing is truer than the truth.
  • Hack: Trial and error is the only way we learn anything
  • Be who you are: Even a bent wire can carry a great light.
  • Use leverage; a bigger hammer isn't always the answer.
  • Use what you have: never dig diamonds with a brick of gold.
  • Have faith; all's possible, except maybe skiing through a revolving door.
  • Think ahead, but don't worship your plans; remember today is the first day of the rest of your learning experience.
Pieter Breed
tag:pb.co.za,2013:Post/1329037 2018-10-04T20:26:31Z 2018-10-04T20:26:31Z riddle me this, riddle me that

I have three siblings. All of my brothers are siblings. 2 of my siblings have two brothers each. One of my brothers only has me as a sibling.

Pieter Breed
tag:pb.co.za,2013:Post/1323184 2018-09-19T11:56:22Z 2018-10-10T21:29:48Z devopsdays 2018 speaking engagement (now with more prosperity)

I've been invited to speak at devopsdays 2018 Cape Town on the 20th of September 2018.

My talk will be about Hashicorp's nomad, a job schedule. 

Here are my conference slides for future posterity.


I'm adding a link to the source code of the slides on here too.

Seriously. Please give the slides a look so what I'm about to say makes sense, it'll take you 2 seconds. The link takes you to a single-page html/javascript app that I use whenever I have to present ideas to groups of people. I run that code on my laptop and connect an HDMI for the overhead projector...

I was in a serious technical conversation today and I took an argumentative position that clojure/script is a pragmatic implementation language for real software projects by real developers, engineers and devopses, facing real project deadlines. My basic argument is that if you use clojure/script, you need fewer lines of code to achieve a specific result than you'd need if you used some other language. That fewer lines of code means there are fewer things to comprehend or mentalise while you are busy doing maintenance work on that code. Also, fewer lines of code, means fewer logical places where things can go wrong.

The counter argument is that syntax sometimes aids in readability. I don't know about that, to be honest. If you can get by without syntax, then you're left with the meaning behind the code. And if you express the meaning meaningfully, then you get a piece of code that's a delight to work with.

(PS/rant - I hate traditional slide-deck software. I'd rather edit a piece of code to build my slide content than dick around with a mouse and icons I can barely see.)

Pieter Breed
tag:pb.co.za,2013:Post/1322908 2018-09-18T13:42:52Z 2018-09-18T13:42:52Z I did a thing. F2004 F1 Ferrari around Nurburgring Nordschleife in < 5 minutes ]]> Pieter Breed tag:pb.co.za,2013:Post/1163654 2017-06-13T20:20:38Z 2017-06-13T20:21:20Z Desmodronic Valve System from Ducati motorbikes. It's a beautiful thing.

Pieter Breed
tag:pb.co.za,2013:Post/1147376 2017-04-18T17:43:56Z 2018-10-10T22:23:48Z House of Cards, SA edition

After reading this article.

Pieter Breed
tag:pb.co.za,2013:Post/1146995 2017-04-17T08:45:22Z 2017-04-17T08:45:22Z Happiness is in Being OK with the world without deciding how it must be

You got the kind of control you wanted. That’s to say where you had a loving relationship with the world, but you didn’t have to make up your mind as to what is should do. You let it decide.

Alan Watts – Master of Control on the youtubes.

Pieter Breed
tag:pb.co.za,2013:Post/1146229 2017-04-13T17:41:04Z 2017-04-13T17:41:04Z Sometimes you are being Frodo and Sam


Yesterday my friend and colleague Husayn had to write Yoda on the whiteboard in the office and he spelled it with a J, like in Joda. He is from Cameroon. As a group of engineers, we realized Husayn has never seen any of the Star Wars movies.

Screen Shot 2017-04-13 at 19.15.52Screen Shot 2017–04–13 at 19.15.52

Later this led to a conversation about the book and the story of Lord of the Rings by J.R.R Tolkien, the other nerd classic and I asked whether he’s read any of those books. He hasn’t, despite being a voracious consumer of books.

I tried to relate how The Hero’s Journey relates to Frodo (and to us) by pointing out how our current situation on the path to The Release of Software into PROD looks exactly like the scene in The Lord of the Rings where Frodo and Sam are getting into the whole Spider thing in the middle section of the story.

This is what Wikipedia has to say about the book. I’m pasting/editing it here, because I believe it to be material.

The Lord of the Rings

From Wikipedia., the free encyclopedia

This article is about the novel.

The title of the novel refers to the story’s main antagonist, the Dark Lord Sauron, who had in an earlier age created the One Ring to rule the other Rings of Power as the ultimate weapon in his campaign to conquer and rule all of Middle-earth. From quiet beginnings in the Shire, a hobbit land not unlike the English countryside, the story ranges across Middle-earth, following the course of the War of the Ring through the eyes of its characters, not only the hobbits Frodo Baggins, Samwise “Sam” Gamgee, Meriadoc “Merry” Brandybuck and Peregrin “Pippin” Took, but also the hobbits’ chief allies and travelling companions: the Men Aragorn son of Arathorn, a Ranger of the North, and Boromir, a Captain of Gondor; Gimli son of Glóin, a Dwarf warrior; Legolas Greenleaf, an Elven prince; and Gandalf, a Wizard.

The Lord of the Rings has [since] been reprinted numerous times and translated into 38 languages.

Tolkien’s work has been the subject of extensive analysis of its themes and origins. Although a major work in itself, the story was only the last movement of a larger epic Tolkien had worked on since 1917, in a process he described as mythopoeia. Influences on this earlier work, and on the story of The Lord of the Rings, include philology, mythology, religion and the author’s distaste for the effects of industrialization, as well as earlier fantasy works and Tolkien’s experiences in World War I. These inspirations and themes have often been denied by Tolkien himself. The Lord of the Rings in its turn is considered to have had a great effect on modern fantasy; the impact of Tolkien’s works is such that the use of the words “Tolkienian” and “Tolkienesque” have been recorded in the Oxford English Dictionary.

The enduring popularity of The Lord of the Rings has led to numerous references in popular culture, the founding of many societies by fans of Tolkien’s works, and the publication of many books about Tolkien and his works. The Lord of the Rings has inspired, and continues to inspire, artwork, music, films and television, video games, and subsequent literature. Award-winning adaptations of The Lord of the Rings have been made for radio, theatre, and film. In 2003, it was named Britain’s best-loved novel of all time in the BBC’s The Big Read.

The Difficult Middle Section

8. THE ORDEAL. Near the middle of the story, the hero enters a central space in the Special World and confronts death or faces his or her greatest fear. Out of the moment of death comes a new life.

(Read the rest here.)

Sometimes where you are is in the middle. Where things are difficult, the challenges are many and you’re suffering through the process of discovering new strength in yourself.

Frodo and Sam capture Gollum, who had been following them from Moria. They force him to guide them to Mordor. They find that the Black Gate of Mordor is too well guarded, so instead they travel to a secret way Gollum knows. On the way, they encounter Faramir, who, unlike his brother Boromir, resists the temptation to seize the Ring. He provides Frodo and Sam with food. Gollum — who is torn between his loyalty to Frodo and his desire for the Ring — betrays Frodo by leading him to the great spider Shelob in the tunnels of Cirith Ungol. Frodo falls when pierced by Shelob’s sting. But with the help of Galadriel’s gifts, Sam fights off the spider. Believing Frodo to be dead, Sam takes the Ring in the hope of finishing the quest alone. Orcs find Frodo, and from their words Sam becomes aware that Frodo is yet alive. The Orcs take Frodo’s body, and Sam chases after them, entering Mordor alone.

Sometimes you are being Frodo and Sam.

The Lord Of The Rings Book CoverThe Lord Of The Rings Book Cover


We’re hiring for a Core Platform Engineer. We’re going to touch a Billion people with financial services (FinTech) and we need help. 🙂

Pieter Breed
tag:pb.co.za,2013:Post/1143542 2017-04-03T09:18:50Z 2017-04-03T09:18:51Z Education of a programmer

I’m putting this link up so I’ll read it again in the future. Wisdom hides in these words.


  • distributed systems
  • complexity
  • organization building
  • systems thinking


Pieter Breed
tag:pb.co.za,2013:Post/1142419 2017-03-29T20:26:09Z 2017-03-29T20:26:09Z poem by tagor
I know that the day will come
when my sight of this earth shall be lost
and life will take its leave in silence
drawing the last curtain over my eyes.

Yet stars will watch at night
and morning rise as before
and hours heave like sea waves
casting off pleasures and pains.

When I think of this end of my moments
the barriers of the moments breaks
and I see by the light of death
your world with its careless treasures.

Rare is its lowliest treasures
rare is its meanest of lives.
Things that I longed for in vain
and things that I got, let them pass.

Let me but truly posses the things that I spurned and overlooked.

Pieter Breed
tag:pb.co.za,2013:Post/1128286 2017-02-02T20:04:36Z 2017-02-02T20:04:36Z Because we are all linked.

I got this from a whatsapp group I belong to. OMG. A child drew this. I can believe. 🙏🏼

Pieter Breed
tag:pb.co.za,2013:Post/1121367 2017-01-08T08:30:30Z 2017-01-08T08:35:26Z In which I realize I'm getting older - 2017.

My calendar has an entry in it. One that I’d forgotten about because I entered it almost 10 months ago. Day of Enlightenment. Ugh. What hubris!

Let’s take stock. Let’s me navel-gaze.

Enough. There’s work to do.

For the benefit of all beings.

Pieter Breed
tag:pb.co.za,2013:Post/1120404 2017-01-04T11:50:38Z 2017-01-04T11:50:38Z Civ VI - evil religious strategy

Here’s the main thrust of the idea:

  • In the game I’m playing, the civs that have high religion don’t have great armies or good science. They are very high on religion though so there’s lots of missionaries and apostles running around. Lightning and thunder, that sort of thing.
  • I have a relatively minor religion arranged around a strategy of collecting money and production from the faithful.
  • I activated the inquisition, which allows you to eliminate all religions except one from a city. (Protip - don’t let your inquisitors get confused about which religion they’re supposed to be members of…)
  • Go to the enemy civ’s Holy sites, conquer those cities using the regular army.
  • Send in the inquisitors to eradicate all faiths that are not mine. Inquisitors are cheap, but you only need one. Have an apostle ready to convert the lost sheep back to the one true faith.
  • Then make peace and return the conquered cities.

The opposition faiths are now inoculated.

In this game, I’m aiming for a science victory, but the religions are being irksome so I’m just trying to delay the faith game enough to actually reach the science victory condition.

Pieter Breed
tag:pb.co.za,2013:Post/1116887 2016-12-20T08:01:54Z 2016-12-20T08:01:55Z The value of values

(Not the Rich Hickey talk, which is excellent, and can be found here.)

I saw this next bit on Seneca Systems’ recruitment site:

Why Have Values?

Out of timber so crooked as that from which man is made, nothing entirely straight can be built. — Immanuel Kant

We recognize that it is more difficult to adhere to values than to simply not have them at all. We also understand that values are aspirational and, as Kant speaks to above, we are inherently flawed creatures [emphasis mine].

I call bullshit! We are not “inherently flawed creatures”. We are beautiful beyond measure. Since we’re dueling with quotes, let me riposté with this one:

Our deepest fear is not that we are inadequate. Our deepest fear is that we are powerful beyond measure. It is our light, not our darkness, that most frightens us. Your playing small does not serve the world. There is nothing enlightened about shrinking so that other people won’t feel insecure around you. We are all meant to shine as children do. It’s not just in some of us; it is in everyone. And as we let our own lights shine, we unconsciously give other people permission to do the same. As we are liberated from our own fear, our presence automatically liberates others. – Marianne Williamson Author, Lecturer

So why have values then?

I actually rather like the values that Seneca Systems chose:

  • Empathy
  • Transparency
  • Right over Easy
  • Don’t always be Right
  • Optimize for People

Values provide shared higher aspirations. We say what we believe in so we can all acknowledge that in each other. Even obvious things have to be communicated clearly.

Pieter Breed
tag:pb.co.za,2013:Post/1115072 2016-12-13T12:34:21Z 2016-12-13T12:34:22Z Step one

 - What am I trying to do?
 - Has it been done before?
 - Does it have a name?

-- Programming Buddha
Pieter Breed
tag:pb.co.za,2013:Post/1111192 2016-11-27T18:17:57Z 2016-11-27T18:17:57Z Russel Brand being awkwardly reflective about Black Friday

Pieter Breed
tag:pb.co.za,2013:Post/1109105 2016-11-18T16:59:57Z 2016-11-18T17:14:18Z Homage and Statement of (my) Humanity

Ok, hi!, I’m Pieter, I’m a human. I haven’t been posting in a while, because bloooooog. ugh.

Firstly. Shout-out to the guys on #devops on zatech’s slack channel. Cobus is an awesome dude and one day my beard will be as long as his is. Dude, please let me buy you that beer :)

Also DevOpsDays in Cape Town was nice! @devopsdayscpt 

I have a Linked-In now. There are some fancy Business People on there. It’s nice

Digitally, I’m still with keybase.io. They’re awesome! I have invites, ask me about them.

I work at Zoona now!. I’m helping them make it real!

The theme of the moment is Ask me how to make tech useful for you at Zoona.

Ok, that's it.


Pieter Breed
tag:pb.co.za,2013:Post/1107730 2016-11-13T11:44:52Z 2016-11-13T11:44:52Z The Template Introduction

When the student is ready the master appears

I am finding myself drawn, more and more, into my latent spirituality.  Everything is connected. There is no beginning and there is no end. However, one has to start somewhere. This introductory video is as good as any other starting point. Enjoy!

Pieter Breed
tag:pb.co.za,2013:Post/1106978 2016-11-10T07:29:44Z 2016-11-10T07:29:44Z The Diamond Sutra (Online)

Come, Blessed One, we pray Thee, roll the wheel of the dew-sweet Law – which is excellent in the beginning, excellent in the middle, and excellent in the end! – Lotus Sutra

It is a great privilege to be able to share a few days worth of work.

Online copy of the Diamond Sutra.

Pieter Breed
tag:pb.co.za,2013:Post/1091449 2016-09-19T09:59:35Z 2016-09-19T09:59:35Z The nature of mind

The Nature of Mind

No words can describe it
No example can point to it
Samsara does not make it worse
Nirvana does not make it better
It has never been born
It has never ceased
It has never been liberated
It has never been deluded
It has never existed
It has never been nonexistent
It has no limits at all
It does not fall into any kind of category.

~ Dudjom Rinpoche

Pieter Breed
tag:pb.co.za,2013:Post/1087397 2016-09-07T05:57:44Z 2020-09-02T19:43:11Z The kindness of undergoing hardships

At first we did not come here clothed, finely adorned, with money in our pocket and with provision to travel. When we came into this unknown place, where we knew no one at all, we had nothing whatsoever - our only wealth was our howling mouth and empty stomach. Our mother gave us food so that we would not go hungry, drink to keep us from thirst, clothes to fend off the cold and wealth to keep us from poverty. It was not as though she just gave us things no longer of use to herself: she herself went without food, without drink and without new clothes.

Furthermore, not only did she sacrifice her happiness as far as this existence is concerned, she also deprived herself of using her assets (as offerings) to provide for her own prosperity in future lives. In brief, without regard to her own happiness, in both this life and the next, she devoted herself to rearing and caring for her child.

Nor was it the case that she obtained what was needed easily and pleasurably; to provide for her child she was obliged to sin, to suffer and to toil. She sinned by having to resort to fishing, killing animals and so on in order to care for us. She suffered because what she gave her child was the fruit of trading, labouring in the fields and so forth, wearing the late evening or early morning frost for her boots, the stars as a hat, riding the horse of her calves, beaten by the whip of the long grass, her legs exposed to the bites of dogs and her face exposed to the looks of men.

She also treated this stranger who has become her child with more love than her own father, mother or lama, even though she knew not who this being was or what it would become. She looked at her child with loving eyes, gave her gentle warmth, cradled him in her arms and talked with sweet words saying, “My joy, ah my sunshine, my treasure, coochi coochi, aren’t you mummy’s joy” and so forth.

Jé Gampopa – Gems of Dharma, Jewels of Freedom

Pieter Breed
tag:pb.co.za,2013:Post/1077133 2016-07-29T08:45:30Z 2016-07-29T08:45:30Z I dub this the Sutra of Bob and Deepak. https://youtu.be/TJ11g4H8IKo

Pieter Breed
tag:pb.co.za,2013:Post/1076952 2016-07-28T17:24:25Z 2016-07-28T17:43:57Z I'm allowed to say this publicly too #VoteSA #VoteDA

I have been doing naughty things at great speed on the various highways that we have here in Cape Town. My recent purchase of an over-powered and over-powering "Commuting Bike" has been causing me to invent reasons for getting around Town. A lot. I also usually need to get wherever I need to be really quickly... Therefore, I notice that we have excellent roads with nice grippy tar every time I find myself entering some corner at an absurd lean-angle by accident

I have been riding on some very nice roads recently and I am very, very grateful. The stack of speeding fines in my drawer carries with it a monetary value to my gratefulness. 

ONE of those roads is called Helen Suzman Blvd and the thought has often entered my mind that I wonder who she was and what she had to do to have a nice road named after her in the New South Africa

I am also lazy and have left actually finding out to apparently right now.

I just saw this video courtesy of The South African

It is interesting! The #DA made it and I have to say it is very, very self-congratulatory, but that's OK. I like it anyway. Enough to want to say "Ek stem saam!" out in public.

U edel-agbare. Ek stem saam.

Edit: You might have heard of the Helen Suzman Foundation, which has been in the news recently.

Pieter Breed
tag:pb.co.za,2013:Post/1074386 2016-07-20T11:11:41Z 2016-07-20T11:12:03Z Ven Fifth Minyak Rinpoche visits Cape Town Kagyu, Aug 2016

Ven Fifth Minyak Rinpoche visits Cape Town Kagyu in the beginning of August 2016. 

Here is a link to the Cape Town Kagyu website with the details of the program.

I also went ahead and created a public google calendar for these events

Pieter Breed
tag:pb.co.za,2013:Post/1066237 2016-06-23T11:44:37Z 2016-06-23T11:53:15Z Gravity, but in real life

Do you remember the Sandra Bullock movie Gravity in which all kinds of shit goes down in space? Turns out, that actually happened. 

But it wasn't actually Sandra Bullock, it was one NASA astronaut and two Russian Cosmonauts. And it wasn't the ISS, it was Mir space station.


They survived...

Linenger was succeeded by Anglo-American astronaut Michael Foale, carried up by Atlantis on STS-84, alongside Russian mission specialist Elena Kondakova. Foale's increment proceeded fairly normally until 25 June when during the second test of the Progress manual docking system, TORU, Progress M-34 collided with solar arrays on the Spektr module and crashed into the module's outer shell, puncturing the module and causing depressurisation on the station. Only quick actions on the part of the crew, cutting cables leading to the module and closing Spektr's hatch, prevented the crews having to abandon the station in Soyuz TM-25. Their efforts stabilised the station's air pressure, whilst the pressure in Spektr, containing many of Foale's experiments and personal effects, dropped to a vacuum. In an effort to restore some of the power and systems lost following the isolation of Spektr and to attempt to locate the leak, EO-24 commander Anatoly Solovyev and flight engineer Pavel Vinogradov carried out a risky salvage operation later in the flight, entering the empty module during a so-called "intra-vehicular activity" or "IVA" spacewalk and inspecting the condition of hardware and running cables through a special hatch from Spektr's systems to the rest of the station. Following these first investigations, Foale and Solovyev conducted a 6-hour EVA outside Spektr to inspect the damage.

Pieter Breed