<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zak Knill on /dev/knill</title><link>https://zknill.io/</link><description>Recent content in Zak Knill on /dev/knill</description><generator>Hugo</generator><language>en-GB</language><lastBuildDate>Sat, 28 Feb 2026 21:33:24 +0000</lastBuildDate><atom:link href="https://zknill.io/index.xml" rel="self" type="application/rss+xml"/><item><title>You are the bottleneck</title><link>https://zknill.io/posts/you-are-the-bottleneck/</link><pubDate>Sat, 28 Feb 2026 21:33:24 +0000</pubDate><guid>https://zknill.io/posts/you-are-the-bottleneck/</guid><description>&lt;p>&lt;strong>The agent can produce code faster than you can review it. That&amp;rsquo;s the bottleneck now, not the keyboard, not the compiler. You.&lt;/strong>&lt;/p>
&lt;p>Before agents, the constraint was how fast you could write code. Now it&amp;rsquo;s how fast you can review it. The agent ships. You approve. And the agent is faster than you.
You&amp;rsquo;re not the producer anymore. You&amp;rsquo;re the reviewer. And that changes everything about how you should spend your time.&lt;/p></description></item><item><title>If code is cheap, intent is the currency</title><link>https://zknill.io/posts/commit-message-intent/</link><pubDate>Tue, 24 Feb 2026 09:15:57 +0000</pubDate><guid>https://zknill.io/posts/commit-message-intent/</guid><description>&lt;p>&lt;strong>Apparently &lt;a href="https://simonwillison.net/guides/agentic-engineering-patterns/code-is-cheap/">writing code is cheap now&lt;/a>&lt;/strong>.
So since the barrier to producing code is gone, the intent behind the code is the most important bit.
Intent is the new scarce resource, and commit messages are where that intent lives.&lt;/p>
&lt;h2 id="agents-work-in-human-processes">Agents work in human processes&lt;/h2>
&lt;p>Agents are still, for now, working inside human processes. The software development lifecycle (I&amp;rsquo;m
getting flashbacks to every &lt;em>agile coach&lt;/em> ever!) is still the same: we still have commits, pull
requests, code review. We still have humans responsible for the agent&amp;rsquo;s output. But generating the
code is cheaper, so the code &lt;em>review&lt;/em> carries more of the weight and responsibility for &lt;em>good
code&lt;/em>.&lt;/p></description></item><item><title>A chatbot's worst enemy is page refresh</title><link>https://zknill.io/posts/chatbots-worst-enemy-is-page-refresh/</link><pubDate>Wed, 11 Feb 2026 15:23:34 +0000</pubDate><guid>https://zknill.io/posts/chatbots-worst-enemy-is-page-refresh/</guid><description>&lt;p>&lt;strong>How is is possible that we&amp;rsquo;ve made incredible gains in the performance of models, but virtually no gains in
the infrastructure that supports them?.&lt;/strong>&lt;/p>
&lt;p>.. or what I like to call: the worst enemy of chatbots is page refresh.&lt;/p>
&lt;p>&lt;em>There are some large GIFs in this article, let them load :)&lt;/em>&lt;/p>
&lt;h3 id="claude-vs-page-refresh">Claude vs. Page refresh&lt;/h3>
&lt;p>If a picture speaks a thousand words, here is a GIF of the Claude UI taken on 11th Feb 2026.&lt;/p></description></item><item><title>Only use agents for tasks you already know how to do</title><link>https://zknill.io/posts/only-ai-tasks-you-know-how-to-do/</link><pubDate>Mon, 09 Feb 2026 10:57:48 +0000</pubDate><guid>https://zknill.io/posts/only-ai-tasks-you-know-how-to-do/</guid><description>&lt;p>We&amp;rsquo;ve all seen the complaints. The burden of reviewing AI &amp;lsquo;output&amp;rsquo; is shifting onto project
maintainers and team members. Folks can easily generate lots of code using AI, that code might
even be functional (in that it passes the tests also written by the AI).&lt;/p>
&lt;p>But that doesn&amp;rsquo;t necessarily make the code &lt;em>good&lt;/em> or &lt;em>correct&lt;/em>.&lt;/p>
&lt;p>So if you want to be a good team member, here&amp;rsquo;s my rule for coding with AI agents:&lt;/p></description></item><item><title>SSE sucks for transporting LLM tokens</title><link>https://zknill.io/posts/sse-sucks-for-transporting-llm-tokens/</link><pubDate>Thu, 27 Nov 2025 16:12:48 +0000</pubDate><guid>https://zknill.io/posts/sse-sucks-for-transporting-llm-tokens/</guid><description>&lt;h2 id="sse-sucks">SSE sucks&lt;/h2>
&lt;p>I&amp;rsquo;m just going to cut to the chase here. SSE as a transport mechanism for LLM tokens is naff.
It&amp;rsquo;s not that it can&amp;rsquo;t work, obviously it can, because people are using it and SDKs are built around
it. But it&amp;rsquo;s not a great fit for the problem space.&lt;/p>
&lt;p>The basic SSE flow goes something like this:&lt;/p>
&lt;ol>
&lt;li>Client makes an HTTP POST request to the server with a prompt&lt;/li>
&lt;li>Server responds with a 200 OK and keeps the connection open&lt;/li>
&lt;li>Server streams tokens back to the client as they are generated, using the SSE format&lt;/li>
&lt;li>Client processes the tokens as they arrive on the long-lived HTTP connection&lt;/li>
&lt;/ol>
&lt;p>Sure the approach has some benefits, like simplicity and compatibility with existing HTTP
infrastructure. But it still sucks.&lt;/p></description></item><item><title>So you want to build AI agent group chat?</title><link>https://zknill.io/posts/how-to-build-ai-group-chat/</link><pubDate>Sat, 15 Nov 2025 13:50:14 +0000</pubDate><guid>https://zknill.io/posts/how-to-build-ai-group-chat/</guid><description>&lt;blockquote>
&lt;p>&lt;em>Disclaimer, I work for Ably; so I&amp;rsquo;m intimately familiar with the tech I mention here. Opinions are my own, etc.&lt;/em>&lt;/p>
&lt;/blockquote>
&lt;h2 id="open-ai-group-chats">Open AI group chats&lt;/h2>
&lt;p>On Nov 13th Open AI &lt;a href="https://openai.com/index/group-chats-in-chatgpt/">announced the pilot of group chats in ChatGPT&lt;/a>.
This post looks at the existing patterns for interacting with models, and how they make it hard to build similar features.&lt;/p>
&lt;p>The Open AI group chat feature allows multiple users to join a chat with an AI model, and have a
conversation together. Responses from each user are visible to all participants, and the model
responds to the entire group. Building this with existing model and sdk transports patterns is hard.&lt;/p></description></item><item><title>Patterns for building realtime features</title><link>https://zknill.io/posts/patterns-for-building-realtime/</link><pubDate>Thu, 06 Feb 2025 16:33:33 +0000</pubDate><guid>https://zknill.io/posts/patterns-for-building-realtime/</guid><description>&lt;p>Realtime features make apps feel modern, collaborative, and up-to-date.
The features predominantly require sharing changes triggered by one user to other users, as the
changes are happening.&lt;/p>
&lt;p>This typically means your server needs to send data to some set of clients,
where those clients don&amp;rsquo;t know they are missing the data.&lt;/p>
&lt;p>These patterns rely on a connection between the client and the server, where the server can notify
the client of some data. This connection could be websockets, sse, event-streams, or polling
(long or short). The connection just needs to allow the server to send data to the client without
the client knowing that there is new data.&lt;/p></description></item><item><title>Phone call asymmetry</title><link>https://zknill.io/posts/phone-call-asymmetry/</link><pubDate>Fri, 03 May 2024 12:08:15 +0100</pubDate><guid>https://zknill.io/posts/phone-call-asymmetry/</guid><description>&lt;p>You get a phone call, but you&amp;rsquo;re away from your phone or you can&amp;rsquo;t answer it right at that moment.
You call the number back and hear an automated voice say:&lt;/p>
&lt;blockquote>
&lt;p>Thank you for calling [some business], for accounts press 1, to place a new order press 2&amp;hellip;.&lt;/p>
&lt;/blockquote>
&lt;p>Perhaps, by sheer luck (or skill) you manage to navigate the labyrinth of options and talk to a real human (sidebar: there&amp;rsquo;s a circle of hell reserved for the flow-chart designer
that creates a branch that ends up in them hanging up on you).&lt;/p></description></item><item><title>Every programmer should know</title><link>https://zknill.io/posts/every-programmer-should-know/</link><pubDate>Tue, 30 Apr 2024 11:41:27 +0100</pubDate><guid>https://zknill.io/posts/every-programmer-should-know/</guid><description>&lt;p>&lt;strong>Programmers should know a lot.. apparently.&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=18381640">Programming paradigms&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=27572218">SSDs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=2725015">Time&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=17908403">Latency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=15607869">Lockless concurrency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=20138409">AWS costs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=9158222">Algorithms&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=8321940">Floating point&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=38838944">More algorithms&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=4047623">More latency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=39657675">More more latency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=3919429">Memory&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=4966363">More more more latency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=12668229">More more more more latency&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=36420502">More memory&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=1530778">Regular expressions&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=3939642">Security&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=3665495">SEO&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=1695156">Programming&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=232622">Vim commands&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=13910772">165 things&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=17027405">Time complexities&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://news.ycombinator.com/item?id=32652346">Optical fibre&lt;/a>&lt;/li>
&lt;/ol></description></item><item><title>How to adopt Realtime updates in your app</title><link>https://zknill.io/posts/how-to-adopt-realtime/</link><pubDate>Thu, 23 Nov 2023 15:27:10 +0000</pubDate><guid>https://zknill.io/posts/how-to-adopt-realtime/</guid><description>&lt;p>&amp;hellip;and why you really should!&lt;/p>



&lt;div class="admonition admonition-tip">
 &lt;div class="admonition-title">What do I mean by &amp;#34;Realtime&amp;#34;&lt;/div>
 &lt;div class="admonition-content">Realtime is where clients of your app can find out about something the moment it happens, rather than having to poll/nag/ask for updates.
It&amp;rsquo;s generally where the server pushes the updates to the clients the moment the updates happens, rather than the clients needing to ask for them.&lt;/div>
&lt;/div>

&lt;p>Realtime updates rely on two main technologies:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Websockets&lt;/strong>: A stateful, persistent, bi-directional &amp;lsquo;channel&amp;rsquo; of communication.&lt;/li>
&lt;li>&lt;strong>Server sent events (SSE)&lt;/strong>: Built on top of HTTP, opens a long-running HTTP connection where multiple independent messages are written to the response over time.&lt;/li>
&lt;/ul>



&lt;div class="admonition admonition-warning">
 &lt;div class="admonition-title">Please don&amp;#39;t use polling&lt;/div>
 &lt;div class="admonition-content">&lt;p>You might also think of polling or long polling as a mechanism for fetching &amp;lsquo;Realtime&amp;rsquo; data from your backend.
Polling is not Realtime.&lt;/p></description></item><item><title>You don't need CRDTs for collaborative experiences</title><link>https://zknill.io/posts/collaboration-no-crdts/</link><pubDate>Thu, 16 Nov 2023 12:00:06 +0000</pubDate><guid>https://zknill.io/posts/collaboration-no-crdts/</guid><description>&lt;h2 id="you-dont-need-crdts">You don&amp;rsquo;t need CRDTs&lt;/h2>
&lt;p>&lt;strong>You don&amp;rsquo;t need CRDTs for collaborative experiences.&lt;/strong>&lt;/p>
&lt;p>First lets get the &amp;lsquo;what-about-ery&amp;rsquo; out the way&amp;hellip;&lt;/p>



&lt;div class="admonition admonition-tip">
 &lt;div class="admonition-title">Things you do need CRDTs for&lt;/div>
 &lt;div class="admonition-content">&lt;ol>
&lt;li>Offline first &amp;ndash; this is wayy harder to get useful behaviour with out CRDTs. If you don&amp;rsquo;t use them, you&amp;rsquo;re pretty much destined to have LWW (which is actually a CRDT behaviour),
and one user is likely to overwrite the changes of another. This isn&amp;rsquo;t a great experience for anyone involved.&lt;/li>
&lt;li>Text editing &amp;ndash; everyone&amp;rsquo;s gonna say &amp;ldquo;but hey, google docs uses operational transform not CRDTs&amp;rdquo;.. OK yes, but &lt;em>you are not google&lt;/em>. Martin Kleppmann has a great round-up of the
various people who though they implemented OT correctly, but actually didn&amp;rsquo;t. The reason that you need CRDTs for text editing collaboration is that it&amp;rsquo;s a really extreme example
of collaboration. The nature of text editing is that any tiny errors in the placement of characters by the convergence algorithm is going to create incorrect words, and
incorrect words are incredibly obvious. Text editing has a high rate of edits (as you type), and the edits need to interleave perfectly or you get incorrect words, and errors in
the interleaving are super obvious (incorrect words)!&lt;/li>
&lt;/ol>
&lt;/div>
&lt;/div>

