Holy Dev Newsletter November 2022
Welcome to the Holy Dev newsletter, which brings you gems I found on the web, updates from my blog, and a few scattered thoughts. You can get the next one into your mailbox if you subscribe.
🎥 A very good and entertaining criticism of today's state of software developement that has on the strait jacket of historical...
A very good and entertaining criticism of today's state of software developement that has on the strait jacket of historical burden, with demonstrations of some of the possibilites, including f.ex. interactive programming, visual tools for exploring program's state and code, Lisp-style recoverable errors, Erlang OTP's evolvable, killable, observable processes. The conclusion: we should use languages and tooling that is at least as good as Smalltalk's and Lisp's.
"[W]e're still digging ourselves into a kind of a pit by continuing to preserve practices that have no rational basis beyond being historical." - and make our job unnecessarily hard. The 80 char limit comes from the width of punch cards. Vi keybindings were optimized for a particular keyboard of the time. We still use fixed-width text as the primary/only medium, ignoring our powerful visual cortext. We still create programs as static artefacts that run from start to finish. (Long-lived serivces, anyone?) Little thought is typically given to evolving state over time.
Programming is a design discipline, i.e. you find out what you’re building as you build. Moreover, "the spec is always wrong" - it is always incomplete and it will change. So debuggability and evolvability are much more important that being "correct by construction," i.e. the (unproven) promise of static typing. We should embrace interactive development, have great tools for inspecting the running program, ones that leverage what we excel at including visual processing (here, the Glamorous toolkit looks as a step in the right direction). Our languages should empower us to express the business logic rather than drown in ceremony.
A fast, more limited, in-memory "graph database" composed of Clojure maps and queried via EQL. When the power/performance tradeoff of Datascript is not worth it. Also supports transforming data as it is being retrieved.
Very lightweight stats for your website: visitors, top pages, referrers, etc. Includes automatic bot detection and campaign stats. Tracking JS is very small (3.5 kB) and has the option to use a pixel, trigger from server middleware or import raw logs. Doesn’t track users with IDs. Open source and can be self-hosted (written in Go and uses either SQLite or Postgres.
Babashka pod that enables you to script ssh - establish a connection, send commands etc.
Great site to preview how social sites see your blog etc., to make sure previews will work as intended.
A 2022 overview of security keys - physical devices you plug into your PC and use for MFA, keeping your passwords really secure, etc. Features multiple variants of YubiKey, Thetis Fido U2F Security Key, CryptoTrust OnlyKey, uQontrol Qkey Password Vault, and HyperFido K18.
Guide to using YubiKey for GPG and SSH, with a master key used for creating sub-keys that are then used for signing/encruption/authentication. You can also only use it for SSH with the key type ed25519-sk without bothering with GPG - you will touch your yubikey during creation and then anytime you want to use that ssh key.
Get an interactive terminal into a GitHub Actions workflow for troubleshooting, f.ex. automatically when it failed. Uses the tmux fork tmate and connection proxying via tmate.io or your own tmate server. You can limit access to your own GH ssh key only.
"Dhall is a programmable configuration language that you can think of as: JSON + functions + types + imports". It is designed to solve the problems that template/configuration languages - such as the one used by Terraform or Kubernetes - have with limited reusability, verbosity, lacking/limited parametrization etc. It has integrations with a number of languages (e.g. Clojure, Go, Rust) and bindings for a bunch of configuration systems such as Ansible, CloudFormation, and Kubernetes (and you can always make your own).
The successur to the famous Netflix' Hystrix resillience library, born of the realization that fixed limits are impractical in complex, dynamic systems. This lib implements and integrates concepts from TCP congestion control to auto-detect concurrency limits for services in order to achieve optimal throughput with optimal latency.
Envoy has similarly working Adaptive Concurrency filter, which "dynamically adjusts the allowed number of requests that can be outstanding (concurrency) to all hosts in a given cluster at any time. Concurrency values are calculated using latency sampling of completed requests and comparing the measured samples in a time window against the expected latency for hosts in the cluster."
A Clojure library inspired by Netflix' concurrency-limits and the talk "Stop Rate Limiting! Capacity Management Done Right" by Jon Moore. - using TCP congestion control methods to find/set real limits of a system over time.
The Conventional Commits specification is a lightweight convention on top of commit messages. It provides an easy set of rules for creating an explicit commit history; which makes it easier to write automated tools on top of. Title line:
Key types are feat and fix but you can use others, such as chore, docs, ci, refactor, perf, test, and others.
feat: allow provided config object to extend other configs BREAKING CHANGE: `extends` key in config file is now used for extending other config files
feat(api)!: send an email to the customer when a product is shipped
"Before you write any code — ask if you could ever possibly want multiple kinds of the thing you are coding. If yes, just do it. Now, not later."
🎥 Great 15 min intro into RxJS, the popular JS lib for reactive streams that I am forced to use.
Great 15 min intro into RxJS, the popular JS lib for reactive streams that I am forced to use.
A simple front-end architecture that works
A great, short article, auto-translated acceptably from Norwegian (the link preview below is not translated but the page it leads to is):
The key points: All the data needed by the app is kept at a single place as a monolithic state object (a cljs atom or JS object). The state is mapped to exactly the data the UI needs and then sent down to the root component and its children etc. Finally, the UI is built from generic, design-based components that are ignorant of any domain concepts.
The "single monolithic state" is known from re-frame and Redux. But contrary to these frameworks, the components here are passive and simply receive their props from their parent instead of pulling it in themselves via subscribe or Redux.connect. There is only one-way data flow from the root to the leaves.
A key factor that the data is fitted - "prepared" - to the UI before being handed over to the root component, i.e. the data is exactly in the form and shape the UI needs it. Thus there is little need for any logic in the components. Every parent component simply destructures its props and passes each one to the respective child.
Equally revolutionary is the insistence on generic, reusable components based on the design and ignorant of the domain (e.g. PrimaryButton instead of SignInButton, or DataTable instead of StudentList). Such design components are quite stable, while what should be displayed where and how changes often. Personally I think you might want some domain-oriented "layout" components that essentially only delegate to appropriate design components to display parts of a (UI-fitted) data entity. The generic components would imply that the prepare-d data also produces their generic props, for example not 'universityClasses' but 'rows'.
The original Norwegian article:
Thank you for reading!