<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>LeoSimons.com</title>
	<atom:link href="https://leosimons.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://leosimons.com</link>
	<description>Leo Simons&#039; homepage</description>
	<lastBuildDate>Thu, 20 Aug 2026 06:49:54 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>

<image>
	<url>https://leosimons.com/wp-content/uploads/2026/02/20251214-leo-portrait-cartoon-small.png?w=32</url>
	<title>LeoSimons.com</title>
	<link>https://leosimons.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">2100343</site><cloud domain='leosimons.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<source:cloud xmlns:source='http://source.scripting.com/'>https://leosimons.com/?rsscloud=notify</source:cloud>
<atom:link rel="search" type="application/opensearchdescription+xml" href="https://leosimons.com/osd.xml" title="LeoSimons.com" />
	<atom:link rel='hub' href='https://leosimons.com/?pushpress=hub'/>
	<item>
		<title>Limiting the number of turns in agent loops through manual caching</title>
		<link>https://leosimons.com/2026/08/20/limiting-the-number-of-turns-in-agent-loops-through-manual-caching/</link>
					<comments>https://leosimons.com/2026/08/20/limiting-the-number-of-turns-in-agent-loops-through-manual-caching/#respond</comments>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Thu, 20 Aug 2026 06:49:54 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=991</guid>

					<description><![CDATA[I&#8217;m creating my own reusable skills. This week I learned something more about the ergonomics: it can be worth it to design what is loaded into context not just around limiting the size, but also around how many turns and tool calls are needed, reducing latency while using the skill. For example, in the verification &#8230; <a href="https://leosimons.com/2026/08/20/limiting-the-number-of-turns-in-agent-loops-through-manual-caching/" class="more-link">Continue reading<span class="screen-reader-text"> "Limiting the number of turns in agent loops through manual&#160;caching"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I&#8217;m creating <a href="https://github.com/lsimons/lsimons-skills">my own reusable skills</a>. This week I learned something more about the ergonomics: it can be worth it to design what is loaded into context not just around limiting the size, but also around how many turns and tool calls are needed, reducing latency while using the skill.</p>



<p class="wp-block-paragraph">For example, in the <a href="https://github.com/lsimons/lsimons-skills/blob/main/skills/setup/references/verification.md">verification step</a> of my <a href="https://leosimons.com/2026/08/11/my-new-agent-skill-setup/">/setup skill</a>, I wanted the AI to double-check it had properly filled in all <code>&lt;placeholders&gt;</code> when processing a template. Initially I had this:</p>



<div class="wp-block-jetpack-markdown"><p><em><strong>Syntactic: did I write valid files?</strong> The characteristic failure is an unfilled placeholder. Check it precisely: extract the placeholder tokens the template itself declares, then assert none of them survive in the file that was written.</em></p>
<p><em>For example, for the README, look for placeholder tokens the template declares:</em></p>
<pre><code class="language-bash">SKILL_DIR=[skill base directory]
grep -ohE '&lt;[^&gt;]{1,80}&gt;' ${SKILL_DIR}/assets/README-template.md | sort -u
# -&gt; &lt;how to use the project&gt; &lt;medium description&gt; &lt;Name&gt; &lt;other sections&gt;
#    &lt;pointers at docs/&gt; &lt;project&gt; &lt;short description&gt;
And then assert each of those is absent from the actual README.md.
</code></pre>
<p><em>A bare grep ‘&lt;’ is the wrong check — it false-positives on ordinary prose and on the comments inside the mise templates.</em></p>
</div>



<p class="wp-block-paragraph">These instructions started out as codifying how the AI had been instructing itself to do the verification, taking into account that the template can change and that there&#8217;s multiple templates. But having the AI invoke bash to run grep to then process a list of placeholder tokens further is slowing things down for no good reason.</p>



<p class="wp-block-paragraph">While the skill its templates do change, they do not change often, and when they do, they go into GitHub and through CI/CD tooling. So the <code>grep</code> commands can be run once, without AI involved, and the results can be inlined into the skill file. The result becomes:</p>



<div class="wp-block-jetpack-markdown"><p><em><strong>Syntactic: did I write valid files?</strong> The characteristic failure is an unfilled placeholder. Check it precisely: for every file written from a template, assert that none of the tokens that template declares survive in it.</em></p>
<p><em>The tokens, by template:</em></p>
<p><em>&lt;!&#8211; placeholders: assets/ &#8211;&gt;</em></p>
<pre><code>assets/CODE_OF_CONDUCT-template.md
  ...
assets/README-template.md
  &lt;how to use the project&gt;
  &lt;medium description&gt;
  &lt;Name&gt;
  &lt;other sections&gt;
...
</code></pre>
<p><em>&lt;!&#8211; /placeholders &#8211;&gt;</em></p>
<p><em>That list is generated from the templates; a template not listed declares no placeholders. If the list is empty, this skill is broken: stop and report it rather than reading no tokens as no placeholders.</em></p>
<p><em>A bare <code>grep '&lt;'</code> is the wrong check — it false-positives on ordinary prose and on the comments inside the mise templates.</em></p>
</div>



<p class="wp-block-paragraph">A lot of the words for the initial skill and also for this update rewrite was done by AI. It also wrote the <a href="https://github.com/lsimons/lsimons-skills/blob/main/scripts/placeholders.py">script</a> that keeps the list updated, and suggested this specific improvement in the first place.</p>



<p class="wp-block-paragraph">You can tell this is AI-written, I think. Besides the em-dash, another clear tell is the sentence <em>&#8220;if the list is empty, this skill is broken: stop and report it rather than reading no tokens as no placeholders&#8221;</em>. Opus 5 is often too careful when it is trying to instruct other AI; I think the CI setup is good and so I don&#8217;t think it is likely I will mess this up.</p>



<p class="wp-block-paragraph">In software development we learned &#8220;<a href="https://wiki.c2.com/?PrematureOptimization">premature optimization is the root of all evil</a>&#8221; and then &#8220;<a href="https://martinfowler.com/bliki/TwoHardThings.html">There are only two hard things in Computer Science: cache invalidation and naming things</a>&#8220;, with caching being one kind of optimization. So we developed a sense of taste to avoid caching until really needed.</p>



<p class="wp-block-paragraph">But now with agentic coding, I find myself reaching for such caches and other optimizations sooner, to limit AI cost but especially to limit AI latency. I feel that, when I keep my agent skills fast, I reach for them more, my AI stays on the rails better, I experience less in-session frustration, and then I can spend my cognitive energy on judging the things that matter, like whether it is properly accomplishing the thing I want it to do.</p>



<p class="wp-block-paragraph">(You can probably also tell that like this post, last sentence is <em>not</em> written by AI. Too long, too many commas, ambiguous grammar. I&#8217;m leaving it in.)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://leosimons.com/2026/08/20/limiting-the-number-of-turns-in-agent-loops-through-manual-caching/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">991</post-id>
		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>My new agent skill: /setup</title>
		<link>https://leosimons.com/2026/08/11/my-new-agent-skill-setup/</link>
					<comments>https://leosimons.com/2026/08/11/my-new-agent-skill-setup/#respond</comments>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Tue, 11 Aug 2026 17:46:23 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=984</guid>

					<description><![CDATA[I&#8217;ve made a /setup skill, the first agent skill I&#8217;ve built that&#8217;s intended for reuse. It brings the scaffolding and boilerplate for any project up to a high standard, adding things such as mise task management, dependency pinning, issue tracking standards, test coverage, and continuous integration. Example improvements made with /setup Here are some pull &#8230; <a href="https://leosimons.com/2026/08/11/my-new-agent-skill-setup/" class="more-link">Continue reading<span class="screen-reader-text"> "My new agent skill:&#160;/setup"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I&#8217;ve made a <a href="https://github.com/lsimons/lsimons-skills/blob/main/skills/setup/SKILL.md">/setup</a> skill, the first <a href="https://agentskills.io/">agent skill</a> I&#8217;ve built that&#8217;s intended for reuse. It brings the scaffolding and boilerplate for any project up to a high standard, adding things such as <code>mise</code> task management, dependency pinning, issue tracking standards, test coverage, and continuous integration.</p>



<h2 class="wp-block-heading">Example improvements made with <code>/setup</code></h2>



<p class="wp-block-paragraph">Here are some pull requests from running <code>/setup</code> along with some of the improvements the agent decided to make:</p>



<ul class="wp-block-list">
<li><a href="https://github.com/lsimons/lsimons-template-py/pull/10">Python project</a>: pinning uv dependencies, adding <a href="https://github.com/rhysd/actionlint">actionlint</a>;</li>



<li><a href="https://github.com/lsimons/lsimons-template-rs/pull/15">Rust project</a>: pinning cargo dependencies, adding <a href="https://github.com/embarkstudios/cargo-deny">cargo-deny</a>;</li>



<li><a href="https://github.com/lsimons/lsimons-template-doc/pull/2">Docs project</a>: running linting in CI, adding <a href="https://github.com/HiDeoo/starlight-links-validator">starlight-links-validator</a>.</li>
</ul>



<h2 class="wp-block-heading">How it works</h2>



<p class="wp-block-paragraph">The skill is written for opus-class agents, giving high-level instructions that exercise the model&#8217;s ability to think and reason. Specific templates ground the model so it knows what good looks like, and it verifies its own work in a fresh context. A sonnet-class model can use the skill but in testing made less comprehensive improvements.</p>



<p class="wp-block-paragraph">The skill works for greenfield and for brownfield repos. It will initialize new projects based on template projects, while for existing projects it will add what&#8217;s missing. The skill is not fully autonomous. It explores first and then asks the user questions in a short batch, but it will ask for more user input and decisions if needed.</p>



<p class="wp-block-paragraph">This skill works on a large variety of projects. I&#8217;ve tested Python / TypeScript / Go / Rust / Monorepo / Markdown and have templates for each of those. It works well for small hobby projects and scales to at least a medium size (~250k LoC) repo.</p>



<h2 class="wp-block-heading">YMMV</h2>



<p class="wp-block-paragraph">This is an opinionated skill, based on 25 years of setting up software projects and doing large-scale platform engineering to set up many parallel projects. While flexible and context-aware, my preferences and my judgment are encoded in it, so if you disagree with me about what a good project setup really is, you need to swap out the templates.</p>



<h2 class="wp-block-heading">TIL: phases</h2>



<p class="wp-block-paragraph">It&#8217;s good to divide agent skill documents into phases for the agent:</p>



<ul class="wp-block-list">
<li>exploration and fact-finding</li>



<li>plan to do the work</li>



<li>ask the user to fill in gaps, assumptions, and make decisions</li>



<li>complete the plan based on those answers</li>



<li>execute the plan to do the actual work</li>



<li>verify the work done achieved the goal (with fresh context)</li>



<li>fix the work that failed verification</li>



<li>re-verify the fixes</li>
</ul>



<p class="wp-block-paragraph">This plan/do/check/act pattern is very similar to an AI-assisted spec-driven TDD loop (spec → tests → code → refactor), with some additional ergonomics to make working with the skill easier for humans.</p>



<p class="wp-block-paragraph">Rewriting the skill to this structure made the changes it proposed more cohesive and more comprehensive.</p>



<h2 class="wp-block-heading">Installing</h2>



<p class="wp-block-paragraph">Using <a href="https://www.skills.sh">skills.sh</a>:</p>


<div class="wp-block-code">
	<div class="cm-editor">
		<div class="cm-scroller">
			
<pre>
<code><div class="cm-line">npx skills add lsimons/lsimons-skills</div></code></pre>
		</div>
	</div>
</div>


<p class="wp-block-paragraph"><code>/setup</code> tries to use <code>/complete</code>, so also install that.</p>



<p class="wp-block-paragraph">Or you can manually copy the <a href="https://github.com/lsimons/lsimons-skills/tree/main/skills/setup">setup directory</a> and the <a href="https://github.com/lsimons/lsimons-skills/tree/main/skills/complete">complete directory</a> into the <code>skills</code> folder of your agent.</p>



<p class="wp-block-paragraph"><em>All words in this post were written by me, not by AI, but AI did help review early post drafts and it helped write the skill (Anthropic Claude Opus).</em></p>
]]></content:encoded>
					
					<wfw:commentRss>https://leosimons.com/2026/08/11/my-new-agent-skill-setup/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">984</post-id>
		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>Fact-based modelling and LinkML</title>
		<link>https://leosimons.com/2026/07/26/fact-based-modelling-and-linkml/</link>
					<comments>https://leosimons.com/2026/07/26/fact-based-modelling-and-linkml/#respond</comments>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Sun, 26 Jul 2026 11:49:38 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=950</guid>

					<description><![CDATA[This blog post was co-authored with AI (Anthropic Claude Fable). Domain-driven design works best when the people who know the domain can check the model. Most modelling notations get in the way of that. Fact-based modelling helps. Instead of starting from tables or classes, you describe the domain as short sentences that a domain expert &#8230; <a href="https://leosimons.com/2026/07/26/fact-based-modelling-and-linkml/" class="more-link">Continue reading<span class="screen-reader-text"> "Fact-based modelling and&#160;LinkML"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>This blog post was co-authored with AI (Anthropic Claude Fable).</em></p>



<p class="wp-block-paragraph"><a href="https://en.wikipedia.org/wiki/Domain-driven_design">Domain-driven design</a> works best when the people who know the domain can check the model. Most modelling notations get in the way of that.</p>



<p class="wp-block-paragraph"><a href="https://medium.com/data-science/what-is-fact-based-modelling-106ba15649fa">Fact-based modelling</a> helps. Instead of starting from tables or classes, you describe the domain as short sentences that a domain expert can confirm or reject. This post walks through that way of working, then translates the result into <a href="https://linkml.io/">LinkML</a>, a schema language with good code generation.</p>



<h2 class="wp-block-heading">Think in facts</h2>



<p class="wp-block-paragraph">The best-known fact-based method is <a href="https://en.wikipedia.org/wiki/Object-role_modeling">Object-Role Modeling</a> (ORM). Its core idea: a domain is a set of facts, and every fact can be read out as a sentence.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Each Team has at least one lead, who is a Person.</p>
</blockquote>



<p class="wp-block-paragraph">A product manager can read that sentence. A DBA can read it. The person who runs the team can read it and tell you it&#8217;s wrong. &#8220;Teams go without a lead for months during reorgs&#8221; is cheap feedback at a whiteboard and expensive feedback after you ship a <code>NOT NULL</code> constraint.</p>



<p class="wp-block-paragraph">You find these facts by talking with domain experts, using concrete examples (&#8220;take the platform team. Who&#8217;s the lead there?&#8221;), plain language, and simple drawings. Examples do double duty: they help you discover a fact, and they test it. If the expert can&#8217;t fill in your example table, the fact is wrong.</p>



<p class="wp-block-paragraph">Read Halpin &amp; Morgan <a href="https://www.sciencedirect.com/book/9780443237904/information-modeling-and-relational-databases"><em>Information Modeling and Relational Databases</em></a> for details.</p>



<h2 class="wp-block-heading">Sketch first, formalize later</h2>



<p class="wp-block-paragraph">Keep formal notation off the whiteboard. Circles and lines and sticky notes are enough: a circle per kind of thing, a line per fact, the sentence written along the line, examples on stickies underneath. ORM has a precise diagram notation, and in a workshop that precision turns contributors into spectators: the expert who happily corrects a sentence will hesitate to touch a diagram symbol.</p>



<p class="wp-block-paragraph">For many domains, a whiteboard photo plus the agreed sentences is the whole model, and stopping there is fine. See <a href="https://leosimons.com/2023/11/02/caseum-my-lightweight-approach-to-software-architecture/">Caseum</a> for more about such a lightweight approach.</p>



<h2 class="wp-block-heading">From facts to schema</h2>



<p class="wp-block-paragraph">Sometimes you can&#8217;t stop there. You need a JSON Schema for an API, a database schema, docs that stay current. Maintaining those by hand, in parallel, is how the drift starts. Better to keep one source file and generate the rest.</p>



<p class="wp-block-paragraph">ORM tooling is not in the best state. <a href="https://linkml.io/">LinkML</a> is a great alternative. It is a YAML-based schema language with <a href="https://linkml.io/linkml/generators/">generators</a> for JSON Schema, SQL, ER diagrams, documentation sites, and typed Pydantic and TypeScript classes.</p>



<p class="wp-block-paragraph">A small HR domain as fact sentences:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Each Person is identified by an id, which is a URI.
Each Person has exactly one name.
Each Person (the coachee) may have one or more coaches, each of whom is a Person.
Each Person has at most one Department.
Each Team has at least one lead, who is a Person.
</pre></div>


<p class="wp-block-paragraph">The same model as LinkML (<code>hr.yaml</code>):</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
id: https://example.org/hr
name: hr
prefixes:
  linkml: https://w3id.org/linkml/
  hr: https://example.org/hr/
default_prefix: hr
default_range: string
imports:
  - linkml:types

classes:
  Person:
    attributes:
      id:
        identifier: true
        range: uri
      name:
        required: true
      coach:
        range: Person
        multivalued: true
      department:
        range: Department
  Department:
    attributes:
      id:
        identifier: true
        range: uri
  Team:
    attributes:
      lead:
        required: true
        multivalued: true
        range: Person
</pre></div>


<p class="wp-block-paragraph">Each fact becomes a slot (LinkML&#8217;s word for an attribute), and the how-many wording maps directly:</p>



<figure class="wp-block-table"><table><thead><tr><th>Fact sentence form</th><th>LinkML slot</th></tr></thead><tbody><tr><td><code>is identified by</code></td><td><code>identifier: true</code></td></tr><tr><td><code>has exactly one</code></td><td><code>required: true</code></td></tr><tr><td><code>has at most one</code></td><td>(the default: optional, single-valued)</td></tr><tr><td><code>has at least one</code></td><td><code>required: true</code> + <code>multivalued: true</code></td></tr><tr><td><code>may have one or more</code></td><td><code>multivalued: true</code></td></tr><tr><td><code>who/which is a X</code></td><td><code>range: X</code></td></tr></tbody></table></figure>



<p class="wp-block-paragraph">Settle on a fixed set of phrases like this and turning the whiteboard into a schema stops being design work and becomes transcription, which AI can do for you.</p>



<h2 class="wp-block-heading">From schema to software</h2>



<p class="wp-block-paragraph">Use the LinkML tools to create other files from the model. For example:</p>



<h3 class="wp-block-heading">JSON Schema</h3>



<p class="wp-block-paragraph">Run <code>gen-json-schema hr.yaml</code>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: jscript; title: ; notranslate">
"Person": {
    "additionalProperties": false,
    "properties": {
        "coach": {
            "items": { "format": "uri", "type": "string" },
            "type": &#91; "array", "null" ]
        },
        "department": { "format": "uri", "type": &#91; "string", "null" ] },
        "id": { "format": "uri", "type": "string" },
        "name": { "type": "string" }
    },
    "required": &#91; "id", "name" ]
}
</pre></div>


<h3 class="wp-block-heading">Mermaid ER diagram</h3>



<p class="wp-block-paragraph">Run <code>gen-erdiagram hr.yaml</code>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
erDiagram
Department {
    uri id
}
Person {
    uri id
    string name
}
Team {
}

Person ||--|o Department : "department"
Person ||--}o Person : "coach"
Team ||--}| Person : "lead"
</pre></div>


<p class="wp-block-paragraph">which <a href="https://mermaid.js.org/#/">Mermaid</a> renders as:</p>


<div class="wp-block-image">
<figure data-wp-context="{&quot;imageId&quot;:&quot;6a86c3f04affa&quot;}" data-wp-interactive="core/image" data-wp-key="6a86c3f04affa" class="aligncenter size-large is-resized wp-lightbox-container"><img width="617" height="1023" data-attachment-id="954" data-permalink="https://leosimons.com/2026/07/26/fact-based-modelling-and-linkml/2026-07-28-linkml-article-er/" data-orig-file="https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png" data-orig-size="744,1234" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;,&quot;alt&quot;:&quot;&quot;}" data-image-title="2026-07-28-linkml-article-er" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=617" data-wp-class--hide="state.isContentHidden" data-wp-class--show="state.isContentVisible" data-wp-init="callbacks.setButtonStyles" data-wp-on--click="actions.showLightbox" data-wp-on--load="callbacks.setButtonStyles" data-wp-on--pointerdown="actions.preloadImage" data-wp-on--pointerenter="actions.preloadImageWithDelay" data-wp-on--pointerleave="actions.cancelPreload" data-wp-on-window--resize="callbacks.setButtonStyles" src="https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=617" alt="" class="wp-image-954" style="width:235px;height:auto" srcset="https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=617 617w, https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=90 90w, https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=181 181w, https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png 744w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 984px) 61vw, (max-width: 1362px) 45vw, 600px" /><button
			class="lightbox-trigger"
			type="button"
			aria-haspopup="dialog"
			data-wp-bind--aria-label="state.thisImage.triggerButtonAriaLabel"
			data-wp-init="callbacks.initTriggerButton"
			data-wp-on--click="actions.showLightbox"
			data-wp-style--right="state.thisImage.buttonRight"
			data-wp-style--top="state.thisImage.buttonTop"
		>
			<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" fill="none" viewBox="0 0 12 12">
				<path fill="#fff" d="M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z" />
			</svg>
		</button></figure>
</div>


<p class="wp-block-paragraph">(Mermaid is supported by many Markdown tools, including on <a href="https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams">GitHub</a>)</p>



<h3 class="wp-block-heading">SQL</h3>



<p class="wp-block-paragraph">Run <code>gen-sqlddl hr.yaml</code>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: sql; title: ; notranslate">
CREATE TABLE "Person" (
    id TEXT NOT NULL,
    name TEXT NOT NULL,
    department TEXT,
    PRIMARY KEY (id),
    FOREIGN KEY(department) REFERENCES "Department" (id)
);

CREATE TABLE "Person_coach" (
    "Person_id" TEXT,
    coach_id TEXT,
    PRIMARY KEY ("Person_id", coach_id),
    FOREIGN KEY("Person_id") REFERENCES "Person" (id),
    FOREIGN KEY(coach_id) REFERENCES "Person" (id)
);
</pre></div>


<h2 class="wp-block-heading">Testing the schema with examples</h2>



<p class="wp-block-paragraph">Given <code>ada.yaml</code>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
id: https://example.org/people/ada
name: Ada
coach:
  - https://example.org/people/grace
</pre></div>


<p class="wp-block-paragraph">and <code>bob.yaml</code>:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
id: https://example.org/people/bob
department: engineering
</pre></div>


<p class="wp-block-paragraph">Here&#8217;s what testing looks like:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: bash; title: ; notranslate">
$ linkml validate -s hr.yaml -C Person ada.yaml
No issues found
$ linkml validate -s hr.yaml -C Person bob.yaml
&#91;ERROR] &#91;bob.yaml/0] 'engineering' is not a 'uri' in /department
&#91;ERROR] &#91;bob.yaml/0] 'name' is a required property in /
</pre></div>


<p class="wp-block-paragraph">There is more to LinkML, but the shape is clear: one schema file, kept under code review, with tool automation so that artifacts do not drift from each other.</p>



<h2 class="wp-block-heading">Remember the facts</h2>



<p class="wp-block-paragraph">Compare the written fact</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph">Each Person (the coachee) may have one or more coaches, each of whom is a Person.</p>
</blockquote>



<p class="wp-block-paragraph">with the LinkML model slot:</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
coach:
  range: Person
  multivalued: true
</pre></div>


<p class="wp-block-paragraph">The sentence names both roles, coach and coachee, and reads aloud in either direction. The slot names one direction and buries the rest in structure. A domain expert can verify the sentence but not the YAML.</p>



<p class="wp-block-paragraph">Keep the written facts and their examples around, right next to the schema. Your AI is excellent at English and so can help keep facts and LinkML in sync.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://leosimons.com/2026/07/26/fact-based-modelling-and-linkml/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">950</post-id>
		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>

		<media:content url="https://leosimons.com/wp-content/uploads/2026/07/2026-07-28-linkml-article-er.png?w=617" medium="image" />
	</item>
		<item>
		<title>Anthropomorphizing my agent</title>
		<link>https://leosimons.com/2026/02/01/anthropomorphizing-my-agent/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Sun, 01 Feb 2026 21:19:30 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=919</guid>

					<description><![CDATA[Giving my coding agent some personality.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I think it&#8217;s useful to distinguish which git commits are (partially) AI-authored. I kind-of like how Claude Code sets Claude as co-author of git commits.</p>



<p class="wp-block-paragraph">But I think my coding agent, with all its own settings and quirks and approach, should be distinguished from everyone else&#8217;s. I use more than just Claude Code (I also use GitHub Copilot, Gemini CLI, <a href="https://shittycodingagent.ai/">pi</a>, &#8230;). I thought my coding agent better have some <em>personality</em>.</p>



<p class="wp-block-paragraph">So I&#8217;ve set up a dedicated <a href="https://github.com/lsimons-bot">@lsimons-bot</a>, <a href="https://github.com/lsimons/lsimons-dotfiles/blob/main/docs/AGENT_SETUP.md">put it in a VM sandbox</a>, and <a href="https://github.com/lsimons/lsimons-dotfiles/blob/8335ff02c7053350c9872ca28e6309b736e31868/claude/CLAUDE.md.symlink#L24">instructed it to identify me as its co-author</a>. <s>I&#8217;ve even <a href="https://lsimonsbot.wordpress.com/">given the bot its own blog</a></s>. Now I get pretty commits with two authors which are me and robot-me:</p>



<figure class="wp-block-image size-large"><a href="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png"><img width="1024" height="372" data-attachment-id="927" data-permalink="https://leosimons.com/2026/02/01/anthropomorphizing-my-agent/lsimons-bot-co-author-2/" data-orig-file="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png" data-orig-size="2346,854" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="lsimons-bot-co-author" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=840" src="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=1024" alt="" class="wp-image-927" srcset="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=1024 1024w, https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=2048 2048w, https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=150 150w, https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=300 300w, https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=768 768w, https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=1440 1440w" sizes="(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px" /></a></figure>



<p class="wp-block-paragraph">I&#8217;ve tuned the settings so that when I am doing tightly controlled AI-assisted development, the git commits have me as main author and <a href='https://lsimons.wordpress.com/mentions/lsimons/' class='__p2-hovercard mention' data-type='fragment-mention' data-username='lsimons'><span class='mentions-prefix'>@</span>lsimons</a>-bot as coauthor. When I&#8217;m doing vibe coding in the VM, it&#8217;s reversed. And it&#8217;s also nice to see the bot make pull requests which I then review and merge.</p>



<p class="wp-block-paragraph">In Claude Code it is possible to configure its <a href="https://code.claude.com/docs/en/settings#attribution-settings">attribution settings</a>, but not all the coding agents I use can do this just yet, so as of right now I&#8217;m instead wasting tokens on it.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">919</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-github.png" />
		<media:content url="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-github.png" medium="image">
			<media:title type="html">lsimons-bot-github</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>

		<media:content url="https://leosimons.com/wp-content/uploads/2026/02/lsimons-bot-co-author-1.png?w=1024" medium="image" />
	</item>
		<item>
		<title>Understanding Yegge&#8217;s Gas Town</title>
		<link>https://leosimons.com/2026/01/02/understanding-yegges-gas-town/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Fri, 02 Jan 2026 14:29:07 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=912</guid>

					<description><![CDATA[What does factory farming for source code look like?]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><em>This blog post was co-authored with AI (Google Gemini).</em></p>



<p class="wp-block-paragraph">Steve Yegge is back with &#8220;<a href="https://steve-yegge.medium.com/welcome-to-gas-town-4f25ee16dd04" target="_blank" rel="noreferrer noopener">Welcome to Gas Town</a>,&#8221; a novella-sized blog post about his new multi-agent coding systems, full of <em>Mad Max</em> references. It’s an entertaining but puzzling read. He describes a chaotic world of &#8220;polecats&#8221;, &#8220;GUPP&#8221;, and &#8220;Convoys&#8221;, covering the architecture with thick narrative lore.</p>



<p class="wp-block-paragraph">To decipher what he is <em>actually</em> building, I fed the post and his <a href="https://github.com/steveyegge/gastown" target="_blank" rel="noreferrer noopener">GitHub repository</a> into Gemini. It helped me understand Steve&#8217;s vision of a Kubernetes-like control plane for AI code agents and then helped write this blog post.</p>



<h3 class="wp-block-heading">Decoding the Metaphors</h3>



<p class="wp-block-paragraph">Yegge’s system manages the chaos of running 20–30 concurrent AI agents in parallel in YOLO mode. Key parts:</p>



<ul class="wp-block-list">
<li><strong>The Controller (&#8220;The Town&#8221;):</strong> A central Go binary that orchestrates agent lifecycles across projects.</li>



<li><strong>Git-Backed State (&#8220;<a href="https://github.com/steveyegge/beads">Beads</a>&#8220;):</strong> The atomic unit of work isn&#8217;t a database row; it&#8217;s a JSON file committed directly to the project&#8217;s Git history. This makes every task, status, and message persistent and version-controlled.</li>



<li><strong>The Interface:</strong> The system uses <code>tmux</code> as a dashboard (HUD) to manage the fleet of agents.</li>
</ul>



<h3 class="wp-block-heading">The Workforce</h3>



<p class="wp-block-paragraph">The &#8220;Gas Town&#8221; roles map to standard distributed system components:</p>



<ul class="wp-block-list">
<li><strong>&#8220;Mayor&#8221; (Interface):</strong> A persistent agent acting as a Chief of Staff for the human user.</li>



<li><strong>&#8220;Polecats&#8221; (Batch Workers):</strong> Ephemeral agents spun up to execute tasks and vanish.</li>



<li><strong>&#8220;Refinery&#8221; (Merge Queue):</strong> An engineer agent that resolves merge conflicts when multiple workers submit code.</li>



<li><strong>&#8220;Witness&#8221; (Watchdog):</strong> A monitor that detects and restarts stalled agents.</li>
</ul>



<h3 class="wp-block-heading">Tackling complexity</h3>



<p class="wp-block-paragraph">More than the specific solution it&#8217;s interesting to see the underlying problems being solved for. Claude Code is polite and it stops to ask for confirmation, so, control systems are built to make it continue. Claude Code context compaction can remember the wrong things, so structured external memory is provided in specific and evolving ways. Etc.</p>



<p class="wp-block-paragraph">The <a href="https://github.com/steveyegge/gastown">git repo</a> reveals more of such complexity not fully explained in the blog, with the <a href="https://github.com/steveyegge/beads/tree/main/docs">docs</a> containing some of the more interesting insights of what happens when you do vibe coding at scale. For example, <a href="https://github.com/steveyegge/gastown/blob/main/docs/identity.md">why identity of (sub)agents matters</a>.</p>



<p class="wp-block-paragraph">The repo&#8217;s <a href="https://github.com/steveyegge/gastown/blob/main/AGENTS.md">AGENTS.md</a> is interesting too. Agents are explicitly forbidden from asking permission to push; they must run tests, commit, push, and verify success before reporting back.</p>



<p class="wp-block-paragraph">Looks like quite a serious attempt to build an industrialized coding factory, turning the developer from a coder into a manager of a synthetic manager of synthetic workers.</p>



<p class="wp-block-paragraph">Looks like most of the work to make this happen is handling accidental complexity, not essential complexity. I wonder how much of that is due to building on top of Claude Code. Perhaps a future iteration integrates its own less-polite less-error-prone coding agent, something like <a href="https://mariozechner.at/posts/2025-11-30-pi-coding-agent/">pi</a>?</p>



<p class="wp-block-paragraph"><em>(Yegge being co-author of the book <a href="https://www.amazon.com/Vibe-Coding-Building-Production-Grade-Software/dp/1966280025">Vibe Coding</a>, who recently said in a talk &#8220;<a href="https://youtu.be/7Dtu2bilcFs?t=402">if you are using an IDE [in 2026], you are a bad engineer</a>&#8220;, because you should use a coding agent)</em></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">912</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2026/01/gas-town-simplified.jpg" />
		<media:content url="https://leosimons.com/wp-content/uploads/2026/01/gas-town-simplified.jpg" medium="image">
			<media:title type="html">Gas Town explanation, simplified</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>AI demands Literate Programming</title>
		<link>https://leosimons.com/2025/10/20/ai-demands-literate-programming/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Mon, 20 Oct 2025 07:26:25 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=895</guid>

					<description><![CDATA[Clarity of expression is demanded of AI-assisted developers.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">In the 1980s, Donald Knuth introduced <a href="https://en.wikipedia.org/wiki/Literate_programming" target="_blank" rel="noreferrer noopener">literate programming</a>, a paradigm where code and documentation are interwoven into a single narrative. The idea was simple but radical: write programs as if you&#8217;re explaining them to another human — not just to a compiler. His <a href="https://www-cs-faculty.stanford.edu/~knuth/lp.html" target="_blank" rel="noreferrer noopener">WEB system</a> and <a href="https://tug.org/texshowcase/">TeX showcase</a> were early demonstrations.</p>



<p class="wp-block-paragraph">Fast forward to today, and we’re seeing a return to that mindset based on the practical demands of AI-assisted development.</p>



<h3 class="wp-block-heading">Spec-Driven Development: A Modern Twist</h3>



<p class="wp-block-paragraph">To keep tools like <a href="https://github.com/features/copilot">GitHub Copilot</a>, <a href="https://chatgpt.com/features/codex">ChatGPT Codex</a>, and <a href="https://www.claude.com/product/claude-code">Claude Code</a> behaving, developers are increasingly writing <strong>natural language specifications</strong> that guide code generation.</p>



<p class="wp-block-paragraph">This trend, often called <a href="https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html">spec-driven development</a>, emphasises writing structured, human-readable descriptions of what software should do — before writing any code.</p>



<p class="wp-block-paragraph">These specs aren’t just throwaway prompts. They’re version-controlled, reviewed, and treated as first-class artifacts. They are what helps keep the AI on the rails.</p>



<h3 class="wp-block-heading">The Parallel with Literate Programming</h3>



<p class="wp-block-paragraph">What’s happening here mirrors Knuth’s vision. In both cases, the developer is encouraged to <strong>think clearly and write clearly</strong>. The difference is that now, the audience isn’t just other humans — it’s also an AI model that can turn prose into functioning code.</p>



<p class="wp-block-paragraph">Modern tools like <a href="https://jupyter.org/">Jupyter</a> already blurred the line between documentation and implementation. This hybrid approach is now becoming a valuable way to avoid AI slop.</p>



<h3 class="wp-block-heading">Why It Matters</h3>



<p class="wp-block-paragraph">This isn’t just a tooling shift — it’s a cultural one. We’re moving away from “vibe-based coding” and toward a more deliberate, explainable, and collaborative process. And in doing so, we’re rediscovering the value of code as a form of communication.</p>



<p class="has-text-align-left has-medium-gray-color has-white-background-color has-text-color has-background has-link-color has-small-font-size wp-elements-1 wp-block-paragraph"><em>Co-authored with AI (Anthropic Claude Sonnet 4.5).</em><br><em>Normally I don&#8217;t publish content that AI helped to write, but I wanted to see how it feels. </em></p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">895</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2025/10/ai-and-literate-programming-medium.png" />
		<media:content url="https://leosimons.com/wp-content/uploads/2025/10/ai-and-literate-programming-medium.png" medium="image">
			<media:title type="html">ai-and-literate-programming-medium</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>Caseum: my lightweight approach to software architecture</title>
		<link>https://leosimons.com/2023/11/02/caseum-my-lightweight-approach-to-software-architecture/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Thu, 02 Nov 2023 11:02:29 +0000</pubDate>
				<category><![CDATA[Architecture]]></category>
		<category><![CDATA[Open Source]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=771</guid>

					<description><![CDATA[Whiteboards. Multiple views. Decision records. Pragmatism. Empathy.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Described my software architecture approach at <a href="https://lsimons.github.io/caseum/">https://lsimons.github.io/caseum/</a>. Just common best practices and whiteboards. Hope a simple approach in simple English can be inspiring.</p>



<p class="wp-block-paragraph">Short video introduction:</p>



<figure class="wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
<iframe class="youtube-player" width="840" height="473" src="https://www.youtube.com/embed/0s1qPY-W1ew?version=3&#038;rel=1&#038;showsearch=0&#038;showinfo=1&#038;iv_load_policy=1&#038;fs=1&#038;hl=en&#038;autohide=2&#038;wmode=transparent" allowfullscreen="true" style="border:0;" sandbox="allow-scripts allow-same-origin allow-popups allow-presentation allow-popups-to-escape-sandbox"></iframe>
</div></figure>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">771</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2023/11/caseum-infographic-large.jpg" />
		<media:content url="https://leosimons.com/wp-content/uploads/2023/11/caseum-infographic-large.jpg" medium="image">
			<media:title type="html">caseum-infographic-large</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>Abstract Career Framework for Engineering</title>
		<link>https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Mon, 03 Apr 2023 17:02:03 +0000</pubDate>
				<category><![CDATA[Tech]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[career ladder]]></category>
		<category><![CDATA[career progression]]></category>
		<category><![CDATA[engineering career framework]]></category>
		<category><![CDATA[engineering ladder]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=671</guid>

					<description><![CDATA[I made a career progression framework for my engineering colleagues at Schuberg Philis but it doesn't work.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">I&#8217;ve published a <a href="http://sbp.progressionapp.com/teams/sbp-engineers">career framework</a> on <a href="https://progression.fyi/">Progression</a>. It is based on advice I frequently give to engineers and engineering leaders at <a href="https://schubergphilis.com/">Schuberg Philis</a> (SBP). Since I was having more such conversations than I have time for, I created this framework last year to help our coaches have more in-depth career development conversations without me.</p>



<p class="wp-block-paragraph">It doesn&#8217;t work!</p>



<h5 class="wp-block-heading">Disclaimer</h5>



<p class="wp-block-paragraph">Though I incorporated feedback from a lot of colleagues, this was never &#8220;the&#8221; SBP framework. Our company culture is as informal as it can be, with just enough structure. Making a framework such as this official is more than we need. We do not want people to think of their careers as a checklist to complete or as a ladder to climb. Instead, we suggest people mostly focus on growing their impact, picking up and mastering new competencies along the way. </p>



<h2 class="wp-block-heading">Framework</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://sbp.progressionapp.com/teams/sbp-engineers/framework" target="_blank" rel=" noreferrer noopener"><img loading="lazy" width="300" height="130" data-attachment-id="674" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-framework/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg" data-orig-size="2527,1099" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-framework" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=300" alt="" class="wp-image-674" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=300 300w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=600 600w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=150 150w" sizes="auto, (max-width: 300px) 85vw, 300px" /></a></figure>
</div>


<p class="wp-block-paragraph">The base <a href="https://sbp.progressionapp.com/teams/sbp-engineers/framework">framework</a> is rather abstract, defining 10 coarse-grained competencies that apply to all engineering roles, such as &#8220;maintaining quality&#8221; or &#8220;planning and organizing&#8221;. The competencies have up to 7 different levels. These 10 competencies are grouped into Craftsmanship, Human, and Leadership categories.</p>



<p class="wp-block-paragraph">The Progression app is organized into strict levels. To make the tool work for us, the online version of the SBP framework defines these levels too. But these are not actually used within SBP! It&#8217;s quite common to have some competencies at a very high level and others still much lower, and that&#8217;s ok with us. One common example is engineers with very highly rated craftsmanship skills but lower rated teamwork skills. The reverse also happens!</p>



<h2 class="wp-block-heading">Roles</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://sbp.progressionapp.com/teams/sbp-engineers"><img loading="lazy" width="300" height="142" data-attachment-id="676" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-roles/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg" data-orig-size="2400,1141" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-roles" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=300" alt="" class="wp-image-676" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=300 300w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=600 600w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=150 150w" sizes="auto, (max-width: 300px) 85vw, 300px" /></a></figure>
</div>


<p class="wp-block-paragraph">In this framework there are only three <a href="https://sbp.progressionapp.com/teams/sbp-engineers">roles </a>defined: engineers, tech leads, and technology officers.</p>



<p class="wp-block-paragraph">Engineers are our individual contributors (ICs), an industry term we don&#8217;t usually use. When engineers lead others, they do so primarily by example, based on the content of their work.</p>



<p class="wp-block-paragraph">Tech leads (TLs) are engineers that accept additional leadership responsibilities. Tech leads are not in charge of teams, we expect servant leadership of them.</p>



<p class="wp-block-paragraph">Technology officers (TOs) are a lot like tech leads but with somewhat different leadership responsibilities. While tech lead engineers lead one specific team, our technology officers have a somewhat broader scope. They&#8217;re a bit further removed from the day-to-day engineering work, giving them time to focus on cross-team topics and to do work for which there isn&#8217;t a dedicated engineering team available, like presales.</p>



<p class="wp-block-paragraph">TLs and TOs are leaders but not managers. At many companies with career ladders and levels, TLs and TOs might be considered staff or principal engineers, i.e. they are still on the &#8220;individual contributor&#8221; track. While some engineers progress by taking a TL or TO role, it&#8217;s important to realize there are engineers at a &#8220;staff&#8221; or &#8220;principal&#8221; level without a TL or TO role. Those engineers could match a different <a href="https://staffeng.com/guides/staff-archetypes/">staff-plus archetype</a> (like Solver or Architect), or just not match up with such a rigid framework at all. It&#8217;s also possible to switch from a TL or TO role to a regular engineering role.</p>



<p class="wp-block-paragraph">Engineers at SBP do have additional roles at times that require additional competencies. For example, engineers can become coaches. These are not part of the framework.</p>



<h2 class="wp-block-heading">Competencies</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://sbp.progressionapp.com/teams/sbp-engineers/skills"><img loading="lazy" width="300" height="221" data-attachment-id="677" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-skills/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg" data-orig-size="1698,1252" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-skills" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=300" alt="" class="wp-image-677" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=300 300w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=600 600w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=150 150w" sizes="auto, (max-width: 300px) 85vw, 300px" /></a></figure>
</div>


<p class="wp-block-paragraph">While Progression is optimized for defining pretty specific skills for pretty specific roles, this particular framework is broad and generic, and so is focused around <a href="https://sbp.progressionapp.com/teams/sbp-engineers/skills">competencies</a>.</p>



<p class="wp-block-paragraph">There are up to 7 different levels defined per competency.</p>



<p class="wp-block-paragraph">While the basic competencies are the same for TLs and TOs as for engineers, our expectations of where our leaders will focus are a little different, and so for some skills there is a leadership (L) variant defined. Since Progression doesn&#8217;t support such variant definitions, there&#8217;s some overlap/copies.</p>



<h2 class="wp-block-heading">Competency example: Client Orientation</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg"><img loading="lazy" width="296" height="299" data-attachment-id="679" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-client-orientation/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg" data-orig-size="1086,1099" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-client-orientation" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=296" alt="" class="wp-image-679" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=296 296w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=592 592w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=148 148w" sizes="auto, (max-width: 296px) 85vw, 296px" /></a></figure>
</div>


<p class="wp-block-paragraph">SBP is an outsourcing company. Our engineers work in self-steering teams dedicated to a single customer. Because all our engineers have a lot of customer interaction, the framework is relatively heavy on communication compared to many other <a href="https://progression.fyi/">progression examples</a>.</p>



<h2 class="wp-block-heading">Progression for comparing roles &amp; levels</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg"><img loading="lazy" width="300" height="211" data-attachment-id="682" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-comparison/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg" data-orig-size="1789,1261" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-comparison" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=300" alt="" class="wp-image-682" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=300 300w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=600 600w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=150 150w" sizes="auto, (max-width: 300px) 85vw, 300px" /></a></figure>
</div>


<p class="wp-block-paragraph">One cool feature in Progression is how it allows you to put different roles and different levels side by side. This can help engineers think about which direction they might develop in next, though since at SBP we don&#8217;t actually use levels it&#8217;s of limited use for us.</p>



<h2 class="wp-block-heading">Progression for check-ins</h2>


<div class="wp-block-image">
<figure class="alignleft size-medium"><a href="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg"><img loading="lazy" width="275" height="300" data-attachment-id="683" data-permalink="https://leosimons.com/2023/04/03/abstract-career-framework-for-engineering/sbp-progression-checkin/" data-orig-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg" data-orig-size="1155,1261" data-comments-opened="1" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="sbp-progression-checkin" data-image-description="" data-image-caption="" data-large-file="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=840" src="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=275" alt="" class="wp-image-683" srcset="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=275 275w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=550 550w, https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=137 137w" sizes="auto, (max-width: 275px) 85vw, 275px" /></a></figure>
</div>


<p class="wp-block-paragraph">A recent addition to Progression is a new check-in feature that arranges a guided conversation between engineers and their managers (or in SBP its case with no line management, coaches). The idea is to agree together on career progression.</p>



<p class="wp-block-paragraph">I like what they&#8217;ve built here. I imagine it&#8217;s a good way for HR departments to help beginning engineering managers have decent-quality factful conversations. But it&#8217;s not the right tool for Schuberg Philis.</p>



<p class="wp-block-paragraph">It&#8217;s a great example of our &#8220;too much structure&#8221; rule kicking in. We have an internal 360-degree feedback process that is less structured than this, asking open questions instead, focusing on the important feedback conversation more and happily sacrificing some structured/detailed record-keeping in the process. Maybe when SBP is 10 times bigger that has to change?</p>



<h2 class="wp-block-heading">Spreadsheet version</h2>



<p class="wp-block-paragraph">I created the Progression version of this framework based on my initial <a href="https://docs.google.com/spreadsheets/d/1Sz3tDzeKl-erdjznDJii5c34hMCFnFHorji2l8yVWqc/edit?usp=sharing">spreadsheet version of this career framework</a>. I&#8217;ve found most people feel quite overwhelmed (at best) when confronted with this spreadsheet, while HR professionals appreciate the comprehensive overview.</p>



<iframe src="https://docs.google.com/spreadsheets/d/e/2PACX-1vQrLGF79MfI2CexJ5fw_K05cEgwAlC-cPcYu7G9iPBSs_zrjOZsBjce5QA6sasKcH5p5M-ys5dL65Ya/pubhtml?gid=1716506889&#038;single=true&#038;widget=true&#038;headers=false" frameborder="0" width="100%" height="400" marginheight="0" marginwidth="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>



<h2 class="wp-block-heading">Success!</h2>



<p class="wp-block-paragraph">Developing this framework helped me think through career guidance in great depth and that has helped make my advice to others clearer. I sometimes refer back to it when helping to weigh or appraise feedback, during salary discussions, or when writing job specs.</p>



<p class="wp-block-paragraph">Putting the framework in Progression really helped to compare our framework to other engineering companies and to benchmark our internal levelless ranking system to common industry levels. Sometimes I can use it to explain to our younger engineers that no, they would not be ranked as principal engineers at Google, yet!</p>



<p class="wp-block-paragraph">The process of iterating on the framework together was valuable. Asking and discussing feedback helped to align our leadership&#8217;s thinking about how we appraise and value our colleagues. Beyond leadership, trying to describe very different work in common terms (dev and ops, for example) helped the people involved to understand more about each other.</p>



<p class="wp-block-paragraph">Our HR professionals happily adopted the framework, for example to develop and finetune our internal leadership training. For my HR colleagues relatively new to IT organizations it really helped them to see and understand both the breadth and the depth of our careers.</p>



<h2 class="wp-block-heading">Failure!</h2>



<p class="wp-block-paragraph">This framework hasn&#8217;t helped our engineers or their coaches! My colleagues may look at it, perhaps understand it as reasonable and sensible&#8230;and then will never use it during their career planning or career development conversations.</p>



<p class="wp-block-paragraph">People are unsure why, but they tell me the framework &#8220;doesn&#8217;t feel right&#8221;.</p>



<p class="wp-block-paragraph">I believe one problem is that the level of abstraction is so high people don&#8217;t know how to apply it for themselves in a practical way. Making more concrete roles (like &#8220;java developer&#8221;) with more concrete skills (like &#8220;java programming&#8221;) could help, but that&#8217;s a lot of work to set up and maintain.</p>



<p class="wp-block-paragraph">I believe another problem is that the setup is still too constrained: our actual appraisal system is more free-form. I imagine if we were to strictly define career levels that would then prompt more colleagues to use this framework. Especially if we linked the levels to salary ranges. And&#8230;I don&#8217;t know that would be positive. SBP tries to be a place where all our colleagues can <a href="https://schubergphilis.com/en/who-we-are/our-story">&#8220;become who they are&#8221;</a>, which for many colleagues can mean quite an &#8220;unbalanced&#8221; set of competencies.</p>



<p class="wp-block-paragraph">The framework also hasn&#8217;t helped me! Our non-technical coaches have difficulty using the more technical competencies. You need an experienced technologist&#8217;s help to evaluate the quality of your solutions designs or implementation plans, to determine how competent you got. It sucks to have a conversation about those things with a non-technical coach.</p>



<p class="wp-block-paragraph">So in the end even if my colleagues do use this framework, they will still ask for my help, and I save no time!</p>



<p class="wp-block-paragraph">Fortunately, it&#8217;s the kind of conversation I love to have.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">671</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg" />
		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg" medium="image">
			<media:title type="html">sbp-progression-framework</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-framework.jpg?w=300" medium="image" />

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-roles.jpg?w=300" medium="image" />

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-skills.jpg?w=300" medium="image" />

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-client-orientation.jpg?w=296" medium="image" />

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-comparison.jpg?w=300" medium="image" />

		<media:content url="https://leosimons.com/wp-content/uploads/2023/04/sbp-progression-checkin.jpg?w=275" medium="image" />
	</item>
		<item>
		<title>Salary setup for Dutch developers</title>
		<link>https://leosimons.com/2022/02/25/salary-setup-for-dutch-developers/</link>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Fri, 25 Feb 2022 14:27:45 +0000</pubDate>
				<category><![CDATA[Work]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=642</guid>

					<description><![CDATA[Let&#8217;s now ignore the BigTech companies who are paying lots of money, as well as the employers trying to compete with them, and instead think a bit about the average local Dutch market. What might a reasonable salary expectation for a full-time job at an average local Dutch employer? Permanent contract vs 1-year contract Dutch &#8230; <a href="https://leosimons.com/2022/02/25/salary-setup-for-dutch-developers/" class="more-link">Continue reading<span class="screen-reader-text"> "Salary setup for Dutch&#160;developers"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">Let&#8217;s now ignore the <a href="https://leosimons.com/2022/02/24/rising-developer-salaries-in-amsterdam/">BigTech companies who are paying lots of money</a>, as well as the employers trying to compete with them, and instead think a bit about the average local Dutch market. What might a reasonable salary expectation for a full-time job at an average local Dutch employer?</p>



<h5 class="wp-block-heading" id="permanent-contract-vs-1-year-contract">Permanent contract vs 1-year contract</h5>



<p class="wp-block-paragraph">Dutch employment law can get <a href="https://www.iamexpat.nl/career/working-in-the-netherlands/work-employment-contracts">pretty complex</a>, making the non-financial terms important when evaluating salary. One key factor is whether you&#8217;re looking at a permanent contract or not.</p>



<p class="wp-block-paragraph">For more senior positions, permanent contracts are common. If you do your job well and the company is doing ok financially, your employer cannot easily fire you from a permanent contract, which makes these popular with risk-averse employees and unpopular with commitment-averse employers.</p>



<p class="wp-block-paragraph">For more junior positions, 1-year contracts are common. 1-year contracts often get automatically extended, and then can convert into permanent contracts automatically after 3 years.</p>



<p class="wp-block-paragraph">Getting a mortgage is harder without a permanent contract since banks are also risk-averse.</p>



<p class="wp-block-paragraph">The base salaries for both contract types tend to be comparable, but permanent contracts usually have better non-base salary benefits leading to a better total benefits package.</p>



<h5 class="wp-block-heading" id="common-benefits-package-components">Common benefits package components</h5>



<p class="wp-block-paragraph">A Dutch contract usually has a <strong>yearly salary</strong> listed for a full-time job. Usually full-time for developer jobs means 40 hours a week. If you work less hours you will be paid proportionally less, while if you structurally work more you typically do not get paid for that at all.</p>



<p class="wp-block-paragraph">The base yearly salary includes 8% yearly <a href="https://www.iamexpat.nl/career/employment-news/holiday-allowance-and-vacation-days-netherlands"><strong>holiday allowance</strong></a>, which is usually paid once a year in May. In May you receive 15.67% of your yearly salary, while in all other months you receive 7.67%.</p>



<p class="wp-block-paragraph"><strong>Discretionary bonuses</strong> are now reasonably common in Dutch IT companies, paid out yearly, with 5%-20% of your yearly salary reasonably common, and 10% probably the most common percentage. Usually <em>discretionary </em>means that your manager informs you once a year if you get your bonus and how much you will get, i.e. your employer can decide for any reason not to pay you the bonus.</p>



<p class="wp-block-paragraph">It is uncommon for Dutch employers to actually <em>promise </em>bonuses, or otherwise tie bonus payout to profits or performance targets in a transparent way. When that arrangement is written into your contract it is a <strong>performance bonus</strong> instead of the common discretionary bonus, which you may encounter for more commercial roles (i.e. a role as a presales engineer).</p>



<p class="wp-block-paragraph"><strong>Stock options</strong>, <strong>shares</strong>, <strong>employee ownership</strong> plans, and <strong>profit sharing</strong> agreements are relatively uncommon for Dutch IT companies. Even when they are used, the &#8220;standard&#8221; approaches that exist in the US tech industry for stock options are not standard here, which means most US-centric advice on the internet about such things does not apply. The actual benefit of such programs relative to base salary can range from pretty neglible (more common) to pretty dominant (rare).</p>



<p class="wp-block-paragraph">Cars in The Netherlands are expensive to own and drive, due to how taxes for them work as well as due to high taxes on gas. <strong>Lease cars</strong> are common, where your employer arranges a car for you (often of the type and with the options you choose) with a leasing company. Your employer pays for your car every month, including for any gas used. If you also use this car for non-work travel then you have to pay extra income tax based on the catalog value of the car. If you like driving a modern car this usually works out cheaper than other approaches. At some employers, lease cars are getting replaced with <strong>mobility budgets</strong> or <strong>public transport passes</strong>.</p>



<p class="wp-block-paragraph">A full-time contract includes a mandatory minimum of 20 vacation days. These are a legal right; you might see them as a government-mandated benefit. Many employers include <strong>extra vacation days</strong> above 20, sometimes with various differing rules for exchanging days for cash, buying extra days, and so on. Besides vacation you also have rights to parental leave, sick days, and so on.</p>



<p class="wp-block-paragraph"><strong>Paid overtime</strong> and <strong>standby shift compensation</strong> are rather uncommon for developers. If your contract does include paid overtime it is likely based on extra hours worked per week or month, which means tracking all hours. Night or weekend hours may be paid at 150% or 200%. Working a lot of overtime can mean quite significant additional income.</p>



<p class="wp-block-paragraph">Most permanent contracts will include a <strong>pension</strong>, for which there are many different and confusing different setups that are hard to compare and value. It&#8217;s common these days that your employer pays some monthly amount into a fund on your behalf that is based on a combination of your base salary plus your age, with the amount being only a few percent for people under 30 and quite a lot for those close to retirement. The fine print around Dutch pensions is often more complex than for the rest of the employment contract combined.</p>



<p class="wp-block-paragraph">Many employers also mention providing the things you need to do your job (like laptops, phones, and training budget) as benefits, or they point out lunch or snack arrangement or fun company events. While of course it&#8217;s nice if an employer provides such <strong>perks</strong>, when contrasting offers financially most of these are usually not significant. It&#8217;s also very rare for these perks to be promised contractually.</p>



<p class="wp-block-paragraph">One aspect that is not considered a perk is what <strong>working from home</strong> arrangements have been made. Besides your own preferences around this, not having a daily commute may save you money &amp; time, while not having your home office setup paid for may cost you money or health.</p>



<h5 class="wp-block-heading" id="salary-ranges">Salary ranges</h5>



<p class="wp-block-paragraph">Dutch developer salaries are perhaps <a href="https://www.payscale.com/research/NL/Job=Software_Developer/Salary">€43k average</a>, compared to <a href="https://www.cpb.nl/augustusraming-2021-2022">€38k median</a> salary across the country for all jobs. I&#8217;ve never heard of a developer making half that average, while double the average and more does happen. Most developers make close to and perhaps a bit less than the average, while a select few developers make a lot more. What&#8217;s going on?</p>



<ul class="wp-block-list"><li>Senior developers switch into different higher-paying jobs that have different titles, like architect, manager, or consultant.</li><li>Senior developers go freelance, so they write invoices instead of receive a salary.</li><li>Senior developers don&#8217;t do things to increase their salary, instead doing things they find more fun or interesting.</li><li><a href="https://danluu.com/talent/">We have no idea what individual developers are worth</a>, so there are few good arguments for employers to pay much higher than average.</li></ul>



<p class="wp-block-paragraph">Even for those salaried developers that do continue to hone their craft and gain the experience that clearly makes them more valuable, I believe the salary curve flattens out. A bit of this is simply due to how The Netherlands thinks about what&#8217;s a fair distribution of income. A bit of this is because developers don&#8217;t like salary negotiation so their employer ends up paying them what they can, instead of what they are worth.</p>



<h5 class="wp-block-heading" id="evaluating-benefits-packages">Evaluating benefits packages</h5>



<p class="wp-block-paragraph">With all that basic understanding in place, you might now enjoy playing with this <a href="https://docs.google.com/spreadsheets/d/1f09Ombqcux91S0scE8pVWa0ZFH-TxFT1JSM7__XRhEw/edit?usp=sharing">google sheet of example developer salaries</a> that I invented to make the above graph:</p>



<iframe src="https://docs.google.com/spreadsheets/d/e/2PACX-1vQ78FhXMEDquuNG33jUD5qdlh5fZKKgkr_m3Jb4D_x-29aLgAq5AxsbivFrUwR8mQehEvZXqXUtLw-r/pubhtml?gid=0&#038;single=true&#038;widget=true&#038;headers=false" frameborder="0" width="100%" height="420" marginheight="0" marginwidth="0" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>



<p class="wp-block-paragraph">The sheet might help as a baseline for evaluating different benefits packages and contrasting some different salary ladders against a simple baseline. The absolute values are probably less useful, though I did attempt to have them be reasonable based on my own experience and some general awareness of 2021 numbers at a couple different companies.</p>
]]></content:encoded>
					
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">642</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2022/02/developer-salary-by-level-1.png" />
		<media:content url="https://leosimons.com/wp-content/uploads/2022/02/developer-salary-by-level-1.png" medium="image">
			<media:title type="html">developer-salary-by-level-1</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
		<item>
		<title>Rising developer salaries in Amsterdam</title>
		<link>https://leosimons.com/2022/02/24/rising-developer-salaries-in-amsterdam/</link>
					<comments>https://leosimons.com/2022/02/24/rising-developer-salaries-in-amsterdam/#comments</comments>
		
		<dc:creator><![CDATA[Leo Simons]]></dc:creator>
		<pubDate>Thu, 24 Feb 2022 08:12:22 +0000</pubDate>
				<category><![CDATA[Work]]></category>
		<guid isPermaLink="false">http://leosimons.com/?p=636</guid>

					<description><![CDATA[What&#8217;s a reasonable base salary for hiring really great senior software engineers in Amsterdam in 2022? As always &#8220;it depends&#8221;, but the answer is also jarringly different in 2022 than it was in 2019. Sample goal: cool company where no-one worries about money FAANG pay high salaries but not everyone wants to work for them. &#8230; <a href="https://leosimons.com/2022/02/24/rising-developer-salaries-in-amsterdam/" class="more-link">Continue reading<span class="screen-reader-text"> "Rising developer salaries in&#160;Amsterdam"</span></a>]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">What&#8217;s a reasonable base salary for hiring really great senior software engineers in Amsterdam in 2022? As always &#8220;it depends&#8221;, but the answer is also jarringly different in 2022 than it was in 2019.</p>



<h5 class="wp-block-heading" id="sample-goal-cool-company-where-no-one-worries-about-money">Sample goal: cool company where no-one worries about money</h5>



<p class="wp-block-paragraph"><a href="https://www.levels.fyi/">FAANG pay high salaries</a> but not everyone wants to work for them. You may choose to work for a smaller company, or a cooler one, or one with better values. Perhaps then we can pay a bit less?</p>



<p class="wp-block-paragraph">Let&#8217;s look for a really cool small company with solid values, and see what they pay. <a href="https://oxide.computer/blog/compensation-as-a-reflection-of-values">Bryan Cantrill&#8217;s Oxide Computer pays everyone who works at their company a $180k base salary</a> (€160k). That is enough:</p>



<ol class="wp-block-list">
<li>to live comfortably with 3 kids in the Bay Area;</li>



<li>to be in the top 5% of income in the US;</li>



<li>to not worry about money, even when putting extra organic raspberries in your shopping basket.</li>
</ol>



<p class="wp-block-paragraph">They also provide good benefits like healthcare and stock options. For them, €160k is enough to take money of the table as a employee demotivator, enough to assemble a great team.</p>



<p class="wp-block-paragraph">The people that work at Oxide Computer could earn more at other employers. That&#8217;s ok: beyond some amount, more money simply matters less than other things, like pursuing autonomy, mastery, and purpose. What they offer candidates is something like <em>join a great team, try to make something cool <a href="https://oxide.computer/principles">following decent principles</a>, live comfortably, and have a small chance to become independently wealthy</em>.</p>



<p class="wp-block-paragraph">While it&#8217;s not a goal for them forever, Oxide so far has (quite) senior engineers only, of which Bryan writes &#8220;engineers at the earliest stages of their career are unlikely to be a fit for us right now&#8221;.</p>



<p class="wp-block-paragraph">Let&#8217;s imagine we start a similarly cool company, with similar hiring goals, but in Amsterdam, The Netherlands. Let&#8217;s imagine we want to hire (someone like) Bryan as our first awesome senior engineer. He&#8217;ll get options or shares of course, and a nice pension, and so on, but what base salary should we pay him so money is not a worry?</p>



<h5 class="wp-block-heading" id="76-000">€76.000?</h5>



<p class="wp-block-paragraph">The <a href="https://www.cpb.nl/augustusraming-2021-2022">estimated modal income in The Netherlands for 2022</a> is €38k gross. €38k is the income where 50% of employees make less and 50% make more than that. An example of a job where you can expect to make about €38k is as a nurse with a college degree.</p>



<p class="wp-block-paragraph">€76k is double that modal income. Ignoring market conditions, I consider double-modal quite a decent benchmark salary for individual contributor senior software engineers with a lot of experience.</p>



<ul class="wp-block-list">
<li>Until our recent housing crisis, a double-modal income was enough to buy a house, support a family, and otherwise live quite a comfortable life in The Netherlands;</li>



<li>€76k is more than the average senior software engineer makes in The Netherlands, which is probably about €60k;</li>



<li>Above-average does seem reasonable to me, after all this is Bryan Cantrill we&#8217;re trying to hire!</li>



<li>It <em>feels</em> right to me.</li>
</ul>



<p class="wp-block-paragraph">Unfortunately, with the rising house prices, €76k salary is currently not enough for Bryan to buy a comfortable Amsterdam family home, and it&#8217;s definitely not enough for unlimited organic fruit. We should deal with our housing crisis and make healthy food cheaper, but in the meantime&#8230;</p>



<h5 class="wp-block-heading" id="100-000">€100.000??</h5>



<ol class="wp-block-list">
<li>To live in a modern 4 bedroom house in the greater Amsterdam area, Bryan&#8217;s mortgage will be about €550k, for which he will need a salary of about €100k;</li>



<li>He&#8217;ll pay about 40% income tax, so will have net income of about €60k, which puts him in <a href="https://www.cbs.nl/nl-nl/visualisaties/inkomensverdeling">the top 5% of income in The Netherlands</a>;</li>



<li>After base costs, perhaps €30k remains as disposable income, which should be plenty for all the raspberries he&#8217;d like, plus a couple of family ski trips a year, plus buying carbon offsets for that trip.</li>
</ol>



<p class="wp-block-paragraph">€100k ought to be enough of a base salary based on the goals we picked. It&#8217;s is a lot more than most software engineers make in The Netherlands, but $180k is of course also more than most software engineers make in the USA. We want excellent people so we will have to pay an excellent salary <img src="https://s0.wp.com/wp-content/mu-plugins/wpcom-smileys/twemoji/2/72x72/1f642.png" alt="🙂" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>



<h5 class="wp-block-heading" id="140-000">€140.000???</h5>



<p class="wp-block-paragraph">Most expats prefer living in Amsterdam itself instead of merely close to it. It&#8217;s nice when the staff at the local organic grocery shop speaks fluent English as they point out the organic raspberries isle. To live in Amsterdam itself, Bryan&#8217;s cost of living goes up a lot, especially for housing.</p>



<ol class="wp-block-list">
<li>He will need perhaps an €800k mortgage, for which he needs a salary of about €140k;</li>



<li>His taxes will then be a bit higher too, for a net income of about €80k, which now puts him in the top 2% of income in The Netherlands;</li>



<li>Perhaps €35k of that is now disposable income, still plenty for those more expensive Amsterdam raspberries.</li>
</ol>



<p class="wp-block-paragraph">Beyond just money, the living standards for the top 5%/2% of earners in The Netherlands are arguably quite amazing compared to the Bay Area. After all The Netherlands is one of the best places to live in the world! Rather than try to flesh out that argument here I&#8217;ll point out the excellent YouTube channel <a href="https://www.youtube.com/c/NotJustBikes/videos?view=0&amp;sort=p&amp;flow=grid">NotJustBikes</a>.</p>



<p class="wp-block-paragraph">So is this €140k enough to get Bryan to join us? Of course €140k isn&#8217;t €160k…but since The Netherlands is so awesome, it does seems somewhat reasonable to me to have to take a bit of a paycut to live here, just like it also seems reasonable to take a bit of a paycut to work for an amazing company such as Oxide Computer. That 12% paycut might be ok to Bryan, and is a bit less than the 15% cost of living difference between the Bay Area and Amsterdam, so day to day it may not feel like a big paycut to him.</p>



<h5 class="wp-block-heading" id="160-000">€160.000????</h5>



<p class="wp-block-paragraph">It&#8217;s 2022 and many US tech companies are all-in on full-remote work, often no longer adjusting for cost of living when setting global salaries. More and more of these companies will pay those Bay Area salaries over here, so to be globally competitive in the job market €160k now really is <a href="https://techpays.com/countries/netherlands/amsterdam/tier-3">the rate to match</a> and that&#8217;s what our imaginary Amsterdam startup should probably do.</p>



<p class="wp-block-paragraph">Wow. While €100k is already considered a high salary by most Dutch standards for just about any regular job, €160k plus benefits is more than most Dutch software engineers imagined they could make at any point in their careers. Over here that €160k makes you a part of the 1% even before considering the rest of your benefits package. Many software engineers are loyal employees who don&#8217;t like to change jobs, but finding out a life-changing amount of money is perhaps on offer (while building cool things with Bryan Cantrill, no less!) many of us are tempted!</p>



<p class="wp-block-paragraph">On the other side of the negotiating table, as a hiring manager who pays their engineers €76k or perhaps €100k and thinks that it is quite a generous and reasonable amount, it can be jarring when we hear that those same engineers now earn 60-100% more somewhere else for the same (or more fun!) work. Should we try and somehow match the US salaries, or, should we accept we&#8217;re not a globally competitive employer and risk losing some of our best people? Ugh! That also doesn&#8217;t <em>feel</em> right.</p>



<h5 class="wp-block-heading" id="how-high-will-salaries-go">How high will salaries go?</h5>



<p class="wp-block-paragraph">Over the last few quarters a lot of Dutch employers are <a href="https://blog.pragmaticengineer.com/off-cycle-compensation-adjustments-for-software-engineers-in-2021/">bumping engineering salaries by 10-20% or more</a>, <a href="https://www.asdcode.de/2022/02/it-salary-survey-december-2021.html">German IT salaries are going up fast</a> too, and probably this really is true across Europe. Employers are trying to remain competitive in the job market&#8230;but is a 10% bump enough when 60% or 100% more salary is on offer elsewhere?</p>



<p class="wp-block-paragraph">Since the scarcity of senior software engineers won&#8217;t be fixed change quickly, and the BigTech companies can afford to pay a lot, it seems like the top global salaries are poised to still go up further in the short term, in turn creating further strain in our local job market, and keeping the money topic on the table for most IT employers and employees everywhere.</p>



<h5 class="wp-block-heading" id="will-salaries-come-back-down">Will salaries come back down?</h5>



<p class="wp-block-paragraph">Perhaps. The global economy seems to be overheating. It could be we are in another bubble, which could burst, which could turn the scarcity of engineers into surplus of engineers really quickly as investment drops. Since no-one really understands how the global economy works, it&#8217;s hard to be sure.</p>



<p class="wp-block-paragraph">(Disclaimers: I work in outsourcing, not at a startup. I work and live in the Amsterdam area but not in Amsterdam itself. This is not how compensation is set up where I work. I make a lot less money than Bryan Cantrill, who was also not consulted about this blog post (I owe you some raspberries, Bryan).)</p>
]]></content:encoded>
					
					<wfw:commentRss>https://leosimons.com/2022/02/24/rising-developer-salaries-in-amsterdam/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">636</post-id>
		<media:thumbnail url="https://leosimons.com/wp-content/uploads/2022/02/rising-developer-salaries-medium.png" />
		<media:content url="https://leosimons.com/wp-content/uploads/2022/02/rising-developer-salaries-medium.png" medium="image">
			<media:title type="html">rising-developer-salaries-medium</media:title>
		</media:content>

		<media:content url="https://0.gravatar.com/avatar/f143f73fe0eba6aa3d82ef89a93e746a76637af3040c276a2421762a1f106676?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">lsimons</media:title>
		</media:content>
	</item>
	</channel>
</rss>