&lt;p>Hold, on.. that all sounds great, but..&lt;/p></description></item><item><title>Giving up my smartphone - Duoqin F22 Pro</title><link>https://zknill.io/posts/duoqin-f22-pro/</link><pubDate>Sat, 04 Nov 2023 09:23:10 +0000</pubDate><guid>https://zknill.io/posts/duoqin-f22-pro/</guid><description>&lt;h2 id="background">Background&lt;/h2>
&lt;p>I was first attracted to the dumbphones after seeing a series of articles on Hacker News.
I like the idea of using - and relying on - my phone less and less.&lt;/p>
&lt;p>No plan survives contact with the enemy, and I knew that I wouldn&amp;rsquo;t manage in life with a stripped
down phone that could only do calls, texts, and maybe some music.&lt;/p>
&lt;p>Eventually I stumbled across the dumbphones subreddit (/r/dumbphones).
On this subreddit I discovered &amp;rsquo;transition phones&amp;rsquo;, that is a phone that can do some smartphone things, but with dumbphone characteristics.
I found that you could have a dumbphone form factor but still install all the smartphone apps you might need.&lt;/p></description></item><item><title>Do developers really want to give over their data?</title><link>https://zknill.io/posts/do-you-want-to-give-over-your-data/</link><pubDate>Fri, 20 Oct 2023 07:28:42 +0100</pubDate><guid>https://zknill.io/posts/do-you-want-to-give-over-your-data/</guid><description>&lt;p>There&amp;rsquo;s a rise in hosted database companies like &lt;a href="https://supabase.com">Supabase&lt;/a>, &lt;a href="https://neon.tech/">Neon&lt;/a>, &lt;a href="https://turso.tech/">Turso&lt;/a>, etc.
When I look at those companies, here&amp;rsquo;s the thing I&amp;rsquo;ve been struggling with:&lt;/p>
&lt;blockquote>
&lt;p>Do developers really want to give over their data?&lt;/p>
&lt;/blockquote>
&lt;p>Your making a trade-off by choosing one of these companies, and the tradeoff is this:&lt;/p>
&lt;blockquote>
&lt;p>They will solve some boring infrastructure and security problems, and in return, they get all your data.&lt;/p>
&lt;p>Not in the Cambridge-Analytica/Facebook style of &amp;ldquo;get all your data&amp;rdquo;.
More like the S3 style; where the cost (in dollars 💲) or the cost (in time/effort 🕔) are high enough to dissuade you from trying to leave. There&amp;rsquo;s a strong lock-in effect.&lt;/p></description></item><item><title>So you want to build Miro and Figma style collaboration?</title><link>https://zknill.io/posts/ephemeral-collaboration/</link><pubDate>Thu, 05 Oct 2023 19:18:40 +0100</pubDate><guid>https://zknill.io/posts/ephemeral-collaboration/</guid><description>&lt;p>Miro and Figma have a bunch of collaboration features, in this post I&amp;rsquo;m going to break down two of those features
and look at what you&amp;rsquo;d have to think about when building these into your own apps.&lt;/p>
&lt;p>&lt;em>Disclaimer: I work for a company in this product space, which is why I care about these problems&lt;/em>.&lt;/p>
&lt;p>Lets start with..&lt;/p>
&lt;h2 id="collaborative-cursors">Collaborative cursors&lt;/h2>
&lt;p>&lt;img src="https://zknill.io/img/collaboration-cursors.png" alt="Collaborative cursors">&lt;/p>
&lt;p>Collaborative cursors allow multiple users to interact on the same page of a website,
and for each participant to see where the other participants are pointing or moving their cursors.&lt;/p></description></item><item><title>Streaming data aggregation</title><link>https://zknill.io/posts/streaming-data-aggregation/</link><pubDate>Wed, 23 Aug 2023 10:12:37 +0100</pubDate><guid>https://zknill.io/posts/streaming-data-aggregation/</guid><description>&lt;p>Imagine you&amp;rsquo;re presented with this problem:&lt;/p>
&lt;blockquote>
&lt;p>Design a system that can show the top 10 most popular songs over the last 10 seconds on the homepage of a music streaming service.&lt;/p>
&lt;p>You have access to a queue of events representing song &amp;lsquo;plays&amp;rsquo; with a &lt;code>(timestamp, song_id)&lt;/code> tuple.&lt;/p>
&lt;p>The data should update, and be as fresh as possible.&lt;/p>
&lt;/blockquote>
&lt;p>We are given this to work with, we need to design a system that satisfies the requirements, replacing the &amp;ldquo;❓&amp;rdquo;:&lt;/p></description></item><item><title>The egg test: a model for reversible and irreversible decision making</title><link>https://zknill.io/posts/egg-test/</link><pubDate>Thu, 17 Aug 2023 10:34:06 +0100</pubDate><guid>https://zknill.io/posts/egg-test/</guid><description>&lt;p>&lt;em>Some talk of one way and two way doors, but I prefer the &amp;rsquo;egg test&amp;rsquo;&lt;/em>.&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>Do you want to know if it&amp;rsquo;s an egg?&lt;/strong>&lt;/p>
&lt;p>&amp;hellip; Drop it, if it cracks and you see an egg, then it&amp;rsquo;s an egg.&lt;/p>
&lt;/blockquote>
&lt;h3 id="the-egg-test-is-irreversible">The egg test is irreversible&lt;/h3>
&lt;p>Clearly the problem here is that it&amp;rsquo;s now not possible to use that egg. This is an &lt;em>irreversible decision&lt;/em>.
That egg is never going to be hole again. And it&amp;rsquo;s going to be super hard to separate the egg, the shell, and whatever else was on the surface you dropped the egg on.&lt;/p></description></item><item><title>Standard lib structured logging in Go 1.21</title><link>https://zknill.io/posts/go-1-21-slog/</link><pubDate>Tue, 08 Aug 2023 17:05:20 +0100</pubDate><guid>https://zknill.io/posts/go-1-21-slog/</guid><description>&lt;blockquote>
&lt;p>Go 1.21 includes structured logging (with levels) as a standard library package.&lt;/p>
&lt;p>In this post we look at the &lt;code>log/slog&lt;/code> package, and what it provides.&lt;/p>
&lt;/blockquote>
&lt;p>TL;DR - here&amp;rsquo;s a package to make using &lt;code>log/slog&lt;/code> easier: &lt;a href="https://github.com/zknill/slogmw">github.com/zknill/slogmw&lt;/a>&lt;/p>
&lt;h2 id="levels">Levels&lt;/h2>
&lt;p>&lt;code>log/slog&lt;/code> comes with 4 levels:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="nx">slog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Debug&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello world&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">slog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Info&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello world&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">slog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Warn&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello world&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">slog&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Error&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;hello world&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The functions providing each level come with the same pair of exported method calls.&lt;/p>
&lt;p>Here&amp;rsquo;s the example for &lt;code>Info&lt;/code>:&lt;/p></description></item><item><title>SQLedge - Postgres on the edge</title><link>https://zknill.io/projects/sqledge/</link><pubDate>Tue, 01 Aug 2023 13:01:24 +0000</pubDate><guid>https://zknill.io/projects/sqledge/</guid><description>&lt;h2 id="sqledge">SQLedge&lt;/h2>
&lt;p>SQLedge uses Postgres logical replication to stream the changes in a source Postgres database to an SQLite database that can run on the edge. SQLedge serves reads from its local SQLite database, and forwards writes to the upstream Postgres server that it&amp;rsquo;s replicating from.&lt;/p>
&lt;p>This lets you run your apps on the edge, and have local, fast, and eventually consistent access to your data.&lt;/p>
&lt;p>&lt;img src="https://zknill.io/img/sqledge.png" alt="sqledge">&lt;/p>
&lt;blockquote>
&lt;p>Check out the repo on &lt;a href="https://github.com/zknill/sqledge">Github&lt;/a>&lt;/p></description></item><item><title>So, you want to deploy on the edge?</title><link>https://zknill.io/posts/edge-database/</link><pubDate>Mon, 31 Jul 2023 08:33:58 +0100</pubDate><guid>https://zknill.io/posts/edge-database/</guid><description>&lt;p>Application developers often deploy their apps into a single area, generally represented by a handful of AZs in a single region.
No matter where their users make requests from, those requests get served by the region where the developers&amp;rsquo; apps run.&lt;/p>
&lt;p>If a user makes a request from Europe, and the apps run in US East, that adds an extra 100-150ms of latency just by round-tripping across the Atlantic.&lt;/p>
&lt;p>Edge computing tries to solve this problem, by letting app developers deploy their applications across the globe, so that apps serve the user requests closer to the user.
This removes a lot of the round-trip latency because the request has to travel less far before getting to a data center that hosts the app.&lt;/p></description></item><item><title>Theory of a program</title><link>https://zknill.io/posts/theory-of-program/</link><pubDate>Mon, 10 Jul 2023 10:21:34 +0100</pubDate><guid>https://zknill.io/posts/theory-of-program/</guid><description>&lt;blockquote>
&lt;p>The building of the program is the same as the building of the theory of it by and in the team of programmers. During the program life a programmer team possessing its theory remains in active control of the program, and in particular retains control over all modifications. The death of a program happens when the programmer team possessing its theory is dissolved. A dead program may continue to be used for execution in a computer and to produce useful results.The actual state of death becomes visible when demands for modifications of the program cannot be intelligently answered. Revival of a program is the rebuilding of its theory by a new programmer team.
– Peter Naur, 1985&lt;/p></description></item><item><title>Push stick drop - dog training</title><link>https://zknill.io/projects/pushstickdrop/</link><pubDate>Fri, 30 Jun 2023 17:01:24 +0000</pubDate><guid>https://zknill.io/projects/pushstickdrop/</guid><description>&lt;p>&lt;img src="https://zknill.io/img/pushstickdrop-cover.png" alt="logo">&lt;/p>
&lt;h2 id="push-stick-drop">Push stick drop&lt;/h2>
&lt;blockquote>
&lt;p>I built &lt;a href="https://pushstickdrop.dog">https://pushstickdrop.dog&lt;/a> to help train my dog potato.&lt;/p>
&lt;/blockquote>
&lt;p>Dogs learn new behaviours incrementally. The Push stick drop tool helps you build the behaviour incrementally.&lt;/p>
&lt;p>When building on new behaviours with your dog, you should do it incrementally. The app takes care of setting the next duration, and tracking the success and failures.
The timer page lets you record a success or a failure against the duration, and tracks your dogs progress over time.&lt;/p></description></item><item><title>Radical and incremental change</title><link>https://zknill.io/posts/radical-incremental-change/</link><pubDate>Mon, 03 Apr 2023 13:25:14 +0100</pubDate><guid>https://zknill.io/posts/radical-incremental-change/</guid><description>&lt;h2 id="the-allure-of-radical-change">The allure of radical change&lt;/h2>
&lt;p>Radical changes are exciting. They are big, bold, and transformative.
Most recently we&amp;rsquo;ve seen this with large language models &amp;ndash; or LLMs &amp;ndash; mostly of the GPT variety.
Radical changes cause ripples and waves. We&amp;rsquo;ve seen academics and techies calling for a pause in giant AI experiments in &lt;a href="https://futureoflife.org/open-letter/pause-giant-ai-experiments/">an open letter&lt;/a>.&lt;/p>
&lt;p>We can easily see the transformative impact of radical change.
This makes it exciting to get behind, and sometimes scary when it arrives,
but either way radical change is alluring because it&amp;rsquo;s easy to see the impact.
Radical changes are capable of mobilising people. Either for, or against, the change.&lt;/p></description></item><item><title>Writing confidently with ChatGPT</title><link>https://zknill.io/posts/writing-confidently-with-chatgpt/</link><pubDate>Fri, 17 Feb 2023 15:25:50 +0000</pubDate><guid>https://zknill.io/posts/writing-confidently-with-chatgpt/</guid><description>&lt;h2 id="an-ai-language-model">An AI language model&lt;/h2>
&lt;p>As an AI language model designed to mimic human conversation, ChatGPT has encountered its fair share of criticisms. One of the most common criticisms is that ChatGPT can be overly confident in its responses.
This perception arises from the fact that people often expect ChatGPT to be an all-knowing oracle that has an answer to every question.
As a result, ChatGPT is sometimes judged harshly when it provides a confidently incorrect response.&lt;/p></description></item><item><title>PackageMap - a tool to build a map of your code</title><link>https://zknill.io/projects/packagemap/</link><pubDate>Fri, 02 Dec 2022 17:01:24 +0000</pubDate><guid>https://zknill.io/projects/packagemap/</guid><description>&lt;p>&lt;img src="https://zknill.io/img/packagemap-cover.png" alt="Packagemap logo">&lt;/p>
&lt;h2 id="packagemap">PackageMap&lt;/h2>
&lt;blockquote>
&lt;p>I built &lt;a href="https://packagemap.co">https://packagemap.co&lt;/a> to see how cohesive or coupled code was.&lt;/p>
&lt;/blockquote>
&lt;p>PackageMap is the next iteration of &lt;a href="https://zknill.io/projects/java-graphviz/">java-graphviz&lt;/a>.&lt;/p>
&lt;p>It builds on the same principles as java-graphviz. Namely that:&lt;/p>
&lt;ol>
&lt;li>Coupling is bad&lt;/li>
&lt;li>Cohesion is good&lt;/li>
&lt;li>The existing code review tools show you file content, but not how the files interact with each other.&lt;/li>
&lt;/ol>
&lt;p>&lt;img src="https://upload.wikimedia.org/wikipedia/commons/0/09/CouplingVsCohesion.svg#center" alt="cohesion and coupling">&lt;/p>
&lt;h3 id="code-review-is-hard">Code review is hard&lt;/h3>
&lt;p>I do a bunch of code review, particularly in github.
The code review UI is great, but I found that I was only reviewing the content of the files.&lt;/p></description></item><item><title>Backend for the Frontend, and incentives</title><link>https://zknill.io/posts/backend-for-the-frontend/</link><pubDate>Tue, 22 Nov 2022 08:36:46 +0000</pubDate><guid>https://zknill.io/posts/backend-for-the-frontend/</guid><description>&lt;p>I work for Attest, we split our engineers largely down the seam of Backend and Frontend.
We have java and go apps that run server-side, developed by our Backend engineers.
We have typescript and vue apps that run in browser/on the client, developed by our Frontend engineers.&lt;/p>
&lt;p>By splitting down this seam, we are also splitting on the goals and incentives of the two disciplines.&lt;/p>
&lt;h2 id="clean-and-reusable-they-said">&amp;ldquo;Clean and reusable&amp;rdquo;, they said..&lt;/h2>
&lt;p>Our Backend engineers are motivated to provide ‘clean’ and ‘reusable’ (in quotes, because we can argue over the definition of those words) APIs.
APIs that &amp;ndash; are supposed to &amp;ndash; expose the core concepts of our system.
APIs that don’t make too many judgements on how the data will be consumed and displayed.&lt;/p></description></item><item><title>Aggregates: one piece of code design</title><link>https://zknill.io/posts/aggregate-design/</link><pubDate>Mon, 10 Oct 2022 08:59:43 +0100</pubDate><guid>https://zknill.io/posts/aggregate-design/</guid><description>&lt;p>&lt;strong>Most tips for code design exist to keep a lid on the exploding complexity of our systems&lt;/strong>.&lt;/p>



&lt;div class="admonition admonition-info">
 &lt;div class="admonition-title">Aggregates&lt;/div>
 &lt;div class="admonition-content">&lt;p>&lt;em>I won&amp;rsquo;t explain all the details of an Aggregate here, but I will say this&amp;hellip;&lt;/em>&lt;/p>
&lt;p>An Aggregate is:&lt;/p>
&lt;ul>
&lt;li>a concept from Domain Driven Design (DDD).&lt;/li>
&lt;li>a cluster of associated objects that we treat as a unit for the purposes of data changes.&lt;/li>
&lt;li>a mechanism for simplifying the inter-object relationships and complexity.&lt;/li>
&lt;li>a container that enforces the invariants on the data it holds.&lt;/li>
&lt;li>globally identified only by its root.&lt;/li>
&lt;li>opaque, all actions must go through the root of the Aggregate.&lt;/li>
&lt;/ul>
&lt;/div>
&lt;/div>

&lt;h2 id="aggregates-are-hierarchical">Aggregates are hierarchical&lt;/h2>
&lt;p>Aggregates are an important mechanism to tame the complexity inside a system by simplifying and limiting the traversal and number of relationships between objects.&lt;/p></description></item><item><title>Please don't write "point of view"</title><link>https://zknill.io/posts/writing-point-of-view/</link><pubDate>Tue, 02 Aug 2022 13:21:34 +0100</pubDate><guid>https://zknill.io/posts/writing-point-of-view/</guid><description>&lt;p>&lt;em>There are some hills I am prepared to die on, this is probably not one of them, but it is irritating as hell.&lt;/em>&lt;/p>



&lt;div class="admonition admonition-note">
 &lt;div class="admonition-title">Note&lt;/div>
 &lt;div class="admonition-content">If you find yourself writing &amp;ldquo;point of view&amp;rdquo; in a slack message, blog post, notion document, or anywhere else.. you&amp;rsquo;re almost certainly doing it wrong.&lt;/div>
&lt;/div>

&lt;h2 id="an-offending-phrase">An offending phrase&lt;/h2>
&lt;p>&lt;strong>Example 1&lt;/strong>:&lt;/p>
&lt;p>This is a post about why I hate the phrase &amp;ldquo;point of view&amp;rdquo;.&lt;/p>
&lt;p>&amp;ldquo;Point of view&amp;rdquo; is used in sentences like this:&lt;/p></description></item><item><title>Debugging Linkerd for mislabelled route metrics</title><link>https://zknill.io/posts/debugging-linkerd-mislabelled-routes/</link><pubDate>Fri, 29 Jul 2022 09:54:25 +0100</pubDate><guid>https://zknill.io/posts/debugging-linkerd-mislabelled-routes/</guid><description>&lt;p>&lt;em>This post is cross-posted from the &lt;a href="https://medium.com/attest-product-and-technology/debugging-mislabelled-route-metrics-from-linkerd-dda47fdff04a">Attest Product &amp;amp; Technology medium publication&lt;/a>.&lt;/em>&lt;/p>
&lt;hr>
&lt;p>At Attest we use &lt;a href="https://linkerd.io">Linkerd&lt;/a>.
Linkerd is a service mesh, it has features that help add observability and reliability to your Kubernetes clusters.&lt;/p>
&lt;p>&lt;strong>This is a story about debugging Linkerd, to find out why our metrics were being misrecorded.&lt;/strong>&lt;/p>
&lt;p>One part of Linkerd (&lt;code>linkerd-proxy&lt;/code>) runs as a sidecar,
this sidecar is attached to your deployment’s pods and the traffic going in and out of the pod passes through linkerd-proxy.
Linkerd can now see the URL paths (endpoints) that your service is being called on,
and can record &lt;a href="https://linkerd.io/2.11/reference/proxy-metrics/">Prometheus metrics&lt;/a> on those endpoints.&lt;/p></description></item><item><title>Addicted to on-call</title><link>https://zknill.io/posts/addicted-to-oncall/</link><pubDate>Sat, 25 Jun 2022 10:39:42 +0100</pubDate><guid>https://zknill.io/posts/addicted-to-oncall/</guid><description>&lt;div class="admonition admonition-abstract">
 &lt;div class="admonition-title">Abstract&lt;/div>
 &lt;div class="admonition-content">This is an explanation of the systems that lead an organisation to become addicted to on-call.&lt;/div>
&lt;/div>

&lt;p>Most people recognise addiction in drugs, alcohol, nicotine, sugar, etc.
A &lt;em>system&lt;/em> can also have addiction in it. In a system it&amp;rsquo;s often called &lt;em>dependence&lt;/em> rather than addiction.
But ultimately it&amp;rsquo;s the same thing.&lt;/p>
&lt;h2 id="system-dependence">System dependence&lt;/h2>
&lt;p>System dependence or addiction stems from a short-term stimulus &amp;ndash; that wears off &amp;ndash; which does nothing to change the underlying reality.&lt;/p></description></item><item><title>Tragedy of the codebase commons</title><link>https://zknill.io/posts/tragedy-of-the-codebase-commons/</link><pubDate>Mon, 20 Jun 2022 10:52:00 +0100</pubDate><guid>https://zknill.io/posts/tragedy-of-the-codebase-commons/</guid><description>&lt;blockquote>
&lt;p>Tragedy of the commons is a situation in which individuals have access to some shared resource. The individuals act independently to their own self-interest, and contrary to
the common good of all, ultimately destroying or depleting the resource.&lt;/p>
&lt;/blockquote>
&lt;h2 id="classic-tragedy-of-the-commons-example">Classic tragedy of the commons example&lt;/h2>
&lt;p>The classic example follows cow farmers feeding cows on a shared (common) pasture.&lt;/p>
&lt;p>Each farmer wants to make the maximum amount of money, to make more money the farmer needs more cows.
There are two rates involved;&lt;/p></description></item><item><title>visualise java class imports using graphviz</title><link>https://zknill.io/projects/java-graphviz/</link><pubDate>Mon, 13 Jun 2022 11:43:00 +0000</pubDate><guid>https://zknill.io/projects/java-graphviz/</guid><description>&lt;h1 id="java-graphviz">java-graphviz&lt;/h1>
&lt;p>&lt;a href="https://github.com/zknill/java-graphviz">https://github.com/zknill/java-graphviz&lt;/a>&lt;/p>
&lt;p>I wrote this tool to visualise the imports and dependencies between java classes.&lt;/p>
&lt;p>Wikipedia has a great article on cohesion and couple of code:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://en.wikipedia.org/wiki/Cohesion_(computer_science)">https://en.wikipedia.org/wiki/Cohesion_(computer_science)&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>In this article there&amp;rsquo;s this image:&lt;/p>
&lt;p>&lt;img src="https://upload.wikimedia.org/wikipedia/commons/0/09/CouplingVsCohesion.svg#center" alt="cohesion and coupling">&lt;/p>
&lt;p>The image show good and bad cohesion and coupling.
It&amp;rsquo;s easy to understand these two in this image, but it&amp;rsquo;s much harder to understand them in code.
I wrote the &lt;code>java-graphviz&lt;/code> tool to help visualise the cohesion and coupling of our codebases.&lt;/p></description></item><item><title>Consistently executable interviews</title><link>https://zknill.io/posts/consistently-executable-interviews/</link><pubDate>Thu, 09 Jun 2022 07:13:23 +0100</pubDate><guid>https://zknill.io/posts/consistently-executable-interviews/</guid><description>&lt;p>Hiring is important for a business, it&amp;rsquo;s a high leverage activity that makes other activities easier or faster. More of the right people (read: qualified and capable) can do more. More of the wrong people (read: underskilled) can tank the whole team.&lt;/p>
&lt;p>Selecting who joins your company or team is important. A great emphasis is put on the interviews, by both the company and the candidate. Less emphasis is put on training the interviewers.&lt;/p></description></item><item><title>usable java setup vim</title><link>https://zknill.io/posts/usable-java-setup-vim/</link><pubDate>Thu, 19 May 2022 17:19:20 +0100</pubDate><guid>https://zknill.io/posts/usable-java-setup-vim/</guid><description>&lt;h1 id="vim--java">Vim + Java&lt;/h1>
&lt;p>This post covers a usable vim setup for writing java.&lt;/p>
&lt;p>I assume that you are familiar enough with vim to be able to configure it, and install packages.&lt;/p>
&lt;h2 id="code-completion">Code completion&lt;/h2>
&lt;p>The main part of writing java in vim is having decent code completion. We are going to use &lt;code>coc.nvim&lt;/code> as the engine, and &lt;code>coc-java&lt;/code> as the language server.
Both are borrowed and forked from VSCode plugins.&lt;/p>
&lt;ol>
&lt;li>Install &lt;a href="https://github.com/neoclide/coc.nvim">https://github.com/neoclide/coc.nvim&lt;/a>&lt;/li>
&lt;li>In vim, run &lt;code>:CocInstall coc-java&lt;/code> to install coc-java.&lt;/li>
&lt;/ol>
&lt;p>We can also install deoplete, which is a completion framework that can help with words and symbols that are already in the open document.&lt;/p></description></item><item><title>docker login error on m1</title><link>https://zknill.io/posts/docker-credstore-m1/</link><pubDate>Thu, 12 May 2022 11:51:25 +0100</pubDate><guid>https://zknill.io/posts/docker-credstore-m1/</guid><description>&lt;p>&lt;strong>After updating my m1 mac, and docker, the docker login to private repo commands started to fail.&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Error saving credentials: error storing credentials - err: exit status 1, out: Post &amp;#34;http://ipc/registry/credstore-updated&amp;#34;: dial unix /Users/user/Library/Containers/com.docker.docker/Data/backend.sock: connect: connection refused
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If you are using &lt;code>lima-vm&lt;/code>, remove this line from &lt;code>~/.docker/config.json&lt;/code>&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">&amp;#34;credStore&amp;#34;: &amp;#34;desktop&amp;#34;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>power distance in comms</title><link>https://zknill.io/posts/power-distance-comms/</link><pubDate>Tue, 03 May 2022 09:03:01 +0100</pubDate><guid>https://zknill.io/posts/power-distance-comms/</guid><description>&lt;h3 id="penduluming-feedback">Penduluming feedback&lt;/h3>
&lt;p>In my career I&amp;rsquo;ve received feedback on my comms.
This is partly because I actively seek it out, comms are important.
It is also partly because my default state &amp;ndash; which I work hard to moderate &amp;ndash; is to be direct, or blunt.&lt;/p>
&lt;p>Until recently, all the feedback I received was to moderate my comms to be less blunt. To share more of the background and context.&lt;/p>
&lt;p>As I started to work more closely with the most senior members of our org, the I started receiving feedback to be more direct.&lt;/p></description></item><item><title>penduluming</title><link>https://zknill.io/posts/penduluming/</link><pubDate>Wed, 27 Apr 2022 19:32:01 +0100</pubDate><guid>https://zknill.io/posts/penduluming/</guid><description>&lt;p>&lt;img src="https://zknill.io/img/penduluming-pendulum.png" alt="pendulum in two positions">&lt;/p>
&lt;p>&lt;strong>One of the mental models I repeatedly use is the idea of a pendulum.&lt;/strong>&lt;/p>
&lt;p>The pendulum swings between two extremes; the two most outer points.
Each position of the pendulum can be mapped to a position we could take on some decision.
The extreme positions of the pendulum are the extremes positions in the decision.&lt;/p>
&lt;p>When this mental model is applied to decisions, I call it &lt;strong>&lt;em>penduluming&lt;/em>&lt;/strong>.&lt;/p></description></item><item><title>santander cycles app</title><link>https://zknill.io/posts/santander-cycles-app/</link><pubDate>Mon, 25 Apr 2022 12:59:39 +0100</pubDate><guid>https://zknill.io/posts/santander-cycles-app/</guid><description>&lt;p>&lt;strong>My TfL Santander cycles app stopped accepting my credit card details.&lt;/strong>&lt;/p>
&lt;hr>
&lt;h5 id="symtoms">symtoms&lt;/h5>
&lt;ul>
&lt;li>When trying to pay for 24hr cycle access the app navigated to a &amp;lsquo;register card details&amp;rsquo; page.&lt;/li>
&lt;li>The app didn&amp;rsquo;t accept any of the card details I tried on the page.&lt;/li>
&lt;/ul>
&lt;h5 id="fix">fix&lt;/h5>
&lt;ul>
&lt;li>Log into the same account on &lt;a href="https://tfl.gov.uk/santandercycles">https://tfl.gov.uk/santandercycles&lt;/a>&lt;/li>
&lt;li>(Re-)register the same card details as in the app&lt;/li>
&lt;li>Open the app, take out a 24hr cycle access.&lt;/li>
&lt;li>The app will prompt you to enter card details, enter the same ones that you put into the website.&lt;/li>
&lt;li>The purchase will succeed&lt;/li>
&lt;/ul></description></item><item><title>email workflow</title><link>https://zknill.io/posts/email-workflow/</link><pubDate>Fri, 18 Mar 2022 16:59:06 +0000</pubDate><guid>https://zknill.io/posts/email-workflow/</guid><description>&lt;h2 id="10-years-of-gmail">10 years of gmail&lt;/h2>
&lt;p>For 10+ years I was a gmail user.&lt;/p>
&lt;p>&lt;a href="https://hey.com">Hey.com&lt;/a> &amp;ndash; and the horror stories of users being locked out of their gmail accounts &amp;ndash; convinced me that I wanted a paid email provider.
I spent ~2 years with Hey. I have moved off hey because &lt;strong>I want to own my email address&lt;/strong>.&lt;/p>
&lt;p>I picked &lt;a href="https://fastmail.com">fastmail&lt;/a>. This post covers my workflow.
I&amp;rsquo;ve omitted some of the details, as this post covers them much better than I can here:&lt;/p></description></item><item><title>change the battlefield</title><link>https://zknill.io/posts/change-the-battlefield/</link><pubDate>Mon, 31 Jan 2022 16:21:28 +0000</pubDate><guid>https://zknill.io/posts/change-the-battlefield/</guid><description>&lt;blockquote>
&lt;p>&lt;strong>If you don&amp;rsquo;t like the game, change the battlefield.&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>This post is about the rules of the game, the games that we play, and how to change the battlefield.&lt;/p>
&lt;h2 id="know-that-you-are-playing-a-game">know that you are playing a game&lt;/h2>
&lt;p>We all play games, life is a game. Everyone in life is a player, and we are all playing.
If we chose not to play the game of life we would be dead, so while we are still alive we are all still playing.
Depending on the game that you play, the rules of the game will be different.&lt;/p></description></item><item><title>focus on what is, then what to do about it</title><link>https://zknill.io/posts/focus-on-what-is/</link><pubDate>Tue, 18 Jan 2022 17:59:09 +0100</pubDate><guid>https://zknill.io/posts/focus-on-what-is/</guid><description>&lt;p>One of my wonderful (and very self-reflective) colleagues reached out to me to ask;&lt;/p>
&lt;blockquote>
&lt;p>I tried to explain something in a PR, but I can see from the next question they asked that they hadn&amp;rsquo;t understood my first explanation.
What should I say to explain the problem to them?&lt;/p>
&lt;/blockquote>
&lt;h2 id="most-misunderstandings-come-from-not-being-in-sync">most misunderstandings come from not being &amp;lsquo;in-sync&amp;rsquo;&lt;/h2>
&lt;p>In &lt;a href="https://zknill.io/posts/connect-the-dots">&amp;ldquo;connecting to dots&amp;rdquo;&lt;/a> I explained a little about my definition of &amp;lsquo;in-sync&amp;rsquo;.
To me, in-sync means :&lt;/p></description></item><item><title>when to share your mental model</title><link>https://zknill.io/posts/when-to-share-your-mental-model/</link><pubDate>Fri, 14 Jan 2022 09:54:08 +0000</pubDate><guid>https://zknill.io/posts/when-to-share-your-mental-model/</guid><description>&lt;p>A mental model is a like &lt;a href="https://zknill.io/posts/explain-using-analogies/">an analogy, it&amp;rsquo;s a shortcut to understanding&lt;/a>.
Mental models help you to simplify and understand something complex and difficult.&lt;/p>
&lt;blockquote>
&lt;p>All models are wrong, but some are useful. &amp;ndash; George E. P. Box, statistician&lt;/p>
&lt;/blockquote>
&lt;p>Given your model could be wrong, it&amp;rsquo;s important to know when you should or should not share it.
By &amp;ldquo;share your mental model&amp;rdquo; I mean; to explain the way you think about the problem, such that the other person understands the problem in the same way that you do.&lt;/p></description></item><item><title>zknill/vending-machine: vending machine in go</title><link>https://zknill.io/projects/vending-machine/</link><pubDate>Fri, 14 Jan 2022 09:43:00 +0000</pubDate><guid>https://zknill.io/projects/vending-machine/</guid><description>&lt;p>A vending machine implementation in go. The machine vends products, and calculates change:&lt;/p>
&lt;p>&lt;a href="https://github.com/zknill/vending">zknill/vending repo&lt;/a>&lt;/p></description></item><item><title>who wrote this shit?</title><link>https://zknill.io/posts/who-wrote-this-shit/</link><pubDate>Mon, 10 Jan 2022 14:13:26 +0000</pubDate><guid>https://zknill.io/posts/who-wrote-this-shit/</guid><description>&lt;p>&lt;strong>This post is a response to &lt;a href="https://www.heltweg.org/posts/who-wrote-this-shit/">Philip Heltweg&amp;rsquo;s &amp;ldquo;who wrote this shit&amp;rdquo; post&lt;/a>.&lt;/strong>&lt;/p>
&lt;p>I like the post linked above, it describes a junior developer who internalises the behaviour of ;&lt;/p>
&lt;ul>
&lt;li>see some bad code&lt;/li>
&lt;li>&lt;code>git blame&lt;/code> to find out who wrote it&lt;/li>
&lt;li>expect to see a name that you don&amp;rsquo;t recognise&lt;/li>
&lt;li>mutter with discontent&lt;/li>
&lt;/ul>
&lt;p>The author of the original post goes on to describe the case when he was pairing with a developer he respected, and found the bad code&amp;rsquo;s author was this very same developer.&lt;/p></description></item><item><title>connecting the dots requires listening</title><link>https://zknill.io/posts/connect-the-dots/</link><pubDate>Wed, 05 Jan 2022 09:53:04 +0000</pubDate><guid>https://zknill.io/posts/connect-the-dots/</guid><description>&lt;p>The &lt;del>best&lt;/del> most useful feedback follows a simple formula. Something like:&lt;/p>
&lt;blockquote>
&lt;ol>
&lt;li>When you &lt;code>[action or behaviour]&lt;/code>&lt;/li>
&lt;li>It has &lt;code>[effect on me or others]&lt;/code>&lt;/li>
&lt;li>Next time please &lt;code>[request]&lt;/code>&lt;/li>
&lt;/ol>
&lt;/blockquote>
&lt;p>This is easy to deal with and easy to connect the dots because they are laid out for you. It&amp;rsquo;s easy to digest.
It&amp;rsquo;s clear in this feedback formula what the &lt;code>behaviour&lt;/code>, the &lt;code>effect&lt;/code> and the change &lt;code>request&lt;/code>.
It&amp;rsquo;s hard &lt;em>not&lt;/em> to connect those dots; it&amp;rsquo;s hard not to understand what&amp;rsquo;s being asked of you.&lt;/p></description></item><item><title>product ideation is dysfunctional</title><link>https://zknill.io/posts/dysfunctional-product-ideation/</link><pubDate>Tue, 21 Dec 2021 14:39:36 +0000</pubDate><guid>https://zknill.io/posts/dysfunctional-product-ideation/</guid><description>&lt;p>Ideation in product management is a broken tool that does not &amp;ldquo;create ideas&amp;rdquo;, as it professes to do.
In this post I describe what&amp;rsquo;s wrong with garbage-ideation and how we can better achieve what &amp;ldquo;ideation&amp;rdquo; claims to provide.&lt;/p>
&lt;h3 id="characteristics-of-typical-ideation">characteristics of typical ideation&lt;/h3>
&lt;ol>
&lt;li>One or more 1hr meetings&lt;/li>
&lt;li>Meetings consisting of a whiteboard, post-it notes, and some structured exercise.&lt;/li>
&lt;li>A large number of stakeholders across a range of disciplines.&lt;/li>
&lt;/ol>
&lt;h3 id="definition">definition&lt;/h3>
&lt;p>When talking about a term, lets start with the definition:&lt;/p></description></item><item><title>bridge the gap in differences, with the right language</title><link>https://zknill.io/posts/language-to-solve-problems/</link><pubDate>Tue, 23 Nov 2021 21:36:49 +0000</pubDate><guid>https://zknill.io/posts/language-to-solve-problems/</guid><description>&lt;p>&lt;strong>If you can find the right language to explain a concept, you can quickly bridge the gap in you team members&amp;rsquo; different perspectives.&lt;/strong>&lt;/p>
&lt;p>Our brains are wired completely differently.
You&amp;rsquo;ve probably come across some of the archetypes for these differences; for example &lt;em>introverts&lt;/em>
vs. &lt;em>extroverts&lt;/em>.&lt;/p>
&lt;p>There are myriad different ways beyond introversion and extroversion that our brains work
differently.
These differences mean that we experience and approach problems entirely differently.
It&amp;rsquo;s really important to have the right language to talk about, understand, and archetype these
differences.&lt;/p></description></item><item><title>positive and negative freedoms</title><link>https://zknill.io/posts/freedoms/</link><pubDate>Fri, 29 Oct 2021 10:24:56 +0100</pubDate><guid>https://zknill.io/posts/freedoms/</guid><description>&lt;p>&lt;strong>We want to give our teams freedoms, but taking some things away can also be freedom&lt;/strong>.&lt;/p>
&lt;p>There are two kinds of freedom, positive and negative freedom.&lt;/p>
&lt;p>&lt;strong>Positive freedom&lt;/strong> is the traditional freedom, freedom to do something.
When your teams have positive freedom they can choose how to build their systems. They can choose what code or tech to use.
Positive freedom feels good, it leads to autonomy and mastery (two of the three things that people need from their work, the third is purpose).&lt;/p></description></item><item><title>failure demand</title><link>https://zknill.io/posts/failure-demand/</link><pubDate>Thu, 21 Oct 2021 13:27:44 +0100</pubDate><guid>https://zknill.io/posts/failure-demand/</guid><description>&lt;p>I&amp;rsquo;ve been looking for a concise way to describe:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;teams that ship a feature should be responsible for the long-term success of that feature,
including the customer ops caused by stuff missing from the feature&amp;rdquo;.&lt;/p>
&lt;/blockquote>
&lt;p>I believe our teams should have an area of ownership, and being responsible (and capable, and allowed) to change anything within their area — to make the team go faster.&lt;/p>
&lt;h2 id="failure-demand-vs-debt">failure demand vs. debt&lt;/h2>
&lt;p>What I now know, is that we are talking about &amp;ldquo;failure demand&amp;rdquo;. Failure demand is &lt;em>demand caused by a failure to do something or do something right for the customer.&lt;/em>&lt;/p></description></item><item><title>don't design a system to avoid a component you don't understand</title><link>https://zknill.io/posts/dont-design-dont-understand/</link><pubDate>Mon, 18 Oct 2021 15:38:11 +0100</pubDate><guid>https://zknill.io/posts/dont-design-dont-understand/</guid><description>&lt;p>Too often your fear of a component that you don&amp;rsquo;t know influences how you design your system.
Whether you call it &amp;lsquo;curiosity&amp;rsquo; or &amp;lsquo;being comfortable with ambiguity&amp;rsquo;, you need to be able to identify when you are seeking out, or shying away from, information.&lt;/p>
&lt;p>Here are some rules of thumb that might show when you are avoiding the complexity, rather than seeking to understand it:&lt;/p>
&lt;ol>
&lt;li>You cannot concisely describe why you&amp;rsquo;ve designed the system in a certain way.&lt;/li>
&lt;li>Your description of your design focuses more on what you don&amp;rsquo;t want to do, rather than what you do.
(We don&amp;rsquo;t want to touch X,Y,Z because of A,B,C.)&lt;/li>
&lt;li>Were you to design from scratch, your design would be significantly different.&lt;/li>
&lt;/ol>
&lt;p>Instead of these, &lt;strong>you should lean into what you don&amp;rsquo;t know, and make it known.&lt;/strong>
Don&amp;rsquo;t seek solutions that avoid the unknown component, because it will come back to bite you.&lt;/p></description></item><item><title>vuepress search h4</title><link>https://zknill.io/posts/vuepress-search-h4/</link><pubDate>Thu, 07 Oct 2021 10:30:38 +0100</pubDate><guid>https://zknill.io/posts/vuepress-search-h4/</guid><description>&lt;p>&lt;strong>Vuepress&amp;rsquo; default theme only searches h1 to h3.&lt;/strong>&lt;/p>
&lt;p>The default vuepress theme comes with header based search powered by &lt;a href="https://vuepress.vuejs.org/plugin/official/plugin-search.html">@vuepress/plugin-search&lt;/a>.
This search plugin operates on the Vue &lt;code>Page&lt;/code> object, which has a &lt;code>$page.headers&lt;/code> property (&lt;a href="https://vuepress.vuejs.org/miscellaneous/glossary.html#headers">docs&lt;/a>).&lt;/p>
&lt;p>The headers are extracted and added to the page object at build-time. &lt;strong>By default only h1, h2, and h3 headers are extracted&lt;/strong>.&lt;/p>
&lt;p>To change the headers that are included in the &lt;code>$page&lt;/code> you can add an &lt;code>markdown.extractHeaders&lt;/code> option to &lt;code>.vuepress/config.js&lt;/code>.&lt;/p></description></item><item><title>know your frogs 🐸</title><link>https://zknill.io/posts/frogs/</link><pubDate>Fri, 24 Sep 2021 08:38:22 +0100</pubDate><guid>https://zknill.io/posts/frogs/</guid><description>&lt;p>&lt;strong>The three rules of frogs&lt;/strong>&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>Eat the frog&lt;/strong>&lt;/p>
&lt;p>&amp;ldquo;The frog&amp;rdquo; is the task that you need to do the most, but want to do the least. It&amp;rsquo;s probably
your hardest of most ambiguous task. But can also be your most valuable. Avoiding the frogs
leads to &amp;ldquo;busy work&amp;rdquo;.&lt;/p>
&lt;p>Do your most important task first thing in the morning. Get it out the way &amp;ndash; and everything
else you do that day is an extra &amp;ldquo;win&amp;rdquo;.&lt;/p></description></item><item><title>habit tracker</title><link>https://zknill.io/posts/habit-tracker/</link><pubDate>Mon, 06 Sep 2021 20:04:35 +0100</pubDate><guid>https://zknill.io/posts/habit-tracker/</guid><description>&lt;p>There are three stages in a habit;&lt;/p>
&lt;ol>
&lt;li>Trigger &amp;ndash; something that starts your habit routine.&lt;/li>
&lt;li>Routine &amp;ndash; the habit behaviour, the action your take.&lt;/li>
&lt;li>Reward &amp;ndash; the benefit you gain from the behaviour.&lt;/li>
&lt;/ol>
&lt;p>When trying to adopt a new habit, I find the hardest stage is the trigger. &lt;em>Remembering&lt;/em> to do the
routine and reward. To solve this problem, &lt;strong>I created a habit tracker in Google Sheets.&lt;/strong> This post
describes that habit tracker, and the formula within it.&lt;/p></description></item><item><title>explain using analogies</title><link>https://zknill.io/posts/explain-using-analogies/</link><pubDate>Tue, 25 May 2021 13:11:43 +0100</pubDate><guid>https://zknill.io/posts/explain-using-analogies/</guid><description>&lt;h3 id="analogies-are-an-effective-way-of-explaining-complex-topics-to-non-technical-stakeholders">&lt;strong>analogies are an effective way of explaining complex topics to non-technical stakeholders&lt;/strong>.&lt;/h3>
&lt;p>Analogies are like building blocks for our thoughts.
When you use analogies to explain topics you allow your reader to draw on what they already know.
By drawing on their existing knowledge, your reader can understand new information faster.
They need to learn less new information and can fill-in the gaps in their own understanding using your analogy.&lt;/p></description></item><item><title>prefer regular adhoc meetings</title><link>https://zknill.io/posts/adhoc-recurring-meetings/</link><pubDate>Mon, 15 Mar 2021 14:25:30 +0000</pubDate><guid>https://zknill.io/posts/adhoc-recurring-meetings/</guid><description>&lt;h3 id="your-meeting-shouldnt-be-recurring-when-it-can-be-adhoc">your meeting shouldn&amp;rsquo;t be recurring, when it can be adhoc.&lt;/h3>
&lt;p>Here are some thoughts to consider when making a recurring meeting.
You can use these thoughts to consider if your meeting should actually be a &amp;lsquo;regular adhoc&amp;rsquo; meeting instead.&lt;/p>
&lt;h3 id="average-value">average value&lt;/h3>
&lt;p>&lt;strong>The value of the meeting is averaged across all the recurring-instances of the meeting&lt;/strong>.&lt;/p>
&lt;p>In this problem we see that the entire set of recurring meetings is seen as a single entity.
The sum of all value across all instances of the recurring meeting are averaged over all the instances of that recurring meeting.
This means that every time there&amp;rsquo;s an instance of a recurring meeting that has no agenda, no value, or is cancelled.. your overall value is impacted,
as this zero-value instance of the meeting drags the average value down.&lt;/p></description></item><item><title>notion: set full width off 🙏</title><link>https://zknill.io/posts/notion-wide/</link><pubDate>Tue, 02 Mar 2021 14:17:47 +0000</pubDate><guid>https://zknill.io/posts/notion-wide/</guid><description>&lt;p>&lt;strong>Please stop toggling notion to &amp;lsquo;Full Width&amp;rsquo;.&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://zknill.io/img/notion-wide-toggle.png" alt="notion wide toggle option">&lt;/p>
&lt;p>In notion there&amp;rsquo;s a toggle option that makes the text in the page take up the full width.&lt;/p>
&lt;p>This is not friendly to read. This site is formatted into a column because it means that your eyes
don&amp;rsquo;t have to travel as far to get to the beginning of the line. This is the same reason that;&lt;/p>
&lt;ul>
&lt;li>books are printed in A5&lt;/li>
&lt;li>that newspapers have columns&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>When turned on, it looks like this;&lt;/p></description></item><item><title>wireguard: private network, roaming</title><link>https://zknill.io/projects/wireguard-private-network/</link><pubDate>Tue, 02 Mar 2021 09:43:00 +0000</pubDate><guid>https://zknill.io/projects/wireguard-private-network/</guid><description>&lt;p>&lt;img src="https://zknill.io/img/wireguard-overview.png" alt="wireguard overview">&lt;/p>
&lt;p>This diagram shows my wireguard setup. Wireguard creates a virtual private network (VPN) that routes
&lt;em>just&lt;/em> traffic for the &lt;code>10.1.1.0/24&lt;/code> subnet (IPs &lt;code>10.1.1.0&lt;/code> to &lt;code>10.1.1.255&lt;/code>).&lt;/p>
&lt;p>&lt;strong>VPN is a misunderstood word&lt;/strong>. A VPN is a virtual private network, but it&amp;rsquo;s commonly used to refer
to technologies that provide privacy and security on a public wifi, or allows you to hide your
traffic from your ISP.&lt;/p>
&lt;h2 id="how-it-works">How it works&lt;/h2>
&lt;p>A VPN is a virtual private network. Here I use wireguard as a VPN with a new network interface added
to each device (&lt;code>wg0&lt;/code>). The &lt;code>wg0&lt;/code> network interface provides access to the &lt;code>10.1.1.0/24&lt;/code> range if
IPs. This virtual network is private, because only authorised members can access it.&lt;/p></description></item><item><title>object storage pricing</title><link>https://zknill.io/posts/object-storage-pricing/</link><pubDate>Tue, 16 Feb 2021 17:40:30 +0000</pubDate><guid>https://zknill.io/posts/object-storage-pricing/</guid><description>&lt;p>Understanding cloud pricing is pretty tricky. This post focuses on the projected costs of storing
backup data in an s3 compatible object store.&lt;/p>
&lt;p>I will show that &lt;strong>scaleway object storage is much better value than aws or backblaze (b2)&lt;/strong>.&lt;/p>
&lt;h3 id="prices">prices&lt;/h3>
&lt;p>Here is a breakdown of the $ USD / month cost of each provider, and their storage tier.&lt;/p>
&lt;p>&lt;em>Note; aws and b2 prices are in USD, scaleway use EUR. I converted the EUR to USD on 16th Feb 2021.&lt;/em>&lt;/p></description></item><item><title>time complexity</title><link>https://zknill.io/posts/time-complexity/</link><pubDate>Fri, 12 Feb 2021 13:27:24 +0000</pubDate><guid>https://zknill.io/posts/time-complexity/</guid><description>&lt;p>When solving problems using a computer, we assign &amp;ldquo;time classes&amp;rdquo; or time complexities to the
problems to describe how long that problem will take to solve.&lt;/p>
&lt;p>They look something like this image;&lt;/p>
&lt;p>&lt;img src="https://zknill.io/img/time-complexity.png" alt="time complexity graph">&lt;/p>
&lt;p>You can see on this graph the difference between the &amp;ldquo;amount of data&amp;rdquo; and &amp;ldquo;the time taken to find
the solution&amp;rdquo; (e.g. process the data).&lt;/p>
&lt;h3 id="good-time-complexity">&amp;ldquo;good&amp;rdquo; time complexity&lt;/h3>
&lt;p>Most problems are linear, that is as the amount of data increase, the time take increases
proportionally.
Sending a different email to each team member is a linear task.
As the number of team members increases, as does the time taken to send all the emails.&lt;/p></description></item><item><title>raspberry pi 4: nas and home server</title><link>https://zknill.io/projects/raspberry-pi-4/</link><pubDate>Sat, 30 Jan 2021 17:43:51 +0000</pubDate><guid>https://zknill.io/projects/raspberry-pi-4/</guid><description>&lt;h2 id="completed-build">Completed build&lt;/h2>
&lt;p>&lt;img src="https://zknill.io/img/projects-raspberrypi-completed.gif" alt="completed build">&lt;/p>
&lt;h3 id="component-list">Component list&lt;/h3>
&lt;ul>
&lt;li>Raspberry Pi 4 (4GB)&lt;/li>
&lt;li>Raspberry Pi official USB-C power supply&lt;/li>
&lt;li>2x Gigabyte 1TB SSD&lt;/li>
&lt;li>2x StarTech.com USB 3.0 to SATA HDD/SSD Adapter Cable&lt;/li>
&lt;li>TerraPi Alpha case: dual SSD &lt;a href="https://theterrapi.com/the-terrapi-alpha/">(link)&lt;/a>&lt;/li>
&lt;li>52Pi Low-Profile ICE Tower Cooling Fan&lt;/li>
&lt;/ul>
&lt;h2 id="build-notes">Build notes&lt;/h2>
&lt;p>The build is pretty simple, these notes are where I diverged from the instructions that came with
each of the components.&lt;/p>
&lt;p>&lt;strong>Cooler + Alpha case compatibility&lt;/strong>&lt;/p>
&lt;p>As can be seen in this official picture, the alpha pi case comes with a fan attachment for the top.&lt;/p></description></item><item><title>custom annotation naming</title><link>https://zknill.io/posts/custom-annotation-naming/</link><pubDate>Tue, 05 Jan 2021 13:24:29 +0000</pubDate><guid>https://zknill.io/posts/custom-annotation-naming/</guid><description>&lt;p>Custom annotations in Java should be named to describe what&amp;rsquo;s required of the method, and not which functionality we would like applied.&lt;/p>
&lt;p>For example;&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@RequiresRole&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">role&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ADMIN&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Instead of;&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-java" data-lang="java">&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@CheckRole&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">role&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ADMIN&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>If we start naming our annotations after &amp;ldquo;how&amp;rdquo; they work, and not &amp;ldquo;what&amp;rdquo; we want, we go down a route of also describing the edge and error cases.
It&amp;rsquo;s very easy for a &lt;code>@CheckXYZ&lt;/code> annotation to start accepting parameters that control; how the exception is thrown, which type of exception, etc.&lt;/p></description></item><item><title>estimate, predict, forecast</title><link>https://zknill.io/posts/forecast/</link><pubDate>Mon, 04 Jan 2021 13:18:01 +0000</pubDate><guid>https://zknill.io/posts/forecast/</guid><description>&lt;p>We are often asked, as engineers, to give estimates for how long a piece of work will take to complete or the complexity of the work.&lt;/p>
&lt;p>This is not a post about the philosophical point on &lt;em>if&lt;/em> we should estimate. This post proposes a change in the way we think, to help make estimates easier and more useful for
everyone.&lt;/p>
&lt;h3 id="what-are-estimates">what are estimates?&lt;/h3>
&lt;p>An estimate is:&lt;/p>
&lt;blockquote>
&lt;p>To calculate approximately (the amount, extent, magnitude, position, or value of something).&lt;/p></description></item><item><title>mfa key recovery</title><link>https://zknill.io/posts/mfa-key-recovery/</link><pubDate>Tue, 08 Dec 2020 15:29:27 +0000</pubDate><guid>https://zknill.io/posts/mfa-key-recovery/</guid><description>&lt;p>&lt;strong>Some SaaS tools mandate multi-factor authentication (MFA), but this is bad for security and bad UX, until someone solves key recovery&lt;/strong>.&lt;/p>
&lt;p>Multi-factor auth revolves around two proofs:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>proof&lt;/th>
 &lt;th>example&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>Something I know&lt;/td>
 &lt;td>password&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>Something I have&lt;/td>
 &lt;td>multi factor device; e.g. smart phone with rotating code, phone number, hardware key like yubikey&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>MFA (depending on implementation) creates much stronger account security, particularly from leaked passwords. The problem is with humans.&lt;/p>
&lt;p>If we ignore the class of security breaches where a human is deceived (e.g. phishing), we are still left with: humans are prone to mistakes. We will always lose our multi-factor device. Without the second
factor, we still need some means of logging into our accounts.&lt;/p></description></item><item><title>inclusion</title><link>https://zknill.io/posts/inclusion/</link><pubDate>Fri, 04 Dec 2020 10:04:00 +0000</pubDate><guid>https://zknill.io/posts/inclusion/</guid><description>&lt;p>As a team grows, inclusion becomes harder.&lt;/p>
&lt;p>When we were a very small startup, our engineering team was 3 people. With a small team, it makes sense to include the team in decisions.
Not including an individual team member accidentally sends a strong message about their value and contribution.&lt;/p>
&lt;p>As we grow as a business, inclusion needs to adapt also. It&amp;rsquo;s no longer feasible to include every team member in every decision.&lt;/p></description></item><item><title>naming is hard</title><link>https://zknill.io/posts/naming-is-hard/</link><pubDate>Mon, 05 Oct 2020 09:27:26 +0100</pubDate><guid>https://zknill.io/posts/naming-is-hard/</guid><description>&lt;p>&lt;strong>Software engineers use convergent thinking&lt;/strong>&lt;/p>
&lt;p>As software engineers, we engage in a type of thinking called &amp;ldquo;convergent thinking&amp;rdquo;. According
to &lt;a href="https://en.wikipedia.org/wiki/Convergent_thinking">wikipedia&lt;/a> this is:&lt;/p>
&lt;blockquote>
&lt;p>[A] type of thinking that focuses on coming up with the single, well-established answer to a problem.&lt;/p>
&lt;/blockquote>
&lt;p>In code we have many different names for this, but they all boil down to the same thing: convergent
thinking.&lt;/p>
&lt;p>But we don&amp;rsquo;t call it &amp;lsquo;convergent thinking&amp;rsquo;, instead we call it:&lt;/p></description></item><item><title>syncthing</title><link>https://zknill.io/posts/syncthing/</link><pubDate>Thu, 28 May 2020 06:47:40 +0100</pubDate><guid>https://zknill.io/posts/syncthing/</guid><description>&lt;p>Syncthing is a peer-to-peer file sync app, this means that each of your devices communicates with it&amp;rsquo;s peers,
within your own private syncthing network structure.
To enable this, you need to decide how you want your devices to interact with each other.&lt;/p>
&lt;p>Syncthing has the concepts of &amp;lsquo;devices&amp;rsquo; and &amp;lsquo;folders&amp;rsquo;. Devices are the participants in your
peer-to-peer network. Folders are a grouping of files that have the same sharing properties. A
single folder can be configured to be shared with all, or just some, or the devices on your network.&lt;/p></description></item><item><title>hyperoptic ipv6</title><link>https://zknill.io/posts/hyperoptic-ipv6/</link><pubDate>Wed, 15 Apr 2020 11:35:17 +0100</pubDate><guid>https://zknill.io/posts/hyperoptic-ipv6/</guid><description>&lt;p>&lt;strong>Setup third party router to work with hyperoptic IPv6&lt;/strong>&lt;/p>
&lt;p>Hyperoptic, the UK ISP, offer IPv6 support.
Here is a link to their FAQ page: &lt;a href="https://www.hyperoptic.com/faq/posts/static-ip-addresses/">https://www.hyperoptic.com/faq/posts/static-ip-addresses/&lt;/a>&lt;/p>
&lt;p>But I had to configure my router manually to get provisioned with an IPv6 address.&lt;/p>
&lt;p>&lt;strong>Steps from hyperoptic&lt;/strong>&lt;/p>
&lt;p>Hyperoptic share some steps for setting up a 3rd party router in the FAQ post linked above.
Those steps are:&lt;/p>
&lt;ol>
&lt;li>Navigate to WAN interface section of router web GUI&lt;/li>
&lt;li>Select dual stack option for WAN interface (IPv4/IPv6)&lt;/li>
&lt;li>For address/prefix source select DHCPv6 (not static option)&lt;/li>
&lt;li>Enable prefix delegation (PD)&lt;/li>
&lt;li>Select SLAAC for IPv6 address creation method&lt;/li>
&lt;/ol>
&lt;p>I use a TP-Link Archer A7 1750 router, and many of these steps had a direct equivalent.&lt;/p></description></item><item><title>video conferencing</title><link>https://zknill.io/posts/video-conferencing/</link><pubDate>Thu, 09 Apr 2020 15:52:35 +0100</pubDate><guid>https://zknill.io/posts/video-conferencing/</guid><description>&lt;p>Many of us have moved to remote working, due to corona virus. I&amp;rsquo;ve been working remotely for the
last 5 weeks.&lt;/p>
&lt;p>The following are some of the improvements that I have adopted:&lt;/p>
&lt;h2 id="external-webcam">External webcam&lt;/h2>
&lt;p>&lt;strong>Webcam always on&lt;/strong>&lt;/p>
&lt;p>Having video on is much more engaging for participants, and helps to mitigate some of those visual
communication cues that you might miss out on when working remotely.&lt;/p>
&lt;p>The gitlab company handbook does a better job of describing these points.
Link to the handbook: &lt;a href="https://about.gitlab.com/handbook/communication/#video-calls">gitlab.com&lt;/a>&lt;/p></description></item><item><title>what makes a good software engineer?</title><link>https://zknill.io/posts/good-software-engineer/</link><pubDate>Thu, 26 Mar 2020 07:58:05 +0000</pubDate><guid>https://zknill.io/posts/good-software-engineer/</guid><description>&lt;p>&lt;em>I came across this question, and it got me thinking. This a result of that thinking.&lt;/em>&lt;/p>
&lt;blockquote>
&lt;p>&lt;strong>How would you find out if somebody is a good software engineer?&lt;/strong>&lt;/p>
&lt;/blockquote>
&lt;p>When answering this question, you must first detail what you are testing for.
This leaves you with some questions, the following are some of those questions:&lt;/p>
&lt;ul>
&lt;li>What is the problem domain?&lt;/li>
&lt;li>What are the external constraints of the system they are working within?&lt;/li>
&lt;li>What are good and bad characteristics / abilities in the context of the problem?&lt;/li>
&lt;li>What tests will bring these characteristics to light?&lt;/li>
&lt;/ul>
&lt;p>Answering these questions will allow you to start to define &amp;ldquo;success&amp;rdquo;. Importantly these &amp;ldquo;success&amp;rdquo;
criteria take into account the environment that the engineer is working in.&lt;/p></description></item><item><title>pr checklist</title><link>https://zknill.io/posts/pr-checklist/</link><pubDate>Sun, 22 Mar 2020 19:50:06 +0000</pubDate><guid>https://zknill.io/posts/pr-checklist/</guid><description>&lt;p>This post contains checklists that intend to increase the value received from, and ease of, the code review
process.&lt;/p>
&lt;h2 id="checklists-reduce-complexity">checklists reduce complexity&lt;/h2>
&lt;p>Checklists help to capture the information that we often forget. This post is aimed at software
engineers who review code. This code review checklist covers the following areas:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>Preparing for code review&lt;/strong>: preparations sets your reviewers up for success.&lt;/li>
&lt;li>&lt;strong>Performing a code review&lt;/strong>: think before you react, share your intentions and goals.&lt;/li>
&lt;li>&lt;strong>Responding to a code review&lt;/strong>: what to do when you&amp;rsquo;ve received the review.&lt;/li>
&lt;/ol>
&lt;p>You have a responsibility as the PR author and PR reviewer to make a code review function well.
Code reviews are not just an input by the reviewer. There are things that the PR author can do to
achieve the most value from a review.&lt;/p></description></item><item><title>mvp service level objectives</title><link>https://zknill.io/posts/mvp-slo/</link><pubDate>Mon, 02 Mar 2020 14:54:38 +0000</pubDate><guid>https://zknill.io/posts/mvp-slo/</guid><description>&lt;p>Apply &amp;ldquo;Service level objectives&amp;rdquo; to product requirements to get an MVP.
SLOs help to decide the level of service that a product will provide to a customer.
SLOs are often used to define or describe the error budget of a service in the Site Reliability Engineering world.&lt;/p>
&lt;p>&lt;a href="https://landing.google.com/sre/sre-book/chapters/service-level-objectives/">Google SRE book &amp;ldquo;service level&amp;rdquo; definitions&lt;/a>.&lt;/p>
&lt;p>&lt;strong>We can apply SLOs to product requirements, to build define an MVP.&lt;/strong>&lt;/p>
&lt;p>Let&amp;rsquo;s follow a concrete example:&lt;/p></description></item><item><title>beware anecdotal evidence</title><link>https://zknill.io/posts/beware-anecdotal-evidence/</link><pubDate>Mon, 17 Feb 2020 11:51:55 +0000</pubDate><guid>https://zknill.io/posts/beware-anecdotal-evidence/</guid><description>&lt;p>&lt;strong>Anecdotal evidence comes in many forms, and sounds most believable.&lt;/strong>&lt;/p>
&lt;blockquote>
&lt;p>Anecdotal evidence is evidence from anecdotes: evidence collected in a casual or informal manner
and relying heavily or entirely on personal testimony.&lt;/p>
&lt;p>&amp;ndash; &lt;a href="https://en.wikipedia.org/wiki/Anecdotal_evidence">&lt;em>wikipedia&lt;/em>&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>Anecdotal evidence takes different forms. I&amp;rsquo;ve encountered many team members who are vivid
storytellers, drawing from previous experiences with phrases like:&lt;/p>
&lt;ol>
&lt;li>&amp;ldquo;this is how I&amp;rsquo;ve seen it done before&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;at my previous job we did&amp;hellip;&amp;rdquo;&lt;/li>
&lt;li>&amp;ldquo;a former mentor of mine said to me&amp;hellip;&lt;/li>
&lt;/ol>
&lt;p>The problem with anecdotes is that they cannot be assessed. This opens them up to a range of
issues and biases, that it&amp;rsquo;s not possible as the person listening to the anecdote to correctly
evaluate. Memories are not objective representations of the situation, and will suffer from
selection and survivorship bias (the belief that because something went well, it was the right thing
to do). Anecdotes suffer from availability bias, the more recent the information consumed, the more
weight and relevance associated to it.&lt;/p></description></item><item><title>data modelling in saas apps</title><link>https://zknill.io/posts/data-modelling-saas/</link><pubDate>Wed, 29 Jan 2020 15:19:49 +0000</pubDate><guid>https://zknill.io/posts/data-modelling-saas/</guid><description>&lt;p>Article originally posted on &lt;a href="https://medium.com/attest-engineering/data-modelling-in-a-saas-app-6c228747035a">medium.com&lt;/a>&lt;/p>
&lt;p>&amp;ndash;&lt;/p>
&lt;p>When building a SaaS application, there are some similarities between apps, regardless of the service that they provide. This article lays out some of the most common cases, and the errors that it’s easy to make. We all have pre-conceived notions of how some flows should work, e.g. “login”. Perhaps those notions do not well translate into a flexible data model that allows for changing requirements.
Below are some “rules of thumb” to help avoid some of the most common pitfalls. This is not meant to be an extensive of definitive guide, only food for thought.
The examples below use a mixture of relational and document store modelling. The examples are only to support the point being argued and not to serve as a reference implementation.&lt;/p></description></item><item><title>effectiveness as an engineer</title><link>https://zknill.io/posts/effectiveness-as-an-engineer/</link><pubDate>Mon, 20 Jan 2020 08:15:42 +0000</pubDate><guid>https://zknill.io/posts/effectiveness-as-an-engineer/</guid><description>&lt;p>Your effectiveness as an engineer is not defined by the output that you deliver.&lt;/p>
&lt;blockquote>
&lt;p>Software engineering is what happens to programming &lt;/br>
when you add time and other programmers.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://research.swtch.com/vgo-eng">Russ Cox&lt;/a>&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>Programming is the pure and simple out out of instructions that tell a computer how to do some task.
It&amp;rsquo;s the purest type of code writing, you and the machine. It&amp;rsquo;s &amp;ldquo;getting a program to work&amp;rdquo;, it&amp;rsquo;s
&amp;ldquo;solving a problem&amp;rdquo;. Programming is a solo activity, you and the computer.
Your effectiveness as a programmer can be measured in terms of how well the program works.&lt;/p></description></item><item><title>brooks law, hierarchy, and async comms</title><link>https://zknill.io/posts/brooks-law-hierarchy-async-comms/</link><pubDate>Thu, 16 Jan 2020 08:08:35 +0000</pubDate><guid>https://zknill.io/posts/brooks-law-hierarchy-async-comms/</guid><description>&lt;h3 id="brooks-law">brooks law&lt;/h3>
&lt;p>There&amp;rsquo;s a thing called &amp;ldquo;Brooks Law&amp;rdquo; (from mythical-man-month) which explains some of the reasons
that software goes slower when adding more humans to a project.&lt;/p>
&lt;p>One of the points that the &amp;ldquo;law&amp;rdquo; states is that:&lt;/p>
&lt;ul>
&lt;li>there&amp;rsquo;s a combinatorial explosion of communication channels as the number of people in the team rise&lt;/li>
&lt;/ul>
&lt;p>When we consider what this looks like visually&lt;/p>
&lt;p>&lt;img src="https://zknill.io/img/brooks-law.png#center" alt="Brooks law">&lt;/p>
&lt;p>&amp;hellip; it&amp;rsquo;s clear that as the number of people in a team rises, the number of communication pathways
explodes.&lt;/p></description></item><item><title>experienced engineers pattern match</title><link>https://zknill.io/posts/experienced-engineer-pattern-matching/</link><pubDate>Wed, 11 Dec 2019 12:25:17 +0000</pubDate><guid>https://zknill.io/posts/experienced-engineer-pattern-matching/</guid><description>&lt;p>&lt;strong>Software engineering is complex not complicated.&lt;/strong>&lt;/p>
&lt;p>Developing software is often more complex than it is complicated. That&amp;rsquo;s to say; the number of
components involved in the development of a non-trivial system is large enough to increase the
complexity. Complexity is not difficulty.&lt;/p>
&lt;p>Complicated refers to the difficulty level, there might be few components but it will take a lot of hard work to solve.&lt;/p>
&lt;p>Given many of the jobs of software engineering can be distilled down to&amp;hellip;&lt;/p></description></item><item><title>short vs. long lifetime structs</title><link>https://zknill.io/posts/struct-lifetime/</link><pubDate>Tue, 26 Nov 2019 16:51:49 +0000</pubDate><guid>https://zknill.io/posts/struct-lifetime/</guid><description>&lt;h1 id="short-vs-long-lifetime-structs">Short vs. Long lifetime structs&lt;/h1>
&lt;p>Much of the go code that we write the structure of; do some operation that might fail, return value and error, handle error if present, continue with value. Here&amp;rsquo;s an example:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nx">s&lt;/span> &lt;span class="nx">Service&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="nf">Login&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userID&lt;/span> &lt;span class="kt">int64&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="kt">error&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">user&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="nx">s&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">repo&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">User&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">userID&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="nx">err&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="kc">nil&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="nx">User&lt;/span>&lt;span class="p">{},&lt;/span> &lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Errorf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;fetch user: %w&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> 
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// trimmed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="kc">nil&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This is a relatively simple example, but I will show how with multiple method calls which return errors, the intent of the original function can easily be confused or lost in the noise of error handling.&lt;/p></description></item><item><title>go 1.13: errors.As(...)</title><link>https://zknill.io/posts/errors-as/</link><pubDate>Sun, 27 Oct 2019 09:32:51 +0000</pubDate><guid>https://zknill.io/posts/errors-as/</guid><description>&lt;p>&lt;strong>Use return values on errors behaviours when testing with &lt;code>errors.As(...)&lt;/code>&lt;/strong>&lt;/p>
&lt;p>For an overview of go1.13 errors package, read the &lt;a href="https://blog.golang.org/go1.13-errors">go blog post&lt;/a>. It describes two new functions &lt;code>errors.Is(...)&lt;/code> which operates like an equality check and &lt;code>errors.As(...)&lt;/code> which operates like a type assertion.&lt;/p>
&lt;p>The example in the blog lists:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">e&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="nx">QueryError&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="nx">errors&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">As&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="nx">e&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// err is a *QueryError, and e is set to the error&amp;#39;s value
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Similar to:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// if e, ok := err.(*QueryError); ok { … }
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Dave Cheney describes error handling patterns in this &lt;a href="https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully">blog post&lt;/a> and recommends omitting constants and sentinel errors in favour of errors with behaviours.&lt;/p></description></item><item><title>cube2222/octosql: query tool</title><link>https://zknill.io/projects/octosql/</link><pubDate>Fri, 19 Jul 2019 06:41:26 +0100</pubDate><guid>https://zknill.io/projects/octosql/</guid><description>&lt;p>Octosql is a query tool that allows you to join, analyse and transform data from multiple databases
and file formats using SQL.&lt;/p>
&lt;p>&lt;a href="https://github.com/cube2222/octosql/commit/22f13777ef0182a0e1bcd6a2dbbc64c6197c2d3d">https://github.com/cube2222/octosql/commit/22f13777ef0182a0e1bcd6a2dbbc64c6197c2d3d&lt;/a>&lt;/p></description></item><item><title>the power and danger of consistency</title><link>https://zknill.io/posts/consistency/</link><pubDate>Thu, 20 Jun 2019 08:02:49 +0100</pubDate><guid>https://zknill.io/posts/consistency/</guid><description>&lt;p>&lt;strong>Humans crave to be, or appear to be, consistent.&lt;/strong>&lt;/p>
&lt;p>This craving, when combined with commitment (notably public commitments) and you have a
powerful and dangerous combination.&lt;/p>
&lt;p>Consistency in software engineering is often thought of in code formatting, or data retention
contexts, but the most interesting consistency is in interactions between engineers and decision
making.&lt;/p>
&lt;h3 id="consistency-presents-in-different-ways">&lt;strong>Consistency presents in different ways&lt;/strong>&lt;/h3>
&lt;p>On the underground in London, I often notice an curious interaction between passengers: there
will be an empty seat, and neither of the commuters will want to sit in it. Even if this means
standing in a more cramped and busy environment. (Lets put health / physical well-being reasons aside
for a moment).&lt;/p></description></item><item><title>high performing teams burnout together</title><link>https://zknill.io/posts/team-burnout/</link><pubDate>Thu, 02 May 2019 08:07:51 +0100</pubDate><guid>https://zknill.io/posts/team-burnout/</guid><description>&lt;p>High performing teams have a number of characteristics that contribute to their success.
They have &lt;a href="https://online.seu.edu/articles/high-and-low-context-cultures/">high context culture&lt;/a> and each of the individuals are highly aware of their contribution and impact on the team. They have a high level of group EQ and consider the emotions and needs of each of the team members.&lt;/p>
&lt;p>These desirable characteristics of teams also make all the team members highly susceptible to the
same set of external factors that could cause burnout for an individual team member. That is to
say&amp;hellip;&lt;/p></description></item><item><title>metrics for team contributions</title><link>https://zknill.io/posts/metrics-github-search/</link><pubDate>Mon, 29 Apr 2019 06:41:09 +0100</pubDate><guid>https://zknill.io/posts/metrics-github-search/</guid><description>&lt;h2 id="smart-goals">SMART goals&lt;/h2>
&lt;p>The best goals have metrics associated to them, one method of setting goals is to make them SMART.&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">S - Specific
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">M - Measurable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">A - Achievable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">R - Realistic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">T - Timebound
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>The &lt;code>M&lt;/code>, and &lt;code>T&lt;/code> of smart can be hard to quantify without overly intrusive metrics recording. Often
time-recording is very poorly implemented and in SRE terms this &amp;ldquo;overhead&amp;rdquo;&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>Administrative work not tied directly to running a service.
Examples include hiring, HR paperwork, team/company meetings, bug queue hygiene, snippets,
peer reviews and self-assessments, and training courses.&lt;/p></description></item><item><title>kubernetes generated names</title><link>https://zknill.io/posts/kubernetes-generated-names/</link><pubDate>Fri, 26 Apr 2019 09:26:38 +0100</pubDate><guid>https://zknill.io/posts/kubernetes-generated-names/</guid><description>&lt;p>&lt;strong>Naming is hard&lt;/strong>&lt;/p>
&lt;p>Naming is hard, especially for kubernetes resources that are some of the lowest possible deployable
units, like pods, replication controllers and jobs.&lt;/p>
&lt;p>Some of these resources cannot be changed once they are created. e.g. re-running a job with a
different image version is not possible without changing it&amp;rsquo;s name.&lt;/p>
&lt;p>But there&amp;rsquo;s a &lt;code>generateName&lt;/code> field on the yaml manifests that we can use instead:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;span class="lnt">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">batch/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Job&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">generateName&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">rerunnablejob-&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">labels&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">rerunnablejob&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">containers&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">myjob&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">image&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">my/image:v0.0.1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;container&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;arguments&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">restartPolicy&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Never&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Now when the job is started it will get a unique generated name like: &lt;code>rerunnablejob-zgj92&lt;/code>.&lt;/p></description></item><item><title>docs requirements</title><link>https://zknill.io/posts/docs/</link><pubDate>Thu, 11 Apr 2019 09:29:38 +0100</pubDate><guid>https://zknill.io/posts/docs/</guid><description>&lt;p>All organisations need the ability to share knowledge and written communication is one of the most
scalable forms. But in many orgs, docs don&amp;rsquo;t get the love they need to succeed.&lt;/p>
&lt;p>&lt;strong>Writing is hard&lt;/strong> and I&amp;rsquo;ve seen many engineers that don&amp;rsquo;t think that being able to write well is a
skill that&amp;rsquo;s useful. I think that written communication is the most important for if your org wants
to scale without huge meeting overhead. I don&amp;rsquo;t want to touch on what makes good writing, or even
what makes good documentations. Instead, lets look at what environment your documentation needs to
succeed and get the love it deserves.&lt;/p></description></item><item><title>Evolution of application monitoring: Linkerd2</title><link>https://zknill.io/talks/evolution-of-monitoring-linkerd2/</link><pubDate>Thu, 11 Apr 2019 08:18:34 +0100</pubDate><guid>https://zknill.io/talks/evolution-of-monitoring-linkerd2/</guid><description>&lt;p>&lt;em>London Microservices User Group - April 2019. Hosted at FarFetch.&lt;/em>&lt;/p>
&lt;p>This talk focuses on the how we have evolved our approach to application monitoring at Attest over
the last 3 years that I&amp;rsquo;ve been working there.&lt;/p>
&lt;h1 id="overview">Overview&lt;/h1>
&lt;p>&lt;strong>&lt;a href="https://zknill.io/slides/evolution-app-monitoring-linkerd2.pdf">Link to slides.&lt;/a>&lt;/strong>&lt;/p>
&lt;p>Starting with the history of attest, we walk through the very basic approach we had to monitoring
and alerting back in April 2016. Touching on each of the changes that we made taking the
architecture and platform through:&lt;/p></description></item><item><title>git: co-authored by</title><link>https://zknill.io/posts/git-co-authored-by/</link><pubDate>Fri, 29 Mar 2019 08:19:47 +0000</pubDate><guid>https://zknill.io/posts/git-co-authored-by/</guid><description>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">Co-authored-by: &amp;lt;zknill@users.noreply.github.com&amp;gt;
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;blockquote>
&lt;p>You can attribute a commit to more than one author by adding one or more Co-authored-by trailers to the commit&amp;rsquo;s message. Co-authored commits are visible on GitHub and can be included in the profile contributions graph and the repository&amp;rsquo;s statistics.&lt;/p>
&lt;/blockquote>
&lt;p>Source: &lt;a href="https://help.github.com/en/articles/creating-a-commit-with-multiple-authors">github&lt;/a>&lt;/p>
&lt;p>Co-author feature on github allows more than one author to be attributed to a commit. &lt;strong>But for what purpose?&lt;/strong> If we consider the purpose of the origial &lt;code>Author:&lt;/code> field of a git commit it has a number of purposes:&lt;/p></description></item><item><title>linkerd: a conversation with real platform owners</title><link>https://zknill.io/talks/linkerd2-webinar/</link><pubDate>Thu, 28 Mar 2019 22:14:04 +0000</pubDate><guid>https://zknill.io/talks/linkerd2-webinar/</guid><description>&lt;h4 id="best-practices-for-monitoring-application-health-on-kubernetes">Best practices for monitoring application health on Kubernetes&lt;/h4>
&lt;p>This webinar digs into observability, and how linkerd and other tools can help with the observability and alert response landscape.&lt;/p>
&lt;p>As a panelist I contributed to the discussion and added unique insight into vetting linkerd2 and it&amp;rsquo;s impact in production and it&amp;rsquo;s differences from linkerd1.&lt;/p>
&lt;p>We touched on how linkerd helps and some tips to take back to your own orgs.&lt;/p>
&lt;p>&lt;a href="https://www.cncf.io/community/webinars/best-practices-for-monitoring-application-health-on-kubernetes/">Link to the video.&lt;/a>&lt;/p></description></item><item><title>linkerd/linkerd2: support for daemonsets</title><link>https://zknill.io/projects/linkerd2/</link><pubDate>Mon, 25 Mar 2019 21:53:56 +0000</pubDate><guid>https://zknill.io/projects/linkerd2/</guid><description>&lt;h4 id="support-for-daemonsets">Support for DaemonSets&lt;/h4>
&lt;p>Linkerd2 is a service mesh and network proxy.&lt;/p>
&lt;p>Add daemonsets to all observability commands and add dashboard support.&lt;/p>
&lt;p>&lt;a href="https://github.com/linkerd/linkerd2">https://github.com/linkerd/linkerd2&lt;/a>&lt;/p></description></item><item><title>micromanagement</title><link>https://zknill.io/posts/micromanagement/</link><pubDate>Mon, 25 Mar 2019 17:04:14 +0000</pubDate><guid>https://zknill.io/posts/micromanagement/</guid><description>&lt;h1 id="the-creeping-and-insidious-nature-of-micromanagement">The creeping and insidious nature of micromanagement&lt;/h1>
&lt;p>Micromanagement:&lt;/p>
&lt;blockquote>
&lt;p>In business management, micromanagement is a management style whereby a manager closely observes and/or controls the work of his/her subordinates or employees.&lt;/p>
&lt;/blockquote>
&lt;p>Insidious:&lt;/p>
&lt;blockquote>
&lt;p>proceeding in a gradual, subtle way, but with very harmful effects.&lt;/p>
&lt;/blockquote>
&lt;p>Micromanagement is insidious, it&amp;rsquo;s creeping. We never know we have it or it&amp;rsquo;s affecting us until we are deep in it&amp;rsquo;s grasps.
At this point the organisational change required to undo it is possible too large for anything meaningful to happen.&lt;/p></description></item><item><title>go get private repo</title><link>https://zknill.io/posts/go-get-private-repo/</link><pubDate>Mon, 25 Mar 2019 16:43:38 +0000</pubDate><guid>https://zknill.io/posts/go-get-private-repo/</guid><description>&lt;h1 id="go-modules">Go Modules&lt;/h1>
&lt;p>Go modules is becomming the standard go dependency tooling. It reuses teh &lt;code>go get&lt;/code> command that&amp;rsquo;s been around for a long time.&lt;/p>
&lt;p>&lt;code>go get&lt;/code> doesn&amp;rsquo;t automatically handle private repos well.&lt;/p>
&lt;p>If you usually clone repos using ssh, then you need to run the following git config command:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-sh" data-lang="sh">&lt;span class="line">&lt;span class="cl">git config --global url.&lt;span class="s1">&amp;#39;git@github.com:&amp;#39;&lt;/span>.insteadOf &lt;span class="s1">&amp;#39;https://github.com/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>It updates &lt;code>$HOME/.gitconfig&lt;/code> with:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">[url &amp;#34;git@github.com:&amp;#34;]
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	insteadOf = https://github.com/
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Then you can:&lt;/p></description></item><item><title>Keeping a logbook with vim</title><link>https://zknill.io/posts/vim-logbook/</link><pubDate>Wed, 20 Mar 2019 17:43:44 +0000</pubDate><guid>https://zknill.io/posts/vim-logbook/</guid><description>&lt;p>There&amp;rsquo;s a lot to be said for embracing basic tools for getting stuff done. Keeping track of notes is a great example of where some basic tooling can achieve the same result as paid services.&lt;/p>
&lt;h2 id="my-setup">My setup&lt;/h2>
&lt;ol>
&lt;li>Vim, some aliases and templates&lt;/li>
&lt;li>Dropbox, for sync&lt;/li>
&lt;li>Grep, for search&lt;/li>
&lt;/ol>
&lt;h4 id="file-structure">File Structure&lt;/h4>
&lt;p>I keep a single file for each day of the year, using the date format: &lt;code>YYYY-MM-DD&lt;/code>.
This makes search easy. Terminal completions in &lt;code>zsh&lt;/code> shell work well with this format.&lt;/p></description></item><item><title>2018: reading list</title><link>https://zknill.io/posts/2018-books/</link><pubDate>Sun, 16 Dec 2018 14:17:57 +0000</pubDate><guid>https://zknill.io/posts/2018-books/</guid><description>&lt;p>The computing book&amp;rsquo;s I&amp;rsquo;ve read&amp;hellip;&lt;/p>
&lt;ol>
&lt;li>
&lt;p>&lt;strong>The Go Progamming Langauge&lt;/strong> - &lt;em>Alan A. A. Donovan and Brian Kernighan&lt;/em>&lt;/p>
&lt;p>This is a golang language bible. It contains great material for both beginners and pros. Including some strong documentation on more advanced topics like reflection.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Design Patterns: Elements of Reusable Object-Oriented Software&lt;/strong> - &lt;em>Erich Gamma, John Vlissides, Ralph Johnson, and Richard Helm&lt;/em>&lt;/p>
&lt;p>Sometimes called the &amp;ldquo;gang of four&amp;rdquo; book, this is a classic.&lt;/p></description></item><item><title>the cost of extrinsic motivators</title><link>https://zknill.io/posts/extrinsic-motivators/</link><pubDate>Sun, 25 Nov 2018 13:19:48 +0000</pubDate><guid>https://zknill.io/posts/extrinsic-motivators/</guid><description>&lt;p>The best output comes from engineers who are intrinsically motivated, according to Dan Pink in his book &amp;ldquo;Drive&amp;rdquo;.
He details strong arguments around how complex, creative, right brain work is hindered by extrinsic motivations (e.g. pay for performance schemes) and is bolstered by intrinsic motivations (e.g. the internal desire to achieve, the feeling of accomplishment).&lt;/p>
&lt;p>As an industry we&amp;rsquo;ve already identified that many of the pay for performance style schemes and metrics are detrimental to the creation of stable software. Measuring software engineers on lines of code or tickets delivered is commonly accepted as flawed when the desired outcome is a correct and bug free product. Measuring performance on lines of code, both max and min, creates an environment where engineers start optimising for the metric, which is detrimental to the quality of the codebase.&lt;/p></description></item><item><title>options make decisions</title><link>https://zknill.io/posts/options-make-decisions/</link><pubDate>Sat, 27 Oct 2018 17:01:30 +0100</pubDate><guid>https://zknill.io/posts/options-make-decisions/</guid><description>&lt;p>We are makers and solution creators, and as makers optimising for solving problems it&amp;rsquo;s easy to focus on a working solution.
The problem is solved.&lt;/p>
&lt;p>But as in refactoring, where a working solution is not &amp;ldquo;done&amp;rdquo; (there&amp;rsquo;s a whole bunch of reorganising and rework that makes the solution beautiful, not just functional), a solved solution should not be the goal of problem solving.&lt;/p>
&lt;p>The best solutions come from options, the best solutions come from multiple solutions. Sure you may come up with the best choice immediately. You may think of other alternatives, dismiss them and conclude that your original idea is the best. Now your original idea is validated by the dismissal of the competing alternatives.&lt;/p></description></item><item><title>Limitations of EKS</title><link>https://zknill.io/posts/eks-limitations/</link><pubDate>Thu, 20 Sep 2018 13:38:08 +0100</pubDate><guid>https://zknill.io/posts/eks-limitations/</guid><description>&lt;p>On Sep 5, 2018 AWS &lt;a href="https://aws.amazon.com/about-aws/whats-new/2018/09/amazon-eks-available-in-ireland/">announced&lt;/a> EKS availbility in eu-west-1 (Ireland). So I checked it out; this post covers some of the limitations.&lt;/p>
&lt;h2 id="pod-limits-per-instance-type">Pod limits per instance type&lt;/h2>
&lt;p>The EKS networking CNI plugin used &lt;a href="https://github.com/aws/amazon-vpc-cni-k8s">github&lt;/a> exploits elastic network interfaces (ENIs) and attaches them to the EKS worker node instances. This allows the instance to have more than one IP, and each pod on the node gets one of those IPs.&lt;/p>
&lt;p>There are limits to the number of ENIs you can attach to each instance type, and how many IPs that ENI can have.
Each pod gets an IP, including those running in the kube-system namespace, such as kube-dns, kube-proxy, aws-node etc.&lt;/p></description></item><item><title>go concurrency correctness</title><link>https://zknill.io/posts/go-concurrency-correctness/</link><pubDate>Tue, 04 Sep 2018 06:51:47 +0100</pubDate><guid>https://zknill.io/posts/go-concurrency-correctness/</guid><description>&lt;p>Go has great concurrency support and as with all concurrent code, it&amp;rsquo;s very easy to write code that&amp;rsquo;s doesn&amp;rsquo;t perform as expected or intended.
This is a look at correctness in concurrent go, and some of the considerations.&lt;/p>
&lt;h2 id="correctness">correctness&lt;/h2>
&lt;p>How do we define correctness, the dictionary definition is:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;the quality or state of being free from error; accuracy.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>But as a whole, correctness is a deep and well read computer science topic. It includes a basis for formally verifying a program; the act of proving or disproving an algorithm, with respect to a formal specification.&lt;/p></description></item><item><title>proverb driven development</title><link>https://zknill.io/posts/proverb-driven-development/</link><pubDate>Wed, 29 Aug 2018 06:18:48 +0100</pubDate><guid>https://zknill.io/posts/proverb-driven-development/</guid><description>&lt;p>Learning to drive at 17, my instructor had a bunch of proverbs or phrases that he used to describe different situations that you&amp;rsquo;d be in as a driver, and what your reaction to them should be.&lt;/p>
&lt;ol>
&lt;li>Keep it simple, keep it safe&lt;/li>
&lt;li>Only a fool breaks the 2 second rule&lt;/li>
&lt;li>When in doubt, don&amp;rsquo;t go out&lt;/li>
&lt;li>Less space less speed&lt;/li>
&lt;li>Gears to go, brakes to slow&lt;/li>
&lt;/ol>
&lt;p>All of them are applicable to software engineering. Lets break them down, one-by-one.&lt;/p></description></item><item><title>go's anonymous functions in loops</title><link>https://zknill.io/posts/gos-anonymous-functions-in-loops/</link><pubDate>Sun, 13 May 2018 18:05:41 +0100</pubDate><guid>https://zknill.io/posts/gos-anonymous-functions-in-loops/</guid><description>&lt;p>In May 2017 I wrote a &lt;a href="https://zknill.io/posts/golang-gotchas/#varaiables-in-a-for-loop">post&lt;/a> about some gotchas in golang from my first few years with the language.&lt;/p>
&lt;p>In that post I included this example code:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">printers&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">strings&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="p">[]&lt;/span>&lt;span class="kt">string&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="s">&amp;#34;a&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;b&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;c&amp;#34;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">str&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="nx">strings&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nx">printers&lt;/span> &lt;span class="p">=&lt;/span> &lt;span class="nb">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">printers&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">func&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span> &lt;span class="nb">print&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">str&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="nx">_&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">print&lt;/span> &lt;span class="o">:=&lt;/span> &lt;span class="k">range&lt;/span> &lt;span class="nx">printers&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nb">print&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>When run this will always give the output:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-plaintext" data-lang="plaintext">&lt;span class="line">&lt;span class="cl">c
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">c
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">c
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>This example is an interesting one, because it&amp;rsquo;s a serial example of a deeper, often concurrent problem.
&lt;strong>The following is a breakdown of the serial and concurrent versions of this problem and an explantion of why they happen:&lt;/strong>&lt;/p></description></item><item><title>programming mental models</title><link>https://zknill.io/posts/programming-mental-models/</link><pubDate>Fri, 20 Apr 2018 15:48:01 +0100</pubDate><guid>https://zknill.io/posts/programming-mental-models/</guid><description>&lt;h1 id="mental-models">mental models&lt;/h1>
&lt;p>As complexity grows in projects, decision making, planning, modelling, reasoning, programming! I&amp;rsquo;ve found that it&amp;rsquo;s far easier to make correct decisions using a more structured way of thinking. For this I use mental models;&lt;/p>
&lt;p>So&amp;hellip; What is a mental model?&lt;/p>
&lt;h2 id="definition">definition&lt;/h2>
&lt;blockquote>
&lt;p>&amp;ldquo;A mental model is an explanation of someone&amp;rsquo;s thought process about how something works in the real world. It is a representation of the surrounding world, the relationships between its various parts and a person&amp;rsquo;s intuitive perception about his or her own acts and their consequences.&amp;rdquo;
&lt;a href="https://en.wikipedia.org/wiki/Mental_model">wikipedia&lt;/a>&lt;/p></description></item><item><title>working with me</title><link>https://zknill.io/posts/working-with-me/</link><pubDate>Wed, 14 Mar 2018 19:52:11 +0000</pubDate><guid>https://zknill.io/posts/working-with-me/</guid><description>&lt;h3 id="what-its-like-to-work-with-me">What it&amp;rsquo;s like to work with me&lt;/h3>
&lt;p>The easiest way to understand someone you work with, or might work with in the future, is to understand a little about them and how they work.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>I expect the best from those I work with&lt;/strong>. I expect people to deliver a task to the best of their ability. Sometime I set high standards for what I think you can achieve, which might lead to a difficult code review when you don&amp;rsquo;t reach that standard. I&amp;rsquo;ve found it always better to expect the best from people. If you think I&amp;rsquo;ve been unfair, feel free to call me out on it. I can quickly check myself.&lt;/p></description></item><item><title>go ast got visitor pattern wrong</title><link>https://zknill.io/posts/go-ast-got-visitor-pattern-wrong/</link><pubDate>Sun, 25 Feb 2018 18:21:39 +0000</pubDate><guid>https://zknill.io/posts/go-ast-got-visitor-pattern-wrong/</guid><description>&lt;h3 id="whats-a-visitor">what&amp;rsquo;s a visitor?&lt;/h3>
&lt;p>According to wikipedia:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;The visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existent object structures without modifying the structures. It is one way to follow the open/closed principle.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>The Visitor pattern, as described in wikipedia allows you to &amp;ldquo;Visit&amp;rdquo; a structure in a way that doesn&amp;rsquo;t require the structure to change.
This is particularly good in scenarios such as traversal of trees.&lt;/p></description></item><item><title>libraries considered harmful</title><link>https://zknill.io/posts/libraries-considered-harmful/</link><pubDate>Sat, 06 Jan 2018 18:42:10 +0000</pubDate><guid>https://zknill.io/posts/libraries-considered-harmful/</guid><description>&lt;h2 id="libraries-considered-harmful">libraries considered harmful&lt;/h2>
&lt;p>Library is shared code that you cannot otherwise name.
It is a confused amalgamation of different parts of code, different usages and different business rules.
It serves more than a single purpose, and thus; no well defined and clear purpose.
It is hard to update, hard to work with, and hard to use.&lt;/p>
&lt;p>Many of us get a nasty feeling when some shared code ends up in a package or class called &lt;em>Util&lt;/em>. Why is this?&lt;/p></description></item><item><title>Delivering Language Agnostic Microservices</title><link>https://zknill.io/talks/delivering-language-agnostic-microservices/</link><pubDate>Fri, 05 Jan 2018 17:59:34 +0000</pubDate><guid>https://zknill.io/talks/delivering-language-agnostic-microservices/</guid><description>&lt;p>I work for a company called Attest, and we transitioned from only a very small number of services to many more with the ability to create new ones easily (and monitor and manage them!)
This talk &lt;a href="https://skillsmatter.com/skillscasts/11361-delivering-language-agnostic-microservices">(video)&lt;/a> focuses on some of the language agnostic changes that we made to enable the delivery of small, well defined services&amp;hellip; microservices!&lt;/p>
&lt;h2 id="in-the-beginning">In the beginning&lt;/h2>
&lt;p>At the start when you only have a very small team, few enough to feed with 2 pizzas, you don&amp;rsquo;t need a distributed system.
You don&amp;rsquo;t need a microservice architecture. Instead you can have one main app, a monolith, that does everything. Something like this:&lt;/p></description></item><item><title>slack @here is bad for your team</title><link>https://zknill.io/posts/slack-@here-is-bad-for-your-team/</link><pubDate>Tue, 19 Dec 2017 19:33:09 +0000</pubDate><guid>https://zknill.io/posts/slack-@here-is-bad-for-your-team/</guid><description>&lt;blockquote>
&lt;p>@here Notify active channel members&lt;/p>
&lt;/blockquote>
&lt;p>The slack help &lt;a href="https://get.slack.help/hc/en-us/articles/202009646-Make-an-announcement">docs&lt;/a> say the &lt;code>@here&lt;/code> mention;&lt;/p>
&lt;p>&lt;em>&amp;hellip;lets you notify just the members in a channel who are currently active in Slack.&lt;/em>&lt;/p>
&lt;p>Alright, that sounds great. It&amp;rsquo;s for making announcements / contacting the team right?
Well no. Slack is best at async but instant communication for teams. &lt;strong>&lt;code>@here&lt;/code> ruins that.&lt;/strong>&lt;/p>
&lt;h3 id="cross-pollinate-teams">Cross pollinate teams&lt;/h3>
&lt;p>Your company is probably made up of a bunch of different teams, sales, marketing, engineering etc.
And there are many reasons why the commercial side of your business should be open to input from engineers.
Not only because it reduces the number of communication paths, reduces layers of overhead and focuses your engineers on
users and commercial goals; but because the cross pollination of ideas helps all the teams involved.&lt;/p></description></item><item><title>zknill/builder: java builder class code generator</title><link>https://zknill.io/projects/builder/</link><pubDate>Mon, 20 Nov 2017 08:01:04 +0000</pubDate><guid>https://zknill.io/projects/builder/</guid><description>&lt;p>&lt;a href="https://github.com/zknill/builder">&lt;code>builder&lt;/code>&lt;/a> generates a class that implements the builder pattern.&lt;/p>
&lt;p>The command:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">builder -s title --class BookTitle
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Generates:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt"> 1
&lt;/span>&lt;span class="lnt"> 2
&lt;/span>&lt;span class="lnt"> 3
&lt;/span>&lt;span class="lnt"> 4
&lt;/span>&lt;span class="lnt"> 5
&lt;/span>&lt;span class="lnt"> 6
&lt;/span>&lt;span class="lnt"> 7
&lt;/span>&lt;span class="lnt"> 8
&lt;/span>&lt;span class="lnt"> 9
&lt;/span>&lt;span class="lnt">10
&lt;/span>&lt;span class="lnt">11
&lt;/span>&lt;span class="lnt">12
&lt;/span>&lt;span class="lnt">13
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">public static class Builder {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> private String title;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public Builder title(final String title) {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> this.title = title;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return this;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> public BookTitle build() {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> return new BookTitle(this);
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> }
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>There are a number of different flags that represent different variable types, use &lt;code>builder --help&lt;/code> for info.&lt;/p></description></item><item><title>Delivering Go Services</title><link>https://zknill.io/talks/delivering-go-services/</link><pubDate>Sun, 19 Nov 2017 19:41:34 +0000</pubDate><guid>https://zknill.io/talks/delivering-go-services/</guid><description>&lt;p>&lt;em>Go London User Group - 15th November 2017&lt;/em>&lt;/p>
&lt;p>&lt;a href="https://www.youtube.com/watch?v=pRdfJTuGxEw">Video&lt;/a>&lt;/p>
&lt;p>This talk focuses on delivery of go services and some of the changes that we made to bring golang into our stack,
and optimise our delivery pipeline.&lt;/p></description></item><item><title>mastering golang taught me java</title><link>https://zknill.io/posts/mastering-golang-taught-me-java/</link><pubDate>Mon, 13 Nov 2017 15:07:51 +0000</pubDate><guid>https://zknill.io/posts/mastering-golang-taught-me-java/</guid><description>&lt;p>I spend much of my time writing go, but the rest is occupied predominantly by java.
It&amp;rsquo;s a well known idiom that new languages at least take inspiration from (if not recycle) the best features of previous language.
There&amp;rsquo;s a lot to be taken from the similarities between languages.&lt;/p>
&lt;p>This is a comparison of go and java and some of the go features that have made me write better java.&lt;/p></description></item><item><title>masterminds/semver: work with semantic versions in go</title><link>https://zknill.io/projects/semver/</link><pubDate>Wed, 04 Oct 2017 15:54:54 +0100</pubDate><guid>https://zknill.io/projects/semver/</guid><description>&lt;p>The &lt;a href="https://github.com/Masterminds/semver">semver&lt;/a> package provides the ability to work with Semantic Versions in Go. Specifically it provides the ability to:&lt;/p>
&lt;ul>
&lt;li>Parse semantic versions&lt;/li>
&lt;li>Sort semantic versions&lt;/li>
&lt;li>Check if a semantic version fits within a set of constraints&lt;/li>
&lt;/ul></description></item><item><title>on meetup talks</title><link>https://zknill.io/posts/on-meetup-talks/</link><pubDate>Sat, 15 Jul 2017 20:49:47 +0100</pubDate><guid>https://zknill.io/posts/on-meetup-talks/</guid><description>&lt;p>I recently gave a talk at London Microservices User Group meetup - &lt;a href="https://zknill.io/talks/proof-of-concept-to-prod/">video here&lt;/a>.&lt;/p>
&lt;p>These are some rules I &lt;em>tried&lt;/em> to live by:&lt;/p>
&lt;ul>
&lt;li>who&amp;rsquo;s the audience?&lt;/li>
&lt;li>what&amp;rsquo;s your story?&lt;/li>
&lt;li>what&amp;rsquo;s the learning?&lt;/li>
&lt;li>get feedback.&lt;/li>
&lt;li>don&amp;rsquo;t be scared to not know.&lt;/li>
&lt;li>don&amp;rsquo;t be scared to share what you know.&lt;/li>
&lt;/ul>
&lt;h2 id="whos-the-audience">who&amp;rsquo;s the audience?&lt;/h2>
&lt;p>Know who you are talking to. Whatever the subject of the talk, make sure it&amp;rsquo;s appropriate for the venue and group that you are talking too.
Go along to a few of the meetings, watch some videos from previous events, find out everything you can in advance.&lt;/p></description></item><item><title>Go Microservices: Proof of Concept to Production</title><link>https://zknill.io/talks/proof-of-concept-to-prod/</link><pubDate>Fri, 07 Jul 2017 22:05:22 +0100</pubDate><guid>https://zknill.io/talks/proof-of-concept-to-prod/</guid><description>&lt;p>&lt;em>London Microservices Meetup - 5th July 2017&lt;/em>&lt;/p>
&lt;p>&lt;a href="https://skillsmatter.com/skillscasts/9714-london-microservices-user-group-july#video">Video&lt;/a>&lt;/p>
&lt;p>Go is a powerful language, but there are a few gotchas. This talk will cover transitioning a proof of concept to a production ready go service, the decisions to be made, the benefits and the pitfalls, and what we liked and disliked about it doing it ourselves.&lt;/p></description></item><item><title>building blocks of go services</title><link>https://zknill.io/posts/building-blocks-of-go-services/</link><pubDate>Sun, 28 May 2017 16:23:43 +0100</pubDate><guid>https://zknill.io/posts/building-blocks-of-go-services/</guid><description>&lt;p>Go has many web frameworks, but none that come with the standard library. There are many to choose from but this is a difficult problem. There are advantages to choosing a very well established framework, but also disadvantages like lock-in, support and flexibility. When you do not know what you will required from a framework in 3 / 6 / 9 months, how can you make that choice?&lt;/p>
&lt;p>Instead of a built in framework, go provides a number of well thought out building blocks from which you can roll your own! These building blocks mean that frameworks are unnecessary to get you off the ground and are unnecessary when you find you need to add additional pieces to your apps.&lt;/p></description></item><item><title>golang/dep: dependency management tool</title><link>https://zknill.io/projects/dep/</link><pubDate>Sun, 28 May 2017 16:06:02 +0100</pubDate><guid>https://zknill.io/projects/dep/</guid><description>&lt;p>There are many blog posts, &lt;a href="https://blog.gopheracademy.com/advent-2016/saga-go-dependency-management/">like the one&lt;/a>, that do a great job of explaining the history behind golang package management and the need for &lt;code>golang/dep&lt;/code>.&lt;/p>
&lt;p>Breifly: &lt;code>dep&lt;/code> is a package management tool to unite them all, and be merged into the standard go toolchain.&lt;/p>
&lt;p>&lt;a href="https://github.com/golang/dep">https://github.com/golang/dep&lt;/a>&lt;/p></description></item><item><title>function as an interface</title><link>https://zknill.io/posts/function-as-an-interface/</link><pubDate>Thu, 18 May 2017 21:03:24 +0100</pubDate><guid>https://zknill.io/posts/function-as-an-interface/</guid><description>&lt;blockquote>
&lt;p>&amp;ldquo;An interface type is defined as a set of method signatures.&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>Taking a simple example:&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">type Fooer interface {
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> Foo(input string) string
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&lt;em>golang nameing convention is interfaces that end in &lt;code>er&lt;/code>, Reader, Writer, Closer etc..&lt;/em>&lt;/p>
&lt;p>In this example, the interface &lt;code>Fooer&lt;/code> defines a single method &lt;code>Foo&lt;/code>, any struct with a method &lt;code>Foo&lt;/code> will implement this interface, (implementation is done implicitly).&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-gdscript3" data-lang="gdscript3">&lt;span class="line">&lt;span class="cl">&lt;span class="n">type&lt;/span> &lt;span class="n">aFoo&lt;/span> &lt;span class="n">struct&lt;/span> &lt;span class="p">{}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">func&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">aFoo&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">Foo&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">input&lt;/span> &lt;span class="n">string&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="n">string&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s2">&amp;#34;foo: &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="n">input&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>But it&amp;rsquo;s not just structs that can implement interfaces, if an interface has a single method, then you can implement it with a function type.&lt;/p></description></item><item><title>golang gotchas</title><link>https://zknill.io/posts/golang-gotchas/</link><pubDate>Wed, 10 May 2017 18:33:31 +0100</pubDate><guid>https://zknill.io/posts/golang-gotchas/</guid><description>&lt;p>Go is a small language&amp;hellip;&lt;/p>
&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-fallback" data-lang="fallback">&lt;span class="line">&lt;span class="cl">+----------+---------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| LANGUAGE | NUM KEYWORDS |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+---------------+
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| golang | 25 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| C | 32 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| python | 33 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">| java | 50 |
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">+----------+---------------+
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>&amp;hellip; it has only a handful of keywords, and a small set of language features. This makes it incredibly easy to learn, but it&amp;rsquo;s not without hiccups. The following is a list of gotchas to be aware of&amp;hellip;&lt;/p></description></item><item><title>structs and named types</title><link>https://zknill.io/posts/structs-and-named-types/</link><pubDate>Mon, 24 Apr 2017 21:30:48 +0100</pubDate><guid>https://zknill.io/posts/structs-and-named-types/</guid><description>&lt;p>I recently blogged about &lt;a href="http://zknill.io/blog/patterns-in-go/">patterns in go&lt;/a>
and in that we saw how methods on structs can be used to implement interfaces. Specifically the &lt;code>Stringer&lt;/code> interface.&lt;/p>
&lt;p>Structs, are not the only way to achieve this; all named types offer similar functionality. First - some definitions from the golang spec&amp;hellip;&lt;/p>
&lt;h2 id="struct-types">struct types&lt;/h2>
&lt;blockquote>
&lt;p>&amp;ldquo;A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (AnonymousField). Within a struct, non-blank field names must be unique.&amp;rdquo; - &lt;a href="https://golang.org/ref/spec#Struct_types">golang spec&lt;/a>&lt;/p></description></item><item><title>deepend</title><link>https://zknill.io/posts/deepend/</link><pubDate>Fri, 14 Apr 2017 17:49:51 +0100</pubDate><guid>https://zknill.io/posts/deepend/</guid><description>&lt;h3 id="getting-started">getting started&lt;/h3>
&lt;p>Imagine a world in which you&amp;rsquo;re a graduate, you&amp;rsquo;ve just completed your final year.
Fresh in your mind is the difference between NP-Hard and NP-Complete problems.
You can sort a btree with your eyes closed.
You&amp;rsquo;re ready to become a contributing member of society.
Or are you? I have a few tough truths for you&amp;hellip;&lt;/p>
&lt;ol>
&lt;li>You don&amp;rsquo;t know anything useful, yet!&lt;/li>
&lt;li>You&amp;rsquo;re not going to have a clue what you&amp;rsquo;re doing on the first day&amp;hellip;&lt;/li>
&lt;li>You&amp;rsquo;ll always have much to learn.&lt;/li>
&lt;li>You&amp;rsquo;re soon going to find carbs are a bad lunchtime friend.&lt;/li>
&lt;/ol>
&lt;p>&amp;hellip; but these shouldn&amp;rsquo;t put you off jumping in at the deepend, it will be the best thing that you do!&lt;/p></description></item><item><title>patterns in go</title><link>https://zknill.io/posts/patterns-in-go/</link><pubDate>Wed, 05 Apr 2017 18:56:00 +0100</pubDate><guid>https://zknill.io/posts/patterns-in-go/</guid><description>&lt;p>In go there are some patterns that make code &lt;em>idomatic&lt;/em>, these are some of my favourite&amp;hellip;&lt;/p>
&lt;h2 id="strategy-pattern">strategy pattern&lt;/h2>
&lt;blockquote>
&lt;p>&amp;ldquo;Enables an algorithm&amp;rsquo;s behavior to be selected at runtime.&amp;rdquo; &lt;a href="https://en.wikipedia.org/wiki/Strategy_pattern">Wikipedia&lt;/a>&lt;/p>
&lt;/blockquote>
&lt;p>This pattern allows you to pass structs, or objects around that have slightly different runtime behavior without having to worry about the difference.
There&amp;rsquo;s a great talk by Dave Cheney about &lt;a href="https://dave.cheney.net/2016/11/13/do-not-fear-first-class-functions">functions as first class citizens.&lt;/a>
This talk does a great job of explaning why and how to use functions in some places instead of interfaces, and this is closely linked to the strategy pattern.&lt;/p></description></item><item><title>developer abc</title><link>https://zknill.io/posts/developer-abc/</link><pubDate>Sun, 26 Mar 2017 20:24:02 +0000</pubDate><guid>https://zknill.io/posts/developer-abc/</guid><description>&lt;p>We all know the acronym ABC&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;Always be coding&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>&amp;hellip;the idea that the more you code, the better you get. The idea that deliberate practice
makes you better. The experience of many more problems makes you better.
You grow that gut feel, for when code smells bad, for a problem that you&amp;rsquo;ve solved before.
It&amp;rsquo;s a natural assumption. But &lt;em>&amp;ldquo;Always be coding&amp;rdquo;&lt;/em> isn&amp;rsquo;t enough, there are other C&amp;rsquo;s that you need to consider.
&lt;em>&amp;ldquo;Consider&amp;rdquo;&lt;/em> being one of them!&lt;/p></description></item><item><title>downtime</title><link>https://zknill.io/posts/downtime/</link><pubDate>Mon, 20 Mar 2017 21:54:34 +0000</pubDate><guid>https://zknill.io/posts/downtime/</guid><description>&lt;p>Downtime.&lt;/p>
&lt;blockquote>
&lt;p>&lt;em>noun&lt;/em>&lt;/p>
&lt;ol>
&lt;li>&lt;/li>
&lt;/ol>
&lt;p>time during which a machine, especially a computer, is out of action or unavailable for use.&lt;/p>
&lt;/blockquote>
&lt;p>Your site is not serving:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>There&amp;rsquo;s been a catastrophic failure and you&amp;rsquo;ve deleted the directory that your database engine stores data in.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You’re performing database maintenance and downtime is necessary.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You’re executing complex database migrations that result in downtime.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>For any developer this is an anxious time. A time when we are all on edge. If the downtime is planned; it&amp;rsquo;s chosen because it&amp;rsquo;s the best outcome from your sum cost comparisons. Yet it still doesn&amp;rsquo;t sit well, you wonder about the affected users. You wonder about the impact. It’s not a place we want to be on a regular basis. If it&amp;rsquo;s unplanned downtime it&amp;rsquo;s a whole other level of concentration. A new kind of focus, all thoughts and energy directed towards the fix and bringing your site back up. Never have you been so productive at 3am.&lt;/p></description></item><item><title>don't fear the junior engineer</title><link>https://zknill.io/posts/dont-fear-the-junior-engineer/</link><pubDate>Wed, 15 Mar 2017 20:57:05 +0000</pubDate><guid>https://zknill.io/posts/dont-fear-the-junior-engineer/</guid><description>&lt;p>The phrase:&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;we were all a learner once&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>is normally applied to learner drivers, but it also applies to the junior engineer. Just like learning to drive a car, there’s a gap, a void, that has to be crossed. For new drivers it’s passing a test and you are then deemed worthy or capable of handling a car on your own, for engineers there is no “Driving Test” to say you are now worthy. There are many different routes into the industry and most (if not all) of them suffer from this gap, or void.&lt;/p></description></item><item><title>hello world</title><link>https://zknill.io/posts/hello-world/</link><pubDate>Thu, 09 Mar 2017 22:17:00 +0000</pubDate><guid>https://zknill.io/posts/hello-world/</guid><description>&lt;div class="highlight">&lt;div class="chroma">
&lt;table class="lntable">&lt;tr>&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code>&lt;span class="lnt">1
&lt;/span>&lt;span class="lnt">2
&lt;/span>&lt;span class="lnt">3
&lt;/span>&lt;span class="lnt">4
&lt;/span>&lt;span class="lnt">5
&lt;/span>&lt;span class="lnt">6
&lt;/span>&lt;span class="lnt">7
&lt;/span>&lt;span class="lnt">8
&lt;/span>&lt;span class="lnt">9
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td class="lntd">
&lt;pre tabindex="0" class="chroma">&lt;code class="language-go" data-lang="go">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">package&lt;/span> &lt;span class="nx">main&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="s">&amp;#34;fmt&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">func&lt;/span> &lt;span class="nf">main&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">	&lt;span class="nx">fmt&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nf">Printf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s">&amp;#34;Hello, world.\n&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div></description></item><item><title>zknill/points: a slack and cli leaderboard!</title><link>https://zknill.io/projects/points/</link><pubDate>Wed, 28 Sep 2016 15:54:54 +0100</pubDate><guid>https://zknill.io/projects/points/</guid><description>&lt;p>&lt;a href="https://github.com/zknill/points">&lt;code>points&lt;/code>&lt;/a> was born out of a joke in the office&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>&amp;ldquo;10 points to whoever can tell me the answer to &amp;hellip;&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>So the natural thing to do next was to build a small go program that could track a leaderboard and display the points in a nice format.&lt;/p>
&lt;p>&lt;a href="https://github.com/zknill/points">https://github.com/zknill/points&lt;/a>&lt;/p>
&lt;hr>
&lt;p>&lt;code>points&lt;/code> allows you to add names and scores and keeps a tally of who&amp;rsquo;s in the lead.&lt;/p>
&lt;p>&lt;code>points&lt;/code> outputs the leaderboard as an ascii table that can easily be shared in your team&amp;rsquo;s chat.&lt;/p></description></item><item><title>about</title><link>https://zknill.io/about/</link><pubDate>Sat, 22 Aug 2015 00:00:00 +0000</pubDate><guid>https://zknill.io/about/</guid><description>&lt;img src="https://zknill.io/img/home-avatar.jpg" alt="Zak Knill" class="profile-img">
&lt;h1 id="zak-knill">&lt;strong>Zak Knill&lt;/strong>&lt;/h1>
&lt;p>&lt;em>staff engineer&lt;/em>&lt;/p>
&lt;p>I work on:&lt;/p>
&lt;ul>
&lt;li>leading teams 🔝&lt;/li>
&lt;li>upskilling engineers 💪&lt;/li>
&lt;li>solving scaling problems; org, and tech 📈&lt;/li>
&lt;li>delivering customer value 🤑&lt;/li>
&lt;/ul>
&lt;p>I am a staff engineer, and an org and tech leader.
I spend much of my time sharing my knowledge with other engineers and up-skilling.
I&amp;rsquo;m currently working on Realtime data problems at &lt;a href="https://ably.com">Ably&lt;/a>, previously &lt;a href="https://unweave.io">Unweave&lt;/a> and &lt;a href="https://askattest.com">Attest&lt;/a>.
I am a seasoned reviewer of PRs, I give &lt;a href="./talks">talks&lt;/a>, write &lt;a href="./posts">blog&lt;/a> &lt;a href="https://medium.com/@zknill">articles&lt;/a> and contribute to &lt;a href="./projects">opensource&lt;/a>.&lt;/p></description></item><item><title/><link>https://zknill.io/cv/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/cv/</guid><description>&lt;html>&lt;head>&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>&lt;title>Zak Knill&lt;/title>&lt;style>
/* cspell:disable-file */
/* webkit printing magic: print all background colors */
html {
	-webkit-print-color-adjust: exact;
}
* {
	box-sizing: border-box;
	-webkit-print-color-adjust: exact;
}

html,
body {
	margin: 0;
	padding: 0;
}
@media only screen {
	body {
		margin: 2em auto;
		max-width: 900px;
		color: rgb(55, 53, 47);
	}
}

body {
	line-height: 1.5;
	white-space: pre-wrap;
}

a,
a.visited {
	color: inherit;
	text-decoration: underline;
}

.pdf-relative-link-path {
	font-size: 80%;
	color: #444;
}

h1,
h2,
h3 {
	letter-spacing: -0.01em;
	line-height: 1.2;
	font-weight: 600;
	margin-bottom: 0;
}

.page-title {
	font-size: 2.5rem;
	font-weight: 700;
	margin-top: 0;
	margin-bottom: 0.75em;
}

h1 {
	font-size: 1.875rem;
	margin-top: 1.875rem;
}

h2 {
	font-size: 1.5rem;
	margin-top: 1.5rem;
}

h3 {
	font-size: 1.25rem;
	margin-top: 1.25rem;
}

.source {
	border: 1px solid #ddd;
	border-radius: 3px;
	padding: 1.5em;
	word-break: break-all;
}

.callout {
	border-radius: 3px;
	padding: 1rem;
}

figure {
	margin: 1.25em 0;
	page-break-inside: avoid;
}

figcaption {
	opacity: 0.5;
	font-size: 85%;
	margin-top: 0.5em;
}

mark {
	background-color: transparent;
}

.indented {
	padding-left: 1.5em;
}

hr {
	background: transparent;
	display: block;
	width: 100%;
	height: 1px;
	visibility: visible;
	border: none;
	border-bottom: 1px solid rgba(55, 53, 47, 0.09);
}

img {
	max-width: 100%;
}

@media only print {
	img {
		max-height: 100vh;
		object-fit: contain;
	}
}

@page {
	margin: 1in;
}

.collection-content {
	font-size: 0.875rem;
}

.column-list {
	display: flex;
	justify-content: space-between;
}

.column {
	padding: 0 1em;
}

.column:first-child {
	padding-left: 0;
}

.column:last-child {
	padding-right: 0;
}

.table_of_contents-item {
	display: block;
	font-size: 0.875rem;
	line-height: 1.3;
	padding: 0.125rem;
}

.table_of_contents-indent-1 {
	margin-left: 1.5rem;
}

.table_of_contents-indent-2 {
	margin-left: 3rem;
}

.table_of_contents-indent-3 {
	margin-left: 4.5rem;
}

.table_of_contents-link {
	text-decoration: none;
	opacity: 0.7;
	border-bottom: 1px solid rgba(55, 53, 47, 0.18);
}

table,
th,
td {
	border: 1px solid rgba(55, 53, 47, 0.09);
	border-collapse: collapse;
}

table {
	border-left: none;
	border-right: none;
}

th,
td {
	font-weight: normal;
	padding: 0.25em 0.5em;
	line-height: 1.5;
	min-height: 1.5em;
	text-align: left;
}

th {
	color: rgba(55, 53, 47, 0.6);
}

ol,
ul {
	margin: 0;
	margin-block-start: 0.6em;
	margin-block-end: 0.6em;
}

li > ol:first-child,
li > ul:first-child {
	margin-block-start: 0.6em;
}

ul > li {
	list-style: disc;
}

ul.to-do-list {
	text-indent: -1.7em;
}

ul.to-do-list > li {
	list-style: none;
}

.to-do-children-checked {
	text-decoration: line-through;
	opacity: 0.375;
}

ul.toggle > li {
	list-style: none;
}

ul {
	padding-inline-start: 1.7em;
}

ul > li {
	padding-left: 0.1em;
}

ol {
	padding-inline-start: 1.6em;
}

ol > li {
	padding-left: 0.2em;
}

.mono ol {
	padding-inline-start: 2em;
}

.mono ol > li {
	text-indent: -0.4em;
}

.toggle {
	padding-inline-start: 0em;
	list-style-type: none;
}

/* Indent toggle children */
.toggle > li > details {
	padding-left: 1.7em;
}

.toggle > li > details > summary {
	margin-left: -1.1em;
}

.selected-value {
	display: inline-block;
	padding: 0 0.5em;
	background: rgba(206, 205, 202, 0.5);
	border-radius: 3px;
	margin-right: 0.5em;
	margin-top: 0.3em;
	margin-bottom: 0.3em;
	white-space: nowrap;
}

.collection-title {
	display: inline-block;
	margin-right: 1em;
}

.simple-table {
	margin-top: 1em;
	font-size: 0.875rem;
}

.simple-table-header {
	background: rgb(247, 246, 243);
	color: black;
	font-weight: 500;
}

time {
	opacity: 0.5;
}

.icon {
	display: inline-block;
	max-width: 1.2em;
	max-height: 1.2em;
	text-decoration: none;
	vertical-align: text-bottom;
	margin-right: 0.5em;
}

img.icon {
	border-radius: 3px;
}

.user-icon {
	width: 1.5em;
	height: 1.5em;
	border-radius: 100%;
	margin-right: 0.5rem;
}

.user-icon-inner {
	font-size: 0.8em;
}

.text-icon {
	border: 1px solid #000;
	text-align: center;
}

.page-cover-image {
	display: block;
	object-fit: cover;
	width: 100%;
	max-height: 30vh;
}

.page-header-icon {
	font-size: 3rem;
	margin-bottom: 1rem;
}

.page-header-icon-with-cover {
	margin-top: -0.72em;
	margin-left: 0.07em;
}

.page-header-icon img {
	border-radius: 3px;
}

.link-to-page {
	margin: 1em 0;
	padding: 0;
	border: none;
	font-weight: 500;
}

p > .user {
	opacity: 0.5;
}

td > .user,
td > time {
	white-space: nowrap;
}

input[type="checkbox"] {
	transform: scale(1.5);
	margin-right: 0.6em;
	vertical-align: middle;
}

p {
	margin-top: 0.5em;
	margin-bottom: 0.5em;
}

.image {
	border: none;
	margin: 1.5em 0;
	padding: 0;
	border-radius: 0;
	text-align: center;
}

.code,
code {
	background: rgba(135, 131, 120, 0.15);
	border-radius: 3px;
	padding: 0.2em 0.4em;
	border-radius: 3px;
	font-size: 85%;
	tab-size: 2;
}

code {
	color: #eb5757;
}

.code {
	padding: 1.5em 1em;
}

.code-wrap {
	white-space: pre-wrap;
	word-break: break-all;
}

.code > code {
	background: none;
	padding: 0;
	font-size: 100%;
	color: inherit;
}

blockquote {
	font-size: 1.25em;
	margin: 1em 0;
	padding-left: 1em;
	border-left: 3px solid rgb(55, 53, 47);
}

.bookmark {
	text-decoration: none;
	max-height: 8em;
	padding: 0;
	display: flex;
	width: 100%;
	align-items: stretch;
}

.bookmark-title {
	font-size: 0.85em;
	overflow: hidden;
	text-overflow: ellipsis;
	height: 1.75em;
	white-space: nowrap;
}

.bookmark-text {
	display: flex;
	flex-direction: column;
}

.bookmark-info {
	flex: 4 1 180px;
	padding: 12px 14px 14px;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
}

.bookmark-image {
	width: 33%;
	flex: 1 1 180px;
	display: block;
	position: relative;
	object-fit: cover;
	border-radius: 1px;
}

.bookmark-description {
	color: rgba(55, 53, 47, 0.6);
	font-size: 0.75em;
	overflow: hidden;
	max-height: 4.5em;
	word-break: break-word;
}

.bookmark-href {
	font-size: 0.75em;
	margin-top: 0.25em;
}

.sans { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol"; }
.code { font-family: "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace; }
.serif { font-family: Lyon-Text, Georgia, ui-serif, serif; }
.mono { font-family: iawriter-mono, Nitti, Menlo, Courier, monospace; }
.pdf .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK JP'; }
.pdf:lang(zh-CN) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK SC'; }
.pdf:lang(zh-TW) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK TC'; }
.pdf:lang(ko-KR) .sans { font-family: Inter, ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, "Apple Color Emoji", Arial, sans-serif, "Segoe UI Emoji", "Segoe UI Symbol", 'Twemoji', 'Noto Color Emoji', 'Noto Sans CJK KR'; }
.pdf .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK JP'; }
.pdf:lang(zh-CN) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK SC'; }
.pdf:lang(zh-TW) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK TC'; }
.pdf:lang(ko-KR) .code { font-family: Source Code Pro, "SFMono-Regular", Menlo, Consolas, "PT Mono", "Liberation Mono", Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK KR'; }
.pdf .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK JP'; }
.pdf:lang(zh-CN) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK SC'; }
.pdf:lang(zh-TW) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK TC'; }
.pdf:lang(ko-KR) .serif { font-family: PT Serif, Lyon-Text, Georgia, ui-serif, serif, 'Twemoji', 'Noto Color Emoji', 'Noto Serif CJK KR'; }
.pdf .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK JP'; }
.pdf:lang(zh-CN) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK SC'; }
.pdf:lang(zh-TW) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK TC'; }
.pdf:lang(ko-KR) .mono { font-family: PT Mono, iawriter-mono, Nitti, Menlo, Courier, monospace, 'Twemoji', 'Noto Color Emoji', 'Noto Sans Mono CJK KR'; }
.highlight-default {
	color: rgba(55, 53, 47, 1);
}
.highlight-gray {
	color: rgba(120, 119, 116, 1);
	fill: rgba(145, 145, 142, 1);
}
.highlight-brown {
	color: rgba(159, 107, 83, 1);
	fill: rgba(187, 132, 108, 1);
}
.highlight-orange {
	color: rgba(217, 115, 13, 1);
	fill: rgba(215, 129, 58, 1);
}
.highlight-yellow {
	color: rgba(203, 145, 47, 1);
	fill: rgba(203, 148, 51, 1);
}
.highlight-teal {
	color: rgba(68, 131, 97, 1);
	fill: rgba(108, 155, 125, 1);
}
.highlight-blue {
	color: rgba(51, 126, 169, 1);
	fill: rgba(91, 151, 189, 1);
}
.highlight-purple {
	color: rgba(144, 101, 176, 1);
	fill: rgba(167, 130, 195, 1);
}
.highlight-pink {
	color: rgba(193, 76, 138, 1);
	fill: rgba(205, 116, 159, 1);
}
.highlight-red {
	color: rgba(212, 76, 71, 1);
	fill: rgba(225, 111, 100, 1);
}
.highlight-gray_background {
	background: rgba(241, 241, 239, 1);
}
.highlight-brown_background {
	background: rgba(244, 238, 238, 1);
}
.highlight-orange_background {
	background: rgba(251, 236, 221, 1);
}
.highlight-yellow_background {
	background: rgba(251, 243, 219, 1);
}
.highlight-teal_background {
	background: rgba(237, 243, 236, 1);
}
.highlight-blue_background {
	background: rgba(231, 243, 248, 1);
}
.highlight-purple_background {
	background: rgba(244, 240, 247, 0.8);
}
.highlight-pink_background {
	background: rgba(249, 238, 243, 0.8);
}
.highlight-red_background {
	background: rgba(253, 235, 236, 1);
}
.block-color-default {
	color: inherit;
	fill: inherit;
}
.block-color-gray {
	color: rgba(120, 119, 116, 1);
	fill: rgba(145, 145, 142, 1);
}
.block-color-brown {
	color: rgba(159, 107, 83, 1);
	fill: rgba(187, 132, 108, 1);
}
.block-color-orange {
	color: rgba(217, 115, 13, 1);
	fill: rgba(215, 129, 58, 1);
}
.block-color-yellow {
	color: rgba(203, 145, 47, 1);
	fill: rgba(203, 148, 51, 1);
}
.block-color-teal {
	color: rgba(68, 131, 97, 1);
	fill: rgba(108, 155, 125, 1);
}
.block-color-blue {
	color: rgba(51, 126, 169, 1);
	fill: rgba(91, 151, 189, 1);
}
.block-color-purple {
	color: rgba(144, 101, 176, 1);
	fill: rgba(167, 130, 195, 1);
}
.block-color-pink {
	color: rgba(193, 76, 138, 1);
	fill: rgba(205, 116, 159, 1);
}
.block-color-red {
	color: rgba(212, 76, 71, 1);
	fill: rgba(225, 111, 100, 1);
}
.block-color-gray_background {
	background: rgba(241, 241, 239, 1);
}
.block-color-brown_background {
	background: rgba(244, 238, 238, 1);
}
.block-color-orange_background {
	background: rgba(251, 236, 221, 1);
}
.block-color-yellow_background {
	background: rgba(251, 243, 219, 1);
}
.block-color-teal_background {
	background: rgba(237, 243, 236, 1);
}
.block-color-blue_background {
	background: rgba(231, 243, 248, 1);
}
.block-color-purple_background {
	background: rgba(244, 240, 247, 0.8);
}
.block-color-pink_background {
	background: rgba(249, 238, 243, 0.8);
}
.block-color-red_background {
	background: rgba(253, 235, 236, 1);
}
.select-value-color-pink { background-color: rgba(245, 224, 233, 1); }
.select-value-color-purple { background-color: rgba(232, 222, 238, 1); }
.select-value-color-green { background-color: rgba(219, 237, 219, 1); }
.select-value-color-gray { background-color: rgba(227, 226, 224, 1); }
.select-value-color-orange { background-color: rgba(250, 222, 201, 1); }
.select-value-color-brown { background-color: rgba(238, 224, 218, 1); }
.select-value-color-red { background-color: rgba(255, 226, 221, 1); }
.select-value-color-yellow { background-color: rgba(253, 236, 200, 1); }
.select-value-color-blue { background-color: rgba(211, 229, 239, 1); }

.checkbox {
	display: inline-flex;
	vertical-align: text-bottom;
	width: 16;
	height: 16;
	background-size: 16px;
	margin-left: 2px;
	margin-right: 5px;
}

.checkbox-on {
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Crect%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%2358A9D7%22%2F%3E%0A%3Cpath%20d%3D%22M6.71429%2012.2852L14%204.9995L12.7143%203.71436L6.71429%209.71378L3.28571%206.2831L2%207.57092L6.71429%2012.2852Z%22%20fill%3D%22white%22%2F%3E%0A%3C%2Fsvg%3E");
}

.checkbox-off {
	background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%3Crect%20x%3D%220.75%22%20y%3D%220.75%22%20width%3D%2214.5%22%20height%3D%2214.5%22%20fill%3D%22white%22%20stroke%3D%22%2336352F%22%20stroke-width%3D%221.5%22%2F%3E%0A%3C%2Fsvg%3E");
}
	
&lt;/style>&lt;/head>&lt;body>&lt;article id="14207b20-d526-45e2-ac0b-55378dab72fa" class="page sans">&lt;header>&lt;div class="page-header-icon undefined">&lt;span class="icon">💡&lt;/span>&lt;/div>&lt;h1 class="page-title">Zak Knill&lt;/h1>&lt;/header>&lt;div class="page-body">&lt;h1 id="aa041012-5ed4-4a72-9299-95d151867733" class="">&lt;mark class="highlight-orange">Staff Engineer &lt;/mark>//&lt;mark class="highlight-orange"> &lt;/mark>Technical Leader&lt;/h1>&lt;p id="72fc1075-a7be-42ef-955b-a11cf0eaecae" class="">📡 personal site: &lt;a href="https://zknill.io">https://zknill.io&lt;/a>&lt;/p></description></item><item><title/><link>https://zknill.io/principles/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/principles/</guid><description>&lt;head>
&lt;meta http-equiv="refresh" content="0; url=https://principles.zknill.io">
&lt;/head></description></item><item><title/><link>https://zknill.io/readme/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/readme/</guid><description>&lt;head>
&lt;meta http-equiv="refresh" content="0; url=https://zknill.notion.site/README-f1bd580818ee4582a1d51ef5c421dd76">
&lt;/head></description></item><item><title/><link>https://zknill.io/til/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/til/</guid><description>&lt;head>
&lt;meta http-equiv="refresh" content="0; url=https://www.notion.so/zknill/Learning-in-public-a4051cef4f85455abf569280240ddd4c">
&lt;/head></description></item><item><title>Home</title><link>https://zknill.io/home/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/home/</guid><description>&lt;p>&lt;em>developer, blog&lt;/em>&lt;/p>
&lt;p>Hey I am Zak Knill, a core engineer; these are some of my thoughts and bit &lt;a href="https://zknill.io/about/">about&lt;/a> me.&lt;/p></description></item><item><title>License</title><link>https://zknill.io/license/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://zknill.io/license/</guid><description>&lt;blockquote>
&lt;p>The MIT License (MIT)&lt;/p>
&lt;p>Copyright (c) 2017 Zak Knill&lt;/p>
&lt;p>Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the &amp;ldquo;Software&amp;rdquo;), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:&lt;/p></description></item></channel></rss>