<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Mark van Lent’s weblog</title>
  <updated>2026-04-13T07:56:23+00:00</updated>
  <link rel="self" type="application/atom+xml" href="https://markvanlent.dev/index.xml" hreflang="en"/>
  <id>tag:markvanlent.dev,2010-04-02:/index.xml</id>
  <link rel="alternate" type="text/html" href="https://markvanlent.dev/" hreflang="en"/>
  <author>
      <name>Mark van Lent</name>
      <uri>https://markvanlent.dev/about/</uri>
    </author>
  <rights>Copyright (c) Mark van Lent, Creative Commons Attribution 4.0 International License.</rights>
  <icon>https://markvanlent.dev/favicon.ico</icon>
  <entry>
    <title type="html"><![CDATA[AI Engineer Europe 2026: reflection]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2026/04/13/ai-engineer-europe-2026-reflection/" type="text/html" />
    <id>https://markvanlent.dev/2026/04/13/ai-engineer-europe-2026-reflection/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="ai" />
    <category term="conference" />
    <category term="opinion" />
    
    <updated>2026-04-13T07:55:45Z</updated>
    <published>2026-04-13T00:00:00Z</published>
    <content type="html"><![CDATA[<p>Now that the dust is settling, it&rsquo;s time to reflect a bit on what I&rsquo;ve heard at
the conference.</p>
<h2 id="my-takeaways">My takeaways</h2>
<p>This was my first AI Engineer conference and also the first AI conference I
attended. It was a nice way to gauge where both I and we as a company stand.
This is especially true when I realise that mostly (only?) companies at the
forefront of these developments were there. AI is hot, but as we
<a href="/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/#most-enterprise-agentic-projects-are-doomed--heres-why--jess-grogan-avignon-and-jack-wang">have heard</a>,
only 12% of the (big) companies are &ldquo;AI achievers&rdquo;.</p>
<p>What I think the speakers agreed on:</p>
<ul>
<li>The role of the engineer is shifting. Less coding, more writing
specifications, planning and reviewing</li>
<li>Codebases must be designed for humans <strong>and</strong> agents to read and understand</li>
<li>Guardrails are essential. If we want to give agents more freedom to solve
problems, we need to give them a certain amount of freedom, but we need
guardrails first</li>
<li>We need to have feedback loops. The quality of the feedback loop determines
the quality of the output</li>
<li>The human stays in the loop (at least for now)</li>
<li>Introducing AI is a process. Start with non-critical, well-scoped tasks and
let the system &ldquo;prove&rdquo; itself and earn trust.</li>
</ul>
<p>There are also things the speakers do not agree on:</p>
<ul>
<li><a href="/2026/04/09/ai-engineer-europe-2026-keynote/session-day-1/#harness-engineering-how-to-build-software-when-humans-steer-and-agents-execute--ryan-lopopolo">Code is free</a> vs <a href="/2026/04/09/ai-engineer-europe-2026-keynote/session-day-1/#it-aint-broke-why-software-fundamentals-matter-more-than-ever--matt-pocock">Code is not cheap</a></li>
<li>Move fast, <a href="/2026/04/09/ai-engineer-europe-2026-keynote/session-day-1/#harness-engineering-how-to-build-software-when-humans-steer-and-agents-execute--ryan-lopopolo">have the agents do the full job</a> and <a href="/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/#cicd-is-dead-agents-need-continuous-compute-and-computers--hugo-santos-and-madison-faulkner">remove humans from the loop to speed up</a> vs <a href="/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/#building-pi-in-a-world-of-slop--mario-zechner">slow down</a> and <a href="/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/#the-friction-is-your-judgment--armin-ronacher-and-cristina-poncela-cubeiro">think</a></li>
</ul>
<p>Other takeaways (that were not explicitly mentioned by multiple speakers):</p>
<ul>
<li>Agents are bad at self-evaluation</li>
<li>Only the first ~100K tokens of context are useful. More context will only make
the agent dumber</li>
<li>Agents are consuming APIs, documentation and websites and might even already
make up a large portion of their users</li>
<li>I should probably use more skills</li>
</ul>
<p>In general, I think we, as an industry, are still figuring out how to work with
AI. This is also directly related to the rapid pace at which things are
changing. Something may work today, but may be obsolete next month.</p>
<h2 id="the-conference">The conference</h2>
<p>Overall, I liked the atmosphere and energy. The organizers managed to get a great
bunch of speakers on stage who delivered insightful content.</p>
<p>I&rsquo;m a bit on the fence about the workshop day. I really liked that the speakers
had more time to go in depth (which is hard in the 20-minute timeslot of the
breakout sessions). On the other hand, I was expecting to do more &ldquo;work&rdquo; in a
workshop. However, most talks I attended were just that: longer talks. And if it
were not for taking notes, I would not have needed to bring my laptop.</p>
<p>Having said that, I had fun, learned a lot and would love to go again next year.</p>
<h2 id="the-trip">The trip</h2>
<p>This wasn&rsquo;t my first trip to London, but it was my first conference there. The
conference was held in the
<a href="https://en.wikipedia.org/wiki/Queen_Elizabeth_II_Centre">Queen Elizabeth II Centre</a>,
which is in the center of the city. And especially since we stayed in a hotel
close to the venue and could walk over there each day, I was very aware of where
we were, which was great. (I like London, can you tell?)</p>
<p>This was also my first time going to a conference with such a big group.
Sixteen people from Schuberg Philis were there! This also meant that for most
(perhaps even all?) of the sessions I joined, I was not the only one from our
company in the room, which means I could discuss what we&rsquo;ve heard and how it
applies to our company and customers. This added a lot of value for me.</p>
<p>And it&rsquo;s also nice to get to know my colleagues a bit better, especially the
people I don&rsquo;t work with on a day-to-day basis. And while we talked a lot of
shop there, there was also time and space to bond on a more personal level.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I think I can say that we, as a company, are in a good place with how we are
adopting AI and helping our customers. Having said that: AI engineering is a
field that is still very much developing. So we&rsquo;ll continue to learn and adapt.</p>
<p>As for myself: I&rsquo;ll work, together with my colleagues, on integrating the
takeaways into my everyday work.</p>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[AI Engineer Europe 2026: Keynote/Session Day 2]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/" type="text/html" />
    <id>https://markvanlent.dev/2026/04/10/ai-engineer-europe-2026-keynote/session-day-2/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="ai" />
    <category term="conference" />
    
    <updated>2026-04-11T01:06:51Z</updated>
    <published>2026-04-10T00:00:00Z</published>
    <content type="html"><![CDATA[<p>The third, and final day of the AI Engineer Europe 2026 conference. The format
of the day is similar to the previous day: kick the day off with keynotes, then
breakout sessions and close with keynotes at the end again. Today I spent most
of my time in the AI Architects track.</p>
<h2 id="building-pi-in-a-world-of-slop--mario-zechner">Building pi in a World of Slop &mdash; Mario Zechner</h2>
<p><a href="https://github.com/badlogic/pi-mono">Pi</a> is the engine inside <a href="https://openclaw.ai/">OpenClaw</a>.</p>
<p>Mario has several issues with Claude Code. To name a few: the system prompt
changes on every release, zero observability, zero model choice, almost zero
extensibility. He gravitated towards <a href="https://opencode.ai/">OpenCode</a> as a
replacement. But Mario found some design choices he did not like.</p>
<p>Started developing pi. It contains four packages:</p>
<ul>
<li>pi-ai: a unified LLM API</li>
<li>pi-tui: a terminal UI framework</li>
<li>pi-agent-core: an agent loop for tool execution, validation, event streaming and message queuing</li>
<li>pi-coding-agent: the CLI</li>
</ul>
<p>Pi has four tools:</p>
<ul>
<li>Read</li>
<li>Write</li>
<li>Edit</li>
<li>Bash</li>
</ul>
<p>That&rsquo;s it.</p>
<p>It is in YOLO mode by default (in other words: it can execute commands without
asking for permission). You need to implement your own guardrails that fit for
<em>your</em> security needs. You can ask pi to implement those for you if you want.</p>
<p>Pi is extensible:</p>
<ul>
<li>skills</li>
<li>prompt templates</li>
<li>themes</li>
<li>extensions: tools, command, shortcuts, compaction, etc.</li>
</ul>
<p>All of these hot-reload.</p>
<p>How do you build an extension? You don&rsquo;t. You specify what you need, and have Pi
build it.</p>
<p>But none of this is about pi. It&rsquo;s about taking control of your own tools.</p>
<p><a href="https://en.wikipedia.org/wiki/Clanker">Clankers</a> are ruining OSS. Mario
auto-closes new PRs that look AI generated with the message that the user should
try again. Since Clankers don&rsquo;t read this, he gets rid of the bots this way.
Users that indeed try again are not blocked the second time.</p>
<blockquote>
<p>Agents are merchants of learned complexity</p></blockquote>
<p>Models are trained on bad architecture decisions and cargo cult best practices.
So it&rsquo;s trained on garbage. The model fills in blanks in your specs with garbage from the internet.</p>
<p>Humans work differently since we feel pain and fix things to resolve/prevent the
pain. Agents don&rsquo;t learn the way we learn.</p>
<p>How we <em>should</em> work with agents:</p>
<ul>
<li>Scoped, so that the agent doesn&rsquo;t need to load tons of code</li>
<li>With a closed loop so the agent can evaluate its own work</li>
<li>Nothing mission critical (instead: dashboards, debugging tools, etc)</li>
<li>Work on the boring stuff or things you haven&rsquo;t had time for yourself</li>
<li>Reproduce cases from user issues</li>
</ul>
<p>Most importantly:</p>
<blockquote>
<p>Slow the fuck down</p></blockquote>
<p>Think about what you are building and why. Also learn to say &ldquo;no&rdquo;. It&rsquo;s easy to
add features, but are they the right features to add? Limit generated code to
what you can review. And do review every line if it&rsquo;s critical code.</p>
<h2 id="the-friction-is-your-judgment--armin-ronacher-and-cristina-poncela-cubeiro">The Friction Is Your Judgment &mdash; Armin Ronacher and Cristina Poncela Cubeiro</h2>
<p>Agents initially felt like an unlocked secret: you get more done. Now you <em>have</em>
to use them or you fall behind. It is not sustainable to do reviews and have
time to think. It&rsquo;s addictive. We are tricked into thinking we are doing more
work. But we have less time to reflect on whether we are even doing the right thing. It
is hard to know when to stop.</p>
<p>Before agents there was a balance between creating and reviewing code. But the
creation part is now amplified. The parts the engineer still has to do are <em>not</em>
amplified. As a result reviews are skipped or rubber-stamped.</p>
<blockquote>
<p>The moments where you want to skip thinking are exactly the moments where it
matters most.</p></blockquote>
<p>Agents are optimized to write code that runs. They are not as good at making an
overall good design. Agents introduce more code paths and local failures. And a
degrading codebase reinforces itself: the agents will create worse code.</p>
<p>Libraries have clearly defined problems they are solving. And they likely have
a simple core. Products, on the other hand, have more interacting components:
flags, permissions, billing, etc. The components are more intertwined. One of
the problems with that is that the context window cannot hold the full picture.</p>
<p>Your codebase has become infrastructure. So you have to design it in a way the
agent can read it. To have an agent-legible codebase you need to have:</p>
<ul>
<li>Modularization with clear boundaries so agents can work in a single area at a
time</li>
<li>Known patterns and conventions the agent can use for pattern-matching</li>
<li>A simple core (push the complexity to layers above)</li>
<li>No hidden magic. If the agent cannot see it, it cannot take it into account</li>
</ul>
<p>Examples of mechanical enforcements:</p>
<ul>
<li>No bare catch-all. This forces the agent to think about error handling</li>
<li>No raw SQL outside the abstraction layer to preserve the query interface</li>
<li>Use components for the UI</li>
<li>No dynamic imports</li>
<li>Enforce unique function names so it&rsquo;s easier to <code>grep</code> for a name</li>
<li>Use <code>erasableSyntaxOnly</code> TypeScript mode</li>
</ul>
<p>In pull request reviews you should separate the input going back to the agent
from what needs to go to a human to make a judgement call.</p>
<p>We still have to go slow. It becomes harder to understand what is going on
in the codebase. This makes cleanup also harder. It&rsquo;s harder to judge the state
of your codebase.</p>
<p>While we like to remove friction, some friction is useful. It makes it possible
to steer the project. The friction isn&rsquo;t your enemy, it&rsquo;s your judgement.</p>
<h2 id="context-is-the-new-code--patrick-debois">Context Is the New Code &mdash; Patrick Debois</h2>
<p>We are prompting to turn context into code. But we are also transforming code back into context in the form of skills.</p>
<p>Patrick is talking about the context development lifecycle today.</p>
<figure><img src="/images/ai_engineer_2026_patrick_debois_cdlc.jpg"
    alt="The Context Development Lifecycle"><figcaption>
      <p>Patrick Debois explained the Context Development Lifecycle. Image taken from <a href="https://tessl.io/blog/context-development-lifecycle-better-context-for-ai-coding-agents/">his article</a> since I didn&rsquo;t get a picture of it</p>
    </figcaption>
</figure>

<h3 id="generate-create--curate-context">Generate: create &amp; curate context</h3>
<p>Prompting is using humans as a context engine. If you want to get advanced: create
rules/instructions (<code>AGENT.md</code> or <code>CLAUDE.md</code>) to have reusable pieces of
context. You can also bring in context in the form of library documentation or
pull context from other places (context connectors, like MCP). But spec
driven development is also a form of context.</p>
<h3 id="evaluate-test--measure-context-quality">Evaluate: test &amp; measure context quality</h3>
<p>We&rsquo;re not yet writing evals for our code context. We could validate our skills
(more or less linting). Use LLM as a judge if the generated code matches the
criteria. Compare the outcome with code generated <em>without</em> the context in your
<code>AGENT.md</code> to see if the context has an effect.</p>
<p>Once a judge gets agents and can do stuff, you basically get an end-to-end test.
The tests give feedback what is working and what is missing. We can generate
actions from there to improve context.</p>
<p>Can we run this in CI/CD? That&rsquo;s hard, because it&rsquo;s not deterministic. Better to
run e.g. 5 or more times to see if it works most of the time.</p>
<h3 id="distribute">Distribute</h3>
<p>What if you want to reuse context in multiple projects/teams? You want to
package the context in one way or another. Then the question becomes: how do you
discover the skills? Via a skills marketplace. But most skills on there are
crap. (It&rsquo;s still useful to learn from others though.) A skill contains
context, code, etc.</p>
<p>We&rsquo;re also going to have dependencies and thus dependency hell.</p>
<p>We&rsquo;ll need security and scan the context. (Snyk has options for this.) Who built
this skill, with what model? We&rsquo;ll need a skill
<a href="https://en.wikipedia.org/wiki/Software_supply_chain">SBOM</a>.</p>
<h3 id="observe-monitor-and-improve-in-production">Observe: monitor and improve in production</h3>
<p>If you maintain a skill as something someone else can use, how do you get
feedback how it&rsquo;s working for others? Look at agent logs. Agent traces. Any
feedback on a PR that it&rsquo;s not correct is also feedback.</p>
<p>What about running code that&rsquo;s running in production and was created from a
context? You could e.g. use <a href="https://www.hud.io/">Hud</a></p>
<p>Agent sandboxing. Is code running in production doing strange things? Having a
context filter is like a WAF and can prevent prompt injections. Use harness
engineering.</p>
<blockquote>
<p>Context is the fuel. Coding agents are the engine.</p></blockquote>
<p>Related articles:</p>
<ul>
<li><a href="https://tessl.io/blog/cicd-for-context-in-agentic-coding-same-pipeline-different-rules/">CI/CD for Context in Agentic Coding: Same Pipeline, Different Rules</a></li>
<li><a href="https://tessl.io/blog/context-development-lifecycle-better-context-for-ai-coding-agents/">The Context Development Lifecycle: Optimizing Context for AI Coding Agents</a></li>
<li><a href="https://openai.com/index/harness-engineering/">Harness engineering: leveraging Codex in an agent-first world</a></li>
</ul>
<p>(Full disclosure: Patrick works for Tessl.)</p>
<h2 id="most-enterprise-agentic-projects-are-doomed--heres-why--jess-grogan-avignon-and-jack-wang">Most Enterprise Agentic Projects Are Doomed — Here&rsquo;s Why &mdash; Jess Grogan-Avignon and Jack Wang</h2>
<p>The presenters work in the world of large enterprises. A bad deployment can take down
critical infrastructure. Control, process, repeatability and governance structures
for human speed, not machine speed.</p>
<p>Only 12% of the companies are &ldquo;AI Achievers&rdquo;. The other 88% of them remain stuck
and are falling behind.</p>
<p>Things that have made companies successful are now holding them back in the time
of AI.</p>
<figure><img src="/images/ai_engineer_2026_jess_grogan-avignon_and_jack_wang.jpg"
    alt="Enterprise behaviours"><figcaption>
      <p>Jess Grogan-Avignon and Jack Wang about enterprise tensions that could hold agentic projects back</p>
    </figcaption>
</figure>

<dl>
<dt>Speed</dt>
<dd>Enterprise speed is running at human speed (with practices such as security
reviews, deployment process, etc). Corporations have not invested like tech
companies have. An example: an application took 2 weeks to build, but 12
months to get it into production.
<p>Approval infra hasn&rsquo;t kept up with the speed at which the supply of code is
growing. To go faster, every human step in the process should become
executable, adaptable code.</p>
</dd>
<dt>Value</dt>
<dd>Needing a business case is not wrong per se. However, they assume scope,
solution, expected value and cost to deliver are known beforehand. But when
the cost to prototype drops massively, you can more easily test things that
were economically impossible before.
<p>Enterprises need to think as a VC: take a bit of risk, not everything will
turn into profit. Try out things.</p>
</dd>
<dt>Delivery</dt>
<dd>Treating a scientific process like software feature delivery. Utopian design
upfront with guaranteed performance and status updates. Instead of building
the thing.
<p>IT is not the problem. The team needs to upskill product managers,
architects, etc around building confidence.</p>
</dd>
<dt>Trust</dt>
<dd>Completed features are not the most valuable thing you ship. There is a large
trust gap. The trust you build when using an AI is the most valuable. Trust in
content quality, accuracy, security, reliability.
<p>Agent autonomy is gated by evidence in outcomes, you need to earn autonomy.
Use what the user is saying to iterate. Shadow mode -&gt; advisory mode -&gt;
controlled autonomy -&gt; expanded autonomy. Engineer for trust, not completion.</p>
</dd>
<dt>Moat</dt>
<dd>What is unique for you? When your customer touches our product. Deployment is
the starting line, not the finish line. How fast can you iterate? Continuous,
compounding feedback loop.</dd>
</dl>
<p>Prescription to succeed:</p>
<ul>
<li>Start now: deliver differently and measure in confidence</li>
<li>Make finance a transformation partner, not a gatekeeper</li>
<li>Make governance speed an engineering problem</li>
<li>Redefine your moat as what you compound from today</li>
</ul>
<h2 id="the-domain-native-ai-organization-how-to-leverage-domain-expertise--chris-lovejoy">The Domain-Native AI Organization: How to Leverage Domain Expertise &mdash; Chris Lovejoy</h2>
<p>We often do not have deep understanding about the processes we are automating.
Use domain experts as oracle, evaluator or architect.</p>
<ul>
<li>Oracle: directly adds expertise</li>
<li>Evaluator: define and measure quality</li>
<li>Architect: build self-improving systems</li>
</ul>
<p>Most common mistakes:</p>
<ul>
<li>Not hiring domain experts (or too late)</li>
<li>Wrong kind of domain expert</li>
<li>Not fitting them in the organization properly</li>
</ul>
<p>Do you need domain experts? Yes! Appraising AI quality requires judgement. And
judgement requires domain expertise.</p>
<h2 id="cicd-is-dead-agents-need-continuous-compute-and-computers--hugo-santos-and-madison-faulkner">CI/CD Is Dead, Agents Need Continuous Compute and Computers &mdash; Hugo Santos and Madison Faulkner</h2>
<p>Agentic software is breaking traditional CI/CD. Why is CI/CD dead? At agent
scale you have more PRs and more repos. But it still takes the same time to
review and verify. Merging all different versions together becomes impossible.</p>
<p>Machine latency in the CI/CD pipeline was hidden behind the &lsquo;slow&rsquo; humans. With
agents the pain points become clear.</p>
<p>Today we have the human in the loop: we validate the PR. So each time a PR is
rejected the agentic software can update the PR fairly quickly only to be
blocked again by the human. But since this means there are only so many changes
going on, we have a relatively big window to get your stuff merged.</p>
<p>The PR as a unit of work was designed for humans. CI matters because it
validates your work. It also facilitates coordination.</p>
<p>Hugo explains a loop we have today: intent + plan (what are you doing) goes into
an agent harness loop. The agent will check out your code. Then goes to internal
validation to make sure the change is correct. It then reports back to the human for
external validation (does it look good?). When done, go to merge queue. This is fast,
but not fast enough because there is a human in the loop.</p>
<figure><img src="/images/ai_engineer_2026_hugo_santos_and_madison_faulkner.jpg"
    alt="CI/CD human out of the loop"><figcaption>
      <p>Hugo Santos showing the human out of the loop setup of tomorrow</p>
    </figcaption>
</figure>

<p>Tomorrow the human will be out of the loop. We&rsquo;ll have all kinds of LLMs (e.g.
one with a security focus, another one with a different focus, etc) to do the
external validation. The human only needs to approve a change once it&rsquo;s in the
pre merge queue. So the human is still gatekeeping, but later in the process.</p>
<h2 id="software-engineering-is-becoming-plan-and-review--louis-knight-webb">Software Engineering Is Becoming Plan and Review &mdash; Louis Knight-Webb</h2>
<p>What are we even going to do in the new AI era?</p>
<p>Work humans do:</p>
<ul>
<li>Plan</li>
<li>Write code</li>
<li>Review my code</li>
<li>Review other people&rsquo;s code</li>
</ul>
<p>If we look at 2021, most time was spent writing code and reviewing other people&rsquo;s code. If we look at
2025, there is little actual coding; most time is spent reviewing other people&rsquo;s code.</p>
<p><img src="/images/ai_engineer_2026_louis_knight-webb.jpg" alt="Louis Knight-Webb about how he spent his time"></p>
<p>So work got displaced. The time we no longer spend on coding (because the AI is
doing that now) mostly changed into planning and reviewing code.</p>
<p>There are basically two modes to work in:</p>
<ul>
<li>Spend a lot of time planning (create a comprehensive plan doc, interrogation, etc). You
spend more time planning, but the coding agent can run longer and there&rsquo;s less
time needed for review.</li>
<li>Spend a lot of time reviewing. Loosely define prompts, at the cost of more
manual QA work. If you spend less time planning, the coding agent yields
results faster, but there&rsquo;s more back and forth with agent delivering half
baked work.</li>
</ul>
<p>The mode you&rsquo;ll want to use depends on the type of work. Plan heavy mode works
well for refactoring/migration. For new features heavy planning works for
backend tasks, but for the frontend the review heavy mode is a better match.</p>
<p>Spending 5 mins of planning saves you 30 minutes of time reviewing code.</p>
<p>The time an agent can run before human intervention is needed has increased over
time. This is good: you want to minimize the time you are spending with the AI.
Most of the time the back and forth is done by the agent itself.</p>
<p>When the agent takes longer than 5 minutes, waiting on the AI (and slacking off)
is not realistic. So when agents run longer and longer, you need to change your
way of working. One option is parallelism (have multiple agents working at the
same time) <a href="https://www.vibekanban.com/">Vibe Kanban</a> is a tool to help you with
that approach.</p>
<p>What should the future look like to help the human?</p>
<ul>
<li>Focusmaxxing: embrace the fact that you cannot context switch every 30
seconds, instead build to get the most out of the humans</li>
<li>Write tasks</li>
<li>QA (websites, APIs)</li>
<li>Code review</li>
<li>Shepherd the change until it&rsquo;s deployed</li>
</ul>
<p><em>Then the talk took another turn.</em></p>
<p>Recently Louis decided to shut down the company behind Vibe Kanban. On stage he
instructed his agent to write the <a href="https://vibekanban.com/blog/shutdown">Goodbye bloop</a>
blog post (with a prompt he had prepared beforehand). Don&rsquo;t worry though, the
project will continue as open source and be maintained by the community.</p>
<h2 id="how-building-with-ai-can-double-the-throughput-of-your-engineering-team--brian-scanlan">How Building with AI Can Double the Throughput of Your Engineering Team &mdash; Brian Scanlan</h2>
<p>Brian works for Intercom. They are the company behind <a href="https://fin.ai/">Fin</a>.</p>
<p>Change is hard. You need clear and executive guidance. How to enable change:</p>
<ul>
<li>Update job descriptions and expectations</li>
<li>Constantly talk about the urgency of AI adoption</li>
<li>Reward great work (financially, socially and publicly)</li>
<li>Give people room to learn, enable them and give them access to tools</li>
<li>Be very specific about what you want to see and how it is to be done</li>
</ul>
<p>Standardizing on a <strong>single</strong>, skill driven AI platform helps. Prove that it
works, optimize its usage. Connect it to everything. Anything the human can do
on their laptop, the agent should also be able to do. (You&rsquo;ll need to be in
control of your environment to make sure it doesn&rsquo;t do anything bad though!)
Start using the platform for <strong>all</strong> technical work. It will make mistakes
initially, but it will become a flywheel where it will become more powerful
over time.</p>
<figure><img src="/images/ai_engineer_2026_brian_scanlan.jpg"
    alt="Replace technical work with Claude"><figcaption>
      <p>Brian Scanlan illustrating technical work being replaced by AI</p>
    </figcaption>
</figure>

<p>Engineering is changing. The engineers focus their time on writing specs,
validation and improving the agents. The agents write, test and review code.</p>
<p>Internal tools at his company are deprecated in favour of first-class vendor
replacements (like Anthropic/Claude Code).</p>
<blockquote>
<p>Give agents problems, not tasks</p></blockquote>
<p>Agents should figure out the necessary tasks on their own. They focus on
durable, high quality, sharable skills.</p>
<p>Current bottleneck: code review.</p>
<p>Intercom has extensive feedback loops via lots of hooks to <a href="https://www.honeycomb.io/">Honeycomb</a>.
They measure things like skill invocations, failures, etc.</p>
<p>A side-effect of using AI more was that their defect rate is going down. This
wasn&rsquo;t a goal, but a natural consequence.</p>
<p>Relevant link:</p>
<ul>
<li><a href="https://brian.scanlan.ie/">Brian&rsquo;s website</a></li>
</ul>
<h2 id="agents-dont-do-standups-building-the-post-engineer-engineering-org--mike-spitz">Agents Don&rsquo;t Do Standups: Building the Post-Engineer Engineering Org &mdash; Mike Spitz</h2>
<p>From &ldquo;how do we help engineers output more?&rdquo; to &ldquo;how do we make agents faster?&rdquo;</p>
<blockquote>
<p>Scrum did not survive</p></blockquote>
<p>Rituals designed for humans don&rsquo;t work for agents. Ceremonies became huddle
sessions.</p>
<p>Specs are turned into LDDs (lightweight design documents) which become tickets and PRs.</p>
<table>
  <thead>
      <tr>
          <th>Humans</th>
          <th>Agents</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Sprint planning</td>
          <td>Don&rsquo;t need 1hr estimation sessions</td>
      </tr>
      <tr>
          <td>Daily Standup</td>
          <td>Update tickets automatically</td>
      </tr>
      <tr>
          <td>Sprint refinement</td>
          <td>Generate tickets via LDDs &amp; flag issues, make sure tickets don&rsquo;t depend on each other</td>
      </tr>
      <tr>
          <td>Retro</td>
          <td>Metrics replace anecdotes</td>
      </tr>
  </tbody>
</table>
<p>How do you start?</p>
<ol>
<li>Pick the engineers with development and broad system knowledge</li>
<li>Scale slowly!</li>
<li>Experiment in non-critical systems</li>
</ol>
<p>Also keep in mind that:</p>
<blockquote>
<p>Not everyone can drive a sports car</p></blockquote>
<p>It&rsquo;s going to be hard for a few engineers. The curious engineer will smash this.</p>
<p>Some guardrails:</p>
<ol>
<li>Verifiable deterministic tasks (unit tests, e2e tests, linters, PR
prerequisites)</li>
<li>Agentic code review (human steering, agentic review; opinionated comments are
easy to offload)</li>
<li>Tiered human in the loop (heavy human review at system design, light review
at code (except security), heavy review at end for product feel)</li>
</ol>
<p>Prerequisites for autonomous loop:</p>
<ol>
<li>Composable skills (all parts of development are abstracted into composable
skills)</li>
<li>Agent-involved stages (agent involved at <em>every</em> stage: spec, LDD,
ticket/branch/PR creation, self-testing, self-QA)</li>
<li>Self-healing agents</li>
<li>Human multipliers (allow humans to parallelize)</li>
</ol>
<p>What do the humans do?</p>
<ol>
<li>Security (ensure no shortcuts were taken by the AI)</li>
<li>Product feel</li>
<li>Scale &amp; engineering complexity for task (Are we spending tokens on work we don&rsquo;t need to do? Is the agent over-engineering?)</li>
</ol>
<p>The playbook to get started:</p>
<ol>
<li>Start with boring, repetitive tasks</li>
<li>Remove as much redundancy from the process as possible</li>
<li>Make sure the good patterns are turned into skills</li>
<li>Build guardrails before autonomy</li>
<li>Build this with your best engineers</li>
<li><strong>Do not</strong> onboard everyone all at once</li>
<li><strong>Do not</strong> try to create a &ldquo;one size fits all&rdquo; approach</li>
<li><strong>Do not</strong> be conservative, otherwise you&rsquo;ll get behind (compounding effect)</li>
<li><strong>Do not</strong> try to do too much at once, you want a phased approach</li>
</ol>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[AI Engineer Europe 2026: Keynote/Session Day 1]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2026/04/09/ai-engineer-europe-2026-keynote/session-day-1/" type="text/html" />
    <id>https://markvanlent.dev/2026/04/09/ai-engineer-europe-2026-keynote/session-day-1/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="ai" />
    <category term="conference" />
    
    <updated>2026-04-10T20:58:45Z</updated>
    <published>2026-04-09T00:00:00Z</published>
    <content type="html"><![CDATA[<p>Day two of the AI Engineering conference.
<a href="/2026/04/08/ai-engineer-europe-2026-workshop-day/">Yesterday</a> was filled with
longer talks where the speakers could go more in-depth. This day consists of
shorter (usually 20 minute) slots of talks and keynotes.</p>
<p><img src="/images/ai_engineer_2026_stage.jpg" alt="AI Engineer Europe 2026 stage"></p>
<h2 id="the-new-application-layer--malte-ubl">The New Application Layer &mdash; Malte Ubl</h2>
<p>AI is changing how we build and what we build. Is there a place for the software
engineer in the future?</p>
<p>Agents are a new kind of software. They are both the builders and users of
software. They allow us to automate things that were economically unviable in the
past. But with AI, this changes. Lots of software was too expensive to write
(compared to the benefits it would bring). Also, companies are making different
decisions with regard to buying from SaaS companies versus making it themselves.
The cheaper the software is to make, the more software there will be. This
leads to more work for software engineers.</p>
<p>As AI Engineers, our job is to build the next application layer: agents.</p>
<p>Archetypes of practical non-coding agents:</p>
<ul>
<li>Always running (24/7)</li>
<li>Compresses the research (in the chain of business event -&gt; research -&gt; human
decision, we can have the AI do the research)</li>
<li>Surfaces the hidden information (lots of info, but you cannot practically use it)</li>
<li>The boring things (more time for the human for the interesting tasks)</li>
</ul>
<p>We also need to realise that software is for agents now. For example, 60% of the
visitors of the vercel.com page are agents.</p>
<p>Not writing the code yourself also makes us less opinionated about the infra. It
&ldquo;just has to run&rdquo;.</p>
<p>We should also have agentic security infrastructure. We need to have an open
mindset for how to change things.</p>
<p>The new application layer can thrive independently of models. Now model A is the
&ldquo;best&rdquo; today, but tomorrow it&rsquo;s model B. We as engineers should provide a stable
interface.</p>
<p>Europe is the leader in AI Engineering. Not models though. But the model
companies are commoditising. The application layer is where the real innovation
happens.</p>
<h2 id="harness-engineering-how-to-build-software-when-humans-steer-and-agents-execute--ryan-lopopolo">Harness Engineering: How to Build Software When Humans Steer and Agents Execute &mdash; Ryan Lopopolo</h2>
<p>Use the models to do the full job. Lean into the idea that the models are
software engineers.</p>
<blockquote>
<p>Code is free</p></blockquote>
<p>Hiring the &ldquo;hands on the keyboard&rdquo; is constrained by token budgets nowadays. We
need to constructively use this capacity. The human skill sets that are now
needed are delegation and systems design.</p>
<p>The models are good enough. Code is free (to produce, maintain and refactor).
Your role is to unblock your team and your team is infinitely large. Your job is
to make use of that team.</p>
<p>Human time and attention is scarce. How do we effectively use it? When we are no
longer blocked by this, we <em>can</em> work on the low(er) priority issues. We have
infinite coding resources, right? Humans don&rsquo;t need to concern themselves with
implementation, but specs and guardrails. Our job is to build systems and
structures to make our teams effective.</p>
<p>What does it mean to do a good job? Used to be years of experience in the field.
Lots of little decisions in everyday work. But agents have seen more code than
we have seen. We need to write down the non-functional requirements so that the
agents can use this. Figure out what the agents are struggling with. Put
guardrails in place to guide the agents. Move on to higher level tasks.</p>
<p>Having a single QA expert in the team who can write a good QA plan can benefit
the whole team. Same goes for other experts: one engineer has more impact on the
whole team than before.</p>
<p>How can we help the agents to make the correct decisions?</p>
<ul>
<li>Continuously run review agents</li>
<li>Check if the code has a secure interface</li>
<li>&ldquo;Lint&rdquo; for non-functional requirements</li>
<li>Figure out why we are spending time on correcting agents and fix the problem so
you can move on</li>
<li>Adapt your codebase to match the world today, e.g. limit file size so they fit
in the context window</li>
<li>Have good error messages with resolution steps. This helps the agent resolve
issues itself</li>
</ul>
<p>Everything is a prompt: rules, skills, error messages, PR comments, et cetera.</p>
<p>Just build things. Do not hesitate to have the agents do the full job.</p>
<h2 id="why-building-eval-platforms-is-hard--phil-hetzel">Why building eval platforms is hard &mdash; Phil Hetzel</h2>
<p>Evals and observability are related. Evals is what you do before hitting
production, observability is what you do when in production.</p>
<p>Why are evals important?</p>
<ul>
<li>LLMs have extreme variability (we love them for it though)</li>
<li>Agents are becoming the norm, people have come to expect them when
interacting with your company</li>
<li>You need to be confident with the agent&rsquo;s performance</li>
</ul>
<p>Evals are not a hard problem: gather a bunch of example inputs, loop through
them with the agent and publish the result. Right? Actually it&rsquo;s way more
complex. Think about tracing, alerting, online scoring, topic modeling, etc.</p>
<figure><img src="/images/ai_engineer_2026_phil_hetzel_1.jpg"
    alt="The iceberg of evals"><figcaption>
      <p>Phil Hetzel speaking about the eval iceberg hidden under the surface</p>
    </figcaption>
</figure>

<p>Different stages of doing evals:</p>
<ol>
<li>A spreadsheet plus a <code>for</code> loop. It&rsquo;s a great place to start, no barrier to
entry. However, it&rsquo;s more about documenting and not really experimenting, and it is
hard to compare experiments. It&rsquo;s also a cumbersome process.</li>
<li>Vibe coded UI. Nice next step, probably has proper persistence (database).
It&rsquo;s a bit more bespoke to bring others into the fold. But it&rsquo;s still more of
a reporting tool.</li>
<li>Encouraging experimentation. Give a user access to an agent configuration
plus a sandbox. Allow the users to tweak prompts and parameters.
(He demonstrated with Braintrust). Still no access to production traces.</li>
<li>&ldquo;The Flywheel&rdquo;. This is where observability and evals are connecting.
Understanding actual user behaviour. This unlocks the feedback loop.
Downside: you now have to manage the eval platform, at the pace the industry
is moving. More importantly: agent traces are nasty (and not like normal
application traces), very large and numerous.</li>
</ol>
<figure><img src="/images/ai_engineer_2026_phil_hetzel_2.jpg"
    alt="The flywheel"><figcaption>
      <p>Phil Hetzel speaking about the flywheel: observe -&gt; analyze -&gt; evaluate -&gt; improve -&gt; etc</p>
    </figcaption>
</figure>

<p>This is a new problem because of the specifics of traces: larger spans, highly
unstructured, difference in read patterns. And while these aspects individually
are not unique problems, the combination of them <em>is</em> new.</p>
<p>Building the right system: specific for agent traces with multipersona
workflows. This allows you to measure agent quality at scale, delivering near
real time feedback.</p>
<p>Looking forward:</p>
<ul>
<li>Surface unknown-unknowns via topic modelling techniques</li>
<li>Build the platform for users <strong>and</strong> agents</li>
<li>Perform observability via an AI proxy or gateway</li>
</ul>
<h2 id="what-breaks-when-you-build-ai-under-sovereignty-constraints--bilge-yücel">What Breaks When You Build AI Under Sovereignty Constraints &mdash; Bilge Yücel</h2>
<p>Sovereign AI is &ldquo;the ability of an organisation to design, deploy and operate AI
systems on its own terms.&rdquo; In a practical sense: this means having explicit
control over data flow, model choices, infrastructure, observability and
operations.</p>
<p>The pillars of sovereignty:</p>
<dl>
<dt>Data sovereignty</dt>
<dd>Data should be stored in &ldquo;trusted jurisdictions&rdquo;. If you send your data to a
model running in the US, you may already not be compliant anymore.</dd>
<dt>Infrastructure sovereignty</dt>
<dd>Maximal control: airgapped (EU AI act safe). Maximal convenience: SaaS (CLOUD
Act risk)</dd>
<dt>Model sovereignty</dt>
<dd>You do not want to tightly couple with a specific model provider. You want to
be able to swap without architectural changes.</dd>
<dt>Operational sovereignty</dt>
<dd>Monitor how AI systems behave, have the human in the loop, manage versioning
and updates to models and applications.</dd>
</dl>
<p>Sovereignty is a spectrum. Some sectors need more sovereignty (finance,
healthcare); some need less (e.g. startups).</p>
<p>What are the engineering challenges? What do you do and what do you break?</p>
<ul>
<li>Replace the frontier API with a self hosted model. Consequence: translate API
logic</li>
<li>Private data to required jurisdiction. Consequence: managing multiple
databases and instances.</li>
<li>Replace managed infra with on-prem. Consequence: you discover vendor lock-in
and are limited by your hardware.</li>
<li>Incorporate observability and tracing. Consequence: you have to do version
control of the whole system.</li>
</ul>
<p>Haystack solves some of these problems. It has:</p>
<ul>
<li>A consistent interface</li>
<li>Explicit data flow (know what data was where)</li>
<li>Serializable to YAML so easy to version</li>
<li>No black box or hidden assumptions because it is open source</li>
</ul>
<p>(Full disclosure: Haystack is a product from Deepset, the company Bilge works
for.)</p>
<p>Architecture:</p>
<ul>
<li>Guardrails (check input)</li>
<li>Agent (with LLM)</li>
<li>Guardrails (prevent leaking info)</li>
</ul>
<figure><img src="/images/ai_engineer_2026_bilge_yucel.jpg"
    alt="Sovereign architecture"><figcaption>
      <p>Bilge Yücel showing a sovereign architecture</p>
    </figcaption>
</figure>

<p>Sovereignty checklist:</p>
<ul>
<li>Can you swap your models without changing the application logic?</li>
<li>Do you have reproducible run logs stored in a compliant way?</li>
<li>Can your team respond to an incident without needing the help of the
hyperscalers?</li>
</ul>
<p>Related blog post: <a href="https://haystack.deepset.ai/cookbook/safety_moderation_open_lms">AI Guardrails: Content Moderation and Safety with Open Language Models</a></p>
<h2 id="software-engineering--ai----gergely-orosz-and-swyx">Software Engineering + AI = ? &mdash; Gergely Orosz and swyx</h2>
<p>Gergely is the man behind <a href="https://www.pragmaticengineer.com/">The Pragmatic Engineer</a>.</p>
<p><strong>What does Gergely think about
<a href="https://www.newsnationnow.com/business/tech/tokenmaxxing-ai-status-game/">tokenmaxxing</a>?</strong>
It&rsquo;s happening at multiple large companies. Token output is measured and e.g.
shown on a leaderboard. This results in uncertainty: people start to think
performance is also measured by token usage. Token count can be weaponized. The
result: people start burning tokens by, for example, asking the AI instead of
reading the docs just to get the token count up, even if the AI doesn&rsquo;t do a
good job of answering the question. Some companies even have a minimum token
count. It&rsquo;s a weird time to live in.</p>
<p><strong>Is AI still making us faster?</strong> Experienced engineers were holding off,
especially the older models on existing codebases. Targeting and measuring token
usage came from the C-suite. They had the idea that if their company was not
using AI tools they are probably not doing well. There&rsquo;s a push to use AI.</p>
<p>Why are engineers putting up with that? For the same reason as we have weird
leetcode interviews: they select for smart people willing to put up with a
bullshit process to get the job. Big tech is a bit weird.</p>
<p>Individually, AI makes us go faster. For teams? Not always. It is hard to
retrofit in some situations. An engineer is perhaps not much more productive.
But enabling non-coding colleagues with AI unlocks the ability for them to not
have to wait for dev but get things done themselves.</p>
<p>These times are hard to understand for us engineers:</p>
<ul>
<li>Using AI takes a long time to get good at it</li>
<li>Knowing the theory behind it and how it works will not make you more productive</li>
</ul>
<p><strong>How is the role of a software engineer changing?</strong> Before AI the role was
already changing. AI just sped it up. Startups were already moving fast with
smaller teams. More roles collapse into the role of a software engineer (think
of DevOps, QA). Now also product management. Software engineers need to know
about the business, and take more responsibility.</p>
<p>&ldquo;Everyone is an engineering manager now&rdquo; (no longer a software engineer). This
is absolute nonsense. Traditional managers take care of people problems, their
growth path, etc. But with AI you don&rsquo;t have to worry about a person. It&rsquo;s more
a tech lead role. You can do more and faster, but are still in control. It&rsquo;s only
orchestration, not management. With agents you have a faster feedback loop than
with humans.</p>
<p>E.g. Uber is not building more features into their core product. Instead they
are working on infra and people are building more. It&rsquo;s a low risk way to be
hands on with AI and learn. But also the codebase is too big for the context
window, building separate things is easier. Plus: anything related to AI gets
funded faster.</p>
<p>Taking AI infra seriously is not well understood. We&rsquo;re learning from each
other.</p>
<h2 id="keynote--kitze">Keynote &mdash; Kitze</h2>
<p>This talk was fast paced and very enjoyable. I can recommend watching it.
(See the <a href="https://www.youtube.com/live/O_IMsEg91g8?si=Ug5UK3Hh5ELZKz37&amp;t=29326">AIE Europe Day 1: Keynotes &hellip;</a> starting at 8:08:46)</p>
<p><img src="/images/ai_engineer_2026_kitze.jpg" alt="Photo of Kitze speaking"></p>
<h2 id="it-aint-broke-why-software-fundamentals-matter-more-than-ever--matt-pocock">It Ain&rsquo;t Broke: Why Software Fundamentals Matter More Than Ever &mdash; Matt Pocock</h2>
<p>With the new paradigm (AI), we should probably chuck out the old rules to make
room for new ones, right? There&rsquo;s a &ldquo;specs to code&rdquo; movement where you
write the spec and have the AI do the coding. You don&rsquo;t look at the code. If
there is a problem with the code, you go back to the spec and try again. This is
driven by the idea that code is cheap.</p>
<p>But ignoring the code doesn&rsquo;t work. A bad codebase is one that is hard to
change. <a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/">The Pragmatic
Programmer</a>
has a chapter on software entropy. It basically means that if you do not pay
enough attention to the design of the whole system, the codebase will become
worse and worse. And this was exactly what he was seeing when he was running the
compiler again and again when using specs to code.</p>
<p>Bad code is the most expensive it has ever been. Good codebases matter more than
ever.</p>
<blockquote>
<p>Software fundamentals matter more than ever</p></blockquote>
<p>Common failure modes and how to avoid them by going back to old software
practices:</p>
<dl>
<dt>The AI didn&rsquo;t do what I wanted</dt>
<dd>No-one knows exactly what they want. There is a communication barrier between
you and the AI. <a href="https://www.amazon.com/Design-Essays-Computer-Scientist/dp/0201362988">The Design Of Design</a>
speaks about &ldquo;the design concept&rdquo;: the idea of what you are building. You and
the AI don&rsquo;t share a design concept. Hence the <code>/grill-me</code> skill. Works
towards a shared understanding. Use his other skills to generate a Product
Requirements Document or issues. (See <a href="https://github.com/mattpocock/skills">Matt&rsquo;s skills repository</a>
for the skills mentioned in this talk.)</dd>
<dt>The AI is way too verbose</dt>
<dd>You also see that in the interaction between a developer and a domain expert.
You need to establish shared language.
<a href="https://en.wikipedia.org/wiki/Domain-driven_design">Domain Driven Design</a>
also needs a ubiquitous language. In our case this is essentially a Markdown
file with concepts and what they mean. Use the <code>/ubiquitous-language</code> skill.
This generates a file (<code>UBIQUITOUS_LANGUAGE.md</code>) with tables with terminology.</dd>
<dt>Code that doesn&rsquo;t work</dt>
<dd>Use feedback loops: static types, browser access to look around, automated tests.</dd>
<dt>Doing way too much</dt>
<dd>The Pragmatic Programmer calls this &ldquo;outrunning your headlights&rdquo;. The rate of
feedback is your speed limit. Related skill <code>/tdd</code>: write test, make test
pass. However, testing is hard (how big of a unit do you want to test, what to
mock, what behaviour to test, etc). Good codebases are easy to test. (Because
of better feedback loops and more clear boundaries.) Use deep modules: few
modules with lots of functionality but simple interfaces.</dd>
</dl>
<figure><img src="/images/ai_engineer_2026_matt_pocock_3.jpg"
    alt="Deep modules vs Shallow modules"><figcaption>
      <p>Matt Pocock about deep vs shallow modules</p>
    </figcaption>
</figure>

<dl>
<dt>AI does not understand my code</dt>
<dd>Again, use deep modules. Easier for the AI to understand the design. How to go
from shallow to deep modules? Use the <code>/improve-codebase-architecture</code> skill.</dd>
<dt>My brain hurts</dt>
<dd>The human cannot keep up. Again: deep modules make it simpler for you to
understand the codebase. You can treat these modules as grey boxes. The AI
can handle what&rsquo;s in the blob. Design the interface but delegate the
implementation.</dd>
</dl>

  <figure>

<blockquote >
Invest in the design of the system every day
</blockquote>

  <figcaption>
    &mdash;Kent Beck, <cite>Extreme Programming Explained</cite>
  </figcaption>
  </figure>


<p>Code is <strong>not</strong> cheap, it is important. If we think of AI as a programmer, you
need someone above that, thinking on the strategic level. And that&rsquo;s you! And
that&rsquo;s why fundamental software development skills are still important.</p>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[AI Engineer Europe 2026: Workshop Day]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2026/04/08/ai-engineer-europe-2026-workshop-day/" type="text/html" />
    <id>https://markvanlent.dev/2026/04/08/ai-engineer-europe-2026-workshop-day/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="ai" />
    <category term="conference" />
    
    <updated>2026-04-09T20:32:35Z</updated>
    <published>2026-04-08T00:00:00Z</published>
    <content type="html"><![CDATA[<p><a href="https://www.ai.engineer/europe">AI Engineer Europe</a> is &ldquo;Europe&rsquo;s first flagship
AI Engineer event&rdquo;. I was fortunate enough to be one of the engineers that
<a href="https://schubergphilis.com/">Schuberg Philis</a> (my employer) sent to this
conference. The first day was workshop day.</p>
<p><img src="/images/ai_engineer_2026_entrance.jpg" alt="AI Engineer Europe 2026 was held at the Queen Elizabeth II Centre in London"></p>
<h2 id="how-to-build-agents-that-run-for-hours-without-losing-the-plot--ash-prabaker-and-andrew-wilson">How to Build Agents That Run for Hours (Without Losing the Plot) &mdash; Ash Prabaker and Andrew Wilson</h2>
<p>Why are agents losing the plot?</p>
<ul>
<li>Context: the agent can&rsquo;t carry state (and has &ldquo;context anxiety&rdquo; if the context
is getting filled up)</li>
<li>Planning: general models are not great at planning (e.g. running out of context)</li>
<li>Verification: models are bad at evaluating their own output (it thinks it&rsquo;s done)</li>
</ul>
<p>There are two ways to fix this:</p>
<ul>
<li>Train the model</li>
<li>Wrap the model in a harness</li>
</ul>
<p>Anthropic&rsquo;s new models were also combined with harness improvements last year.
Every release of Claude could run longer unattended.</p>
<p>Harness design for long-running agents. Building the generator/evaluation
loop, and then deleting half of it when the model caught up. Splitting up the
generator (which builds the thing) from the evaluator (which grades the thing).
Most people now use the same instance to build and evaluate.</p>
<blockquote>
<p>Tuning a standalone evaluator to be skeptical is tractable. Making a generator
self-critical is not.</p></blockquote>
<p>Added one more role: plan (a 1-line prompt to full spec). This is the input for
the generator. If you squint a bit, you&rsquo;ll notice that this mimics the real
world where we have a product manager (the plan role), an individual contributor
(the generator) and a QA person (the evaluator).</p>
<p>Before any code gets written, the generator and evaluator negotiate what &ldquo;done&rdquo;
looks like for this chunk. They iterate via files until they agree (one agent
writes, the other reads and responds). The agents agree on a contract. This
bridges user stories to testable behaviour.</p>
<p>Ash presented an example where a solo agent built a game vs building a game
with a full harness. The solo agent was done in 20 minutes, and the full harness
took 6 hours. But the result was significantly better and more
thought through.</p>
<p>Out of the box, Claude is a poor QA agent. It would find a bug and then decide
itself that it wasn&rsquo;t a big deal and approve the work anyway.</p>
<p>With Opus 4.6 half of what was described about harnesses became obsolete. The
new model needs less scaffolding and the harness can be less complex.</p>
<figure><img src="/images/ai_engineer_2026_anthropic_harness_design.jpg"
    alt="human -&gt; planner -&gt; generator -&gt; evaluator"><figcaption>
      <p>The simplified harness where the human prompts the planner, the planner writes the spec and the generator and evaluator work on the application</p>
    </figcaption>
</figure>

<p>Takeaways:</p>
<ol>
<li>Use an adversarial evaluator, self-evaluation is a trap</li>
<li>Structured handoffs are better than compaction</li>
<li>Make subjective quality gradable with rubrics the model can apply</li>
<li>Read the traces as they are your primary debugging loop</li>
<li>Delete scaffolding where the model catches up</li>
</ol>
<p>The models you are using (e.g. Opus for planning and Sonnet for building)
influence the harness. The harness patterns that work tend to get absorbed back
into the tools. Most of the loop described is buildable in Claude Code right now
with primitives that are already available.</p>
<p>Further reading on the Anthropic Engineering blog:</p>
<ul>
<li><a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents">Effective harnesses for long-running agents</a></li>
<li><a href="https://www.anthropic.com/engineering/harness-design-long-running-apps">Harness design for long-running application development</a></li>
</ul>
<h2 id="building-your-own-software-factory--eric-zakariasson">Building Your Own Software Factory &mdash; Eric Zakariasson</h2>
<p>Eric spoke about his experience using Cursor to build a software factory.
Some parts are running autonomously. It&rsquo;s hard work. These are his observations.</p>
<p>There are several levels of using AI. At the lowest level you have autocomplete.
Then you progress to a coding intern, a junior dev, a developer (where the
majority of code is written by the AI tool) to a senior developer and finally a
software factory where the AI is a black box and operates like a
<a href="https://en.wikipedia.org/wiki/Lights_out_(manufacturing)">dark factory</a>.</p>
<p>Why would you want a factory:</p>
<ul>
<li>Throughput (24/7, machines do not need sleep)</li>
<li>Consistent output (like an actual factory, but with AI there is a risk of
losing determinism)</li>
<li>Leverage your taste better</li>
</ul>
<p>What do you need to build a factory?</p>
<ul>
<li>Primitives &amp; patterns (co-located code and usage patterns)</li>
<li>Guardrails (you want to let the agents free, but not <em>too</em> free, think about
rules, hooks and tests)</li>
<li>Enablers (what can you allow the agents to do to be more free, think about
skills, MCPs)</li>
</ul>
<figure><img src="/images/ai_engineer_2026_eric_zakariasson.jpg"
    alt="Building the factory"><figcaption>
      <p>Eric Zakariasson speaking about what you need to build a software factory</p>
    </figcaption>
</figure>

<p>Rules should be created dynamically: if you see an agent doing something you
don&rsquo;t like, write a rule. Note that agents will behave better over time with
newer models, so rules may become obsolete.</p>
<p>Other aspects you&rsquo;ll need:</p>
<ul>
<li>Runnable: can the agent start the env?</li>
<li>Accessible: can the agent use the tools it needs, e.g. Datadog?</li>
<li>Verifiable: can the agent perform check itself?</li>
</ul>
<p>For each of the different stages of the software development lifecycle you will
need an agent. For example to review changes and automated testing.</p>
<p>You need to shift your way of working:</p>
<ul>
<li>You&rsquo;ll write less code yourself (if any) but are managing your agents</li>
<li>You are also going from sync to async</li>
<li>You need to think more about scope and parallelising work (e.g. running some
tasks in parallel will guarantee merge conflicts while other tasks do not
interfere with each other).</li>
<li>You still need to know how data flows and what users want</li>
<li>You&rsquo;ll want to identify the human in the loop. Is there e.g. a copy/paste
action the user is doing now? Automate that away.</li>
</ul>
<p>Since agents will run for longer periods, you need to trust them more. You get
to know the agents: their weaknesses and their strengths and how to prompt them.</p>
<p>When having agents work in parallel, Eric is using separate environments (even
in different VMs) to have reproducible and isolated environments without side
effects from other branches and ongoing work. This will take more effort to
setup, but once you are there, it is easier to scale up the number of agents
working on your code.</p>
<p>You need to keep an eye out for where the agents go off the rails. Use this
information to improve the factory.</p>
<p>Now how do you go from 5 agents to 100? Same as before: observe the outcome.
And:</p>
<ul>
<li>Make sure the agents can verify their own work</li>
<li>Setup automations. Examples:
<ul>
<li>Eric demonstrated asking the agent what actions he does frequently so he can work on automating those</li>
<li>Review the comments made on PR reviews so the agents can learn from them</li>
</ul>
</li>
<li>Move up abstractions</li>
</ul>
<p>The takeaways:</p>
<ul>
<li>Be clear about your intent: what problem are you solving?</li>
<li>Stay in the loop for important decisions (e.g. which payment system to use,
etc)</li>
<li>Build systems and tools: codify them and give your agents access to them</li>
<li>Store context for later and keep it up-to-date (since it will evolve)</li>
<li>Let the agents be free (one team had even given the agents a place to complain
and that proved to be very useful)</li>
</ul>
<h2 id="build-your-own-deep-research-agent--technical-writer--louis-françois-bouchard-paul-iusztin-and-samridhi-vaid">Build Your Own Deep Research Agent + Technical Writer &mdash; Louis-François Bouchard, Paul Iusztin and Samridhi Vaid</h2>
<p>The team built a multi-agent pipeline to replace the research and technical writing
process. They give a topic and it will write a technical article (without slop
or hallucinations). It targets short content, like LinkedIn posts.</p>
<p>The GitHub repo of their project:
<a href="https://github.com/iusztinpaul/designing-real-world-ai-agents-workshop">iusztinpaul/designing-real-world-ai-agents-workshop</a></p>
<p>Constraints:</p>
<ul>
<li>Costs per task</li>
<li>Latency</li>
<li>Quality</li>
<li>Compliance &amp; data privacy</li>
</ul>
<p>There&rsquo;s a scale from simple prompts (where you have more control and less costs)
via workflows to single agent to multiple agents (where you have more autonomy,
and thus less control, and higher costs). It&rsquo;s best to always use the most
simple solution. For example: if the context is known at or before query time
and has less than 200K tokens, a simple prompt can suffice, using
<a href="https://www.geeksforgeeks.org/artificial-intelligence/context-augmented-generation-cag/">Context Augmented Generation (CAG)</a>.</p>
<p>In a situation where the context is not known beforehand (e.g. because it is
private or too recent), you might benefit from including a workflow. (A
workflow is a sequence of fixed steps, with the same steps in the same order
each time). Think about using
<a href="https://www.geeksforgeeks.org/nlp/what-is-retrieval-augmented-generation-rag/">Retrieval-Augmented Generation (RAG)</a>.</p>
<p>The next step is when you need the system to take autonomous actions or you need
dynamic behaviour. Then you get to agents, which can react to what is happening.
These agents can use also tools, which can have their own:</p>
<ul>
<li>System prompt</li>
<li>Validation logic</li>
<li>LLMs</li>
</ul>
<p>Tools are specialists, but with one shared decision maker which has a global
context. Delegation to tools helps with context management. The tools can have
their own context windows.</p>
<p>AI products are never just agents, simple workflows or LLM calls and tools.
They combine all of them. AI engineers need to understand how to build these
complex systems. And deep research systems are a perfect project on how to learn
these complex, multi-agent systems.</p>
<p>The MCP server they built (see <a href="https://github.com/iusztinpaul/designing-real-world-ai-agents-workshop">their GitHub repo</a>):</p>
<ul>
<li>Tools: actions the agent can do</li>
<li>Prompts: instructions the agent can follow</li>
<li>Resource: data the agent can read</li>
</ul>
<p>Why both skills and MCP? They are moving to using skills more, but those cannot
replace the MCP server completely because some tools are too complex to be
turned into skills.</p>
<p>LinkedIn post generation:</p>
<ul>
<li>Guidelines: what to write about (topic, angle, etc)</li>
<li>Profiles: how to write (structure, terminology, character of the post)</li>
<li>Research</li>
</ul>
<p>Debugging workflows/agents purely through logs is hard. You want traces
(LLM/tool calls with full I/O + metadata), latency and cost tracking. They used
<a href="https://www.comet.com/docs/opik/">Opik</a> for observability.</p>
<p>You also want to automate evals. Generating one post allows for manual review,
but when scaling to 100 posts, it&rsquo;s quite impossible to manually review each one.
One small change could break something completely, so you need to review.</p>
<p>They used a three layer architecture:</p>
<ul>
<li>Optimization</li>
<li>Regression testing (evals in CI/CD)</li>
<li>Production monitoring (using Opik)</li>
</ul>
<p>They encourage us to run their project ourselves and reading the code to
understand the details of what&rsquo;s going on.</p>
<h2 id="ai-coding-for-real-engineers--matt-pocock">AI Coding For Real Engineers &mdash; Matt Pocock</h2>
<p>To follow the workshop along, see Matt&rsquo;s workshop at
<a href="https://aihero.dev/s/ai-2026">aihero.dev/s/ai-2026</a>.</p>
<p><em>Before I begin with my notes: this session is definitely worth watching (again)
once it&rsquo;s available on YouTube! Matt is an excellent teacher, has great energy
and great content as well.</em></p>
<p>Once there are about 100K tokens in the context, the AI starts to get dumber and
making increasingly dumb decisions. We don&rsquo;t want the AI to bite off more than
it can chew, so keep your tasks small.</p>
<figure><img src="/images/ai_engineer_2026_matt_pocock_1.jpg"
    alt="Smart zone / dumb zone"><figcaption>
      <p>Matt Pocock explaining about the smart zone / dumb zone</p>
    </figcaption>
</figure>

<p>Even with the 1M context window of Claude the &ldquo;smart zone&rdquo; is still around 100K.
Claude basically just expanded the dumb zone. Good for retrieval, less good for
coding.</p>
<p>So how <em>do</em> you tackle big tasks? Multi-phase plans are a common solution. It&rsquo;s
basically a loop. This is where the
<a href="https://ralph-wiggum.ai/">Ralph Wiggum loop</a> comes from. Matt likes something
smarter though.</p>
<p>Every session starts with a system prompt. If you have 200K tokens in here
already, you are in the &ldquo;dumb zone&rdquo; from the start. To stay in the &ldquo;smart zone&rdquo;
you can clear the context. This does mean that you lose everything that
happened after the system prompt. Alternatively, you can compact.</p>
<p>If you show the number of tokens in the status line of Claude Code (or whatever
tool you are using), you know how close to the &ldquo;dumb zone&rdquo; you are. When using
<code>/compact</code> it squeezes all information. The downside of using <code>/compact</code> over
<code>/clear</code>: with the latter you have a deterministic state.</p>
<p>The <code>/grill-me</code> skill (<a href="https://github.com/mattpocock/skills/tree/main/grill-me">source</a>)
is a really nice way of taking inputs from the world. It can 

<cite>interview
the user relentlessly about a plan or design until reaching shared
understanding</cite> (according to the skill itself). Ideally you use
the <code>/grill-me</code> skill with both the developer and the domain expert in the room.</p>
<p>After the <code>/grill-me</code> skill, you want to summarize all those valuable tokens
into a Product Requirements Document (PRD). This is the definition of done for
your agent. You can use the <code>/write-a-prd</code> skill
(<a href="https://github.com/mattpocock/skills/tree/main/write-a-prd">source</a>)
to write this document. Note that there are testing decisions in there too.
These are important!</p>
<p>Matt explains that he does not actually read the PRDs that are generated. In the
grilling sessions he makes sure he and the AI are on the same wavelength so
there&rsquo;s no need to review the resulting document. Why would he? Doing so would
basically only test the LLM&rsquo;s ability to summarize.</p>
<p>Should he optimize the plan? Matt doesn&rsquo;t think optimizing the plan to death
adds a lot of value. Things will change afterwards anyway.</p>
<p>To see examples of a PRD and issues generated from it, check his
<a href="https://github.com/mattpocock/course-video-manager">course-video-manager</a>
repository, in particular the closed issues.</p>
<p>Now that we have our destination, how do we split it? Matt likes creating a
<a href="https://en.wikipedia.org/wiki/Kanban_board">Kanban board</a> out of it.
He created a skill to do this: <code>/prd-to-issues</code>
(<a href="https://github.com/mattpocock/skills/tree/main/prd-to-issues">source</a>).</p>
<p>As you&rsquo;ll see in that skill, Matt instructs the AI to use vertical slices. LLMs
love to code horizontally, so per layer (database, business logic, frontend).
This means you don&rsquo;t get feedback on your work until all layers are done. If
you slice vertical layers, you can test the entire flow sooner. (Also known
as &ldquo;tracer bullets&rdquo; if you&rsquo;ve read
<a href="https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/">The Pragmatic Programmer</a>.)</p>
<p>With the Kanban board setup, it&rsquo;s easier to parallelize working on tasks. And
once we have the issues that can be worked on, the human can step out of the
loop.</p>
<p>For implementation use the <code>/tdd</code> skill
(<a href="https://github.com/mattpocock/skills/tree/main/tdd">source</a>). It does red/green
refactors: write failing test first and then make it succeed. This not only adds
(good) tests to the codebase, but starting with the tests it&rsquo;s harder to &lsquo;cheat&rsquo;
with writing the tests (you cannot write the tests to match the implementation
because the latter does not exist yet).</p>
<p>How do you conform with existing architecture, coding standards, API design,
constraints, etc?</p>
<ul>
<li>Push instructions to the LLM (e.g. in <code>CLAUDE.md</code>)</li>
<li>Pull: give the agent an opportunity to collect info, e.g. via skills.</li>
</ul>
<p>For the implementer you should use the pull strategy so it can pull what it
needs. For the reviewer use push (these are our standards, make sure they are
adhered to).</p>
<p>You absolutely need to have feedback loops for the AI. The quality of
your feedback loops directly affect the quality of the output.</p>
<p>As you may have noticed, there are two types of work when building something:</p>
<ul>
<li>Human in the loop (HITL) tasks (like planning) which <strong>need</strong> the human</li>
<li>Away from keyboard (AFK) tasks (like implementation) where the AI can work
autonomously</li>
</ul>
<p>To recap the process thus far: you have an idea, you have the grilling session
with the AI and this results in a PRD, which gets turned into issues on a Kanban
board. These are HITL steps. Now the AI can take over and handle implementation
where one or more agents work (the night shift so to speak). Once the AI is done,
the human steps back in the loop for the QA/review step.</p>
<figure><img src="/images/ai_engineer_2026_matt_pocock_2.jpg"
    alt="Smart zone / dumb zone"><figcaption>
      <p>Matt Pocock discussing the phases in the whole process from idea to finished implementation</p>
    </figcaption>
</figure>

<p>And yes, we do need code review. There is no way to avoid this. If we delegate
coding to the agent in small PRs, we have to review more code. Matt doesn&rsquo;t feel
good saying that, but it&rsquo;s his honest answer. The QA step is also where you can
impose your opinions on the agent. Note that in the QA phase we also create more
issues on the Kanban board.</p>
<p>You <em>can</em> and <em>should</em> have an automated review step though. Only QA manually
afterwards. But be careful that the automated review isn&rsquo;t done in the &ldquo;dumb
zone&rdquo;, you want to review in the smart zone.</p>
<p>Frontend in particular is tricky. It needs human eyes. AI is not very good at
that yet. But you <em>can</em> ask it to create a couple of prototypes to trigger
a feedback loop with the agent.</p>
<p>So how does this work in a team? You involve the team in all HITL steps. And
while the idea, research, prototype steps look linear, in the messy world you&rsquo;ll
bounce back and forth between those phases.</p>
<p>What if you have a bad, complicated codebase that even humans do a bad job in?
How do you improve that? If your files are &ldquo;shallow modules&rdquo; (small files with
little functionality), it&rsquo;s hard for AI to navigate. It has to manually track
through the repo. Also hard to draw test boundaries. Hard to test interaction
between modules. Should the tests mock other modules?</p>
<p>Building a codebase that is easy to test is essential, because the feedback loop
is better. &ldquo;Deep modules&rdquo; are better; these are modules with more functionality.
Dependencies are more clear. So how do you go from a bad codebase to a good one?
How to group modules? The <code>/improve-codebase-architecture</code> skill
(<a href="https://github.com/mattpocock/skills/tree/main/improve-codebase-architecture">source</a>)
will find places to deepen the modules.</p>
<p>(The concept of shallow/deep modules come from the book
<a href="https://www.amazon.com/Philosophy-Software-Design-John-Ousterhout/dp/1732102201">A Philosophy of Software Design</a> discusses dependencies.)</p>
<p>If you take only one thing away from today: use the <code>/improve-codebase-architecture</code> skill</p>
<div class="note ">
  <div class="note_header">
    <span class="hidden">:</span>
  </div>
  <div class="note_body">
    Matt was brilliant at switching between telling Claude what to do next,
presenting and answering questions. I&rsquo;ve tried to make the above a bit of a
logical story. What remains are more random notes resulting from questions from
the audience.
  </div>
</div>

<p>You need to have (enough) control over the thing to be able to fix it. The PRD
contains which modules are updated, it also helps you keep in control of the
beast. Because we delegate more, we lose sense of our codebase. By building
deep modules (big shapes), it&rsquo;s easier to have their mental models in your mind.
You don&rsquo;t need to code review all details in a module, you only need to make
sure the shape does what it needs to do.</p>
<p>Code is important, so understanding the tools deeply make you a better developer
and you&rsquo;ll get more out of AI.</p>
<p>When using plan mode, you can tell in <code>CLAUDE.md</code> to be terse (&ldquo;when talking to
me, sacrifice grammar for the sake of concision&rdquo;). This helps when reading the
plans. But Matt dropped this in favour of the grilling session where he and the
LLM came to the same shared understanding and he no longer needed to read the
plans.</p>
<p>Does he keep the markdown plans for future reference? No clear answer. Matt is
wary of outdated documentation (names and requirements have changed). He tends
to get rid of the plans and marking the issues as closed.</p>
<p>What does he think of <a href="https://steve-yegge.medium.com/introducing-beads-a-coding-agent-memory-system-637d7d92514a">Beads</a>
from Steve Yegge? It&rsquo;s another way to manage Kanban boards.</p>
<p>Sidenote: <a href="https://github.com/mattpocock/sandcastle">Sandcastle</a> (also created
by Matt) is an orchestrator. It takes Ralph loop from sequential to parallel.</p>
<p>Matt is not selling a way of working. He does recommend buying old programming
books (pre AI) since they contain a lot of wisdom that is still applicable.</p>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[FOSDEM 2026]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2026/01/31/fosdem-2026/" type="text/html" />
    <id>https://markvanlent.dev/2026/01/31/fosdem-2026/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="ansible" />
    <category term="conference" />
    <category term="docker" />
    <category term="infrastructure as code" />
    <category term="git" />
    <category term="python" />
    <category term="security" />
    
    <updated>2026-02-01T14:54:22Z</updated>
    <published>2026-01-31T00:00:00Z</published>
    <content type="html"><![CDATA[<p>January is already almost over, so time for <a href="https://fosdem.org/2026/">FOSDEM</a>,
the yearly <q>free event for software developers to meet, share ideas and
collaborate</q> in Brussels. <a href="/2025/02/01/fosdem-2025/">Last year</a> I
focussed on the Go track, this year I selected a mix of security and Python
related talks to attend.</p>
<h2 id="streamlining-signed-artifacts-in-container-ecosystems--tonis-tiigi">Streamlining Signed Artifacts in Container Ecosystems &mdash; Tonis Tiigi</h2>
<p>It&rsquo;s possible to sign Docker images, but at the moment most are actually not
signed. Also, users should understand what the signature is protecting and what
it&rsquo;s <em>not</em> protecting. We should not want signing just to tick a box on the
security checlist, but because of the security it adds. And we need something
simple: integrated with existing tools, should not slow down tools.</p>
<p>Buildkit powers &ldquo;<code>docker build</code>&rdquo; but is not limited to Dockerfiles. It&rsquo;s high
performance, can build complex builds and has caching.</p>
<p>A modern build is a graph of images, Git repositories, local files, etc. The
results are images, binaries, archives.</p>
<figure><img src="/images/fosdem2026_tonis_tiigi.jpg"
    alt="Photo of Tonis Tiigi explaining the graph that is modern software building"><figcaption>
      <p>Tonis Tiigi explaining that builds of modern software are a complex graph</p>
    </figcaption>
</figure>

<p>We need Supply-chain Levels for Software Artifacts (SLSA) provenance: what has
actually happened in the build? What was the build config? Et cetera. It&rsquo;s useful to
figure out how an artifact was built.</p>
<p>Buildkit does not sign images by default. GitHub has <a href="https://docs.github.com/en/packages/managing-github-packages-using-github-actions-workflows/publishing-and-installing-a-package-with-github-actions#publishing-a-package-using-an-action">an example in the
documentation</a>
to run a build with Buildkit and generate an artifact. It claims to generate an
<q>unforgeable statement</q>. But if your GitHub credentials are
leaked and the attacker can get your hands on the temporary signing key, they can
use it to sign their own artifacts.</p>
<p>Docker created the <a href="https://github.com/docker/github-builder">github-builder</a>
repository. It contains reusable GitHub Actions to securely build images. If you
use this, your images are signed to prove that they were built from a certain
repository, using the configured build steps. Where Buildkit (among other
things) provides isolation, <code>github-builder</code> provides signing context. It also
protects against build dependency leaks.</p>
<p>So that takes care of the signatures, but how do you verify them?</p>
<ul>
<li>The command &ldquo;<code>docker inspect</code>&rdquo; now shows verified signatures</li>
<li>You can manually verify it with <a href="https://github.com/sigstore/cosign">cosign</a></li>
<li>You can also use sigstore/policy-controller for Kubernetes</li>
</ul>
<p>Buildx also includes experimental Rego (Open Policy Agent) policy support. This
means you can write a matching policy for <code>Dockerfile</code>, e.g. <code>Dockerfile.rego</code>,
which is then automatically loaded. All build sources now need to pass policy
for the build to continue (images, Git repositories, URLs, etc).</p>
<p>You can do very complex stuff in the policies. As simple example Tonis showed:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-rego" data-lang="rego"><span class="line"><span class="cl"><span class="kd">package</span><span class="w"> </span><span class="nx">docker</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">allow</span><span class="w"> </span><span class="kd">if</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nx">input</span><span class="o">.</span><span class="nx">image</span><span class="o">.</span><span class="nx">repo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">&#34;org/app&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nf">docker_github_builder_tag</span><span class="p">(</span><span class="nx">input</span><span class="o">.</span><span class="nx">image</span><span class="o">,</span><span class="w"> </span><span class="s2">&#34;org/app&#34;</span><span class="o">,</span><span class="w"> </span><span class="nx">input</span><span class="o">.</span><span class="nx">image</span><span class="o">.</span><span class="nx">tag</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span></code></pre></div><p>This policy should make sure that the image can only be built from this
repository and that the image tag should match the Git tag.</p>
<p>Summary:</p>
<ul>
<li>No reason not to sign</li>
<li>Not all signatures are equal</li>
<li>Software pulling packages should verify pulled content</li>
</ul>
<p><a href="https://fosdem.org/2026/schedule/event/HJAJTU-streamlining_signed_artifacts_in_container_ecosystems/">Link to the conference page</a></p>
<h2 id="sequoia-git-making-signed-commits-matter--neal-h-walfield">Sequoia git: Making Signed Commits Matter &mdash; Neal H. Walfield</h2>
<p>Version control systems (also known as VCSs) track the following:</p>
<ul>
<li>Changes to the code</li>
<li>Authorship</li>
<li>Other metadata</li>
<li>Commit message</li>
</ul>
<p>But the author can be faked: the metadata is set by the author, including the
author&rsquo;s name. After a quick &ldquo;<code>git config</code>&rdquo; command you can commit as anyone you
want, for example <a href="https://en.wikipedia.org/wiki/Linus_Torvalds">Linus Torvalds</a>.
Sure, GitHub could see that the committer (the one pushing the commit) and
author are different. However, this is not necessarily bad because we might
simply want to give proper attribution to the author of the commit.</p>
<p>And in theory the forge might also be compromised, or someone may have gotten
permission to push to the project.</p>
<p>To prevent impersonations, we can cryptographically prove who the author is by
signing the commits. But now the problem shifts to the certificates. Because
anyone can create a key with any name (again, for example Linus) attached to it.
So what does a signed commit mean now?</p>
<p>How can we be sure that the author is who they say they are? There are ways:</p>
<ul>
<li>You could talk to developer the verify</li>
<li>You could go to <a href="https://en.wikipedia.org/wiki/Key_signing_party">key signing parties</a></li>
<li>You can use a central authority that you trust (e.g.
<a href="https://keys.openpgp.org/">keys.openpgp.org</a>, the Linux developer keyring,
the <code>distributions-gpg-keys</code> package, or, if you trust Github, use
<code>github.com/&lt;username&gt;.gpg</code>)</li>
</ul>
<p>You can use the following command to show the Git log and the signatures on them:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git log --show-signature
</span></span></code></pre></div><p>But now you need to actually check that the signatures are indeed made by the
certificates you trust.</p>
<p>It&rsquo;s up to the maintainers of the software to curate a list of contributors and
track when contributors join and leave (yes, there is a temporal element as
well). This is hard. Maintainer needs tooling. And you would want to detect
unauthorized commits (impersonation, a malicious forge, a machine in the middle
or for instance when project is given to a new maintainer by a forge/registry).</p>
<p>What does the solution look like?</p>
<ul>
<li>Clear semantics</li>
<li>The project itself maintains signing policy</li>
<li>Third party uses maintainers&rsquo; policy to authenticate project</li>
<li>Verification, not attestation: do not rely on any external authority</li>
</ul>
<p>(Note that the maintainers can still be socially engineered to include the key
of an attacker in their policy. So they still have to be careful about who is
added to the policy.)</p>
<p>Sequoia git provides:</p>
<ul>
<li>Specification</li>
<li>Config</li>
<li>Tooling</li>
</ul>
<p>With <a href="https://gitlab.com/sequoia-pgp/sequoia-git">Sequoia git</a> (which part of
the <a href="https://sequoia-pgp.org/">Sequoia PGP project</a>) you can have a signing
policy in an <code>openpgp-policy.toml</code> file in the project&rsquo;s Git repository. It
specifies users, their keys and their capabilities. You can use <code>sg-git</code> to help
maintain this file.</p>
<p>For instance to add user Alice and then describe the current policy, you can use
the following commands:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sq-git policy authorize alice --committer &lt;cert&gt;
</span></span><span class="line"><span class="cl">sq-git policy describe
</span></span></code></pre></div><p>A commit is &ldquo;authenticated&rdquo; if at least one parent commit says the commit is
acceptable (via the policy). To verify that there is an authenticated path from
the current state back to a certain commit we trust, use this command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">sq-git log --trust-root &lt;sha of trusted commit&gt;
</span></span></code></pre></div><p>Projects may have contributions from others that are not included in the policy.
To maintain an authenticated path when accepting the contribution, a trusted
author needs to merge the contribution via a merge commit that <em>is</em>
authenticated. (You may need to use the &ldquo;<code>--no-ff</code>&rdquo; on the merge to make sure
there is a merge commit though.)</p>
<p><a href="https://fosdem.org/2026/schedule/event/KFSUCW-sequoia-git/">Link to the conference page</a></p>
<h2 id="an-endpoint-telemetry-blueprint-for-security-teams--victor-lyuboslavsky">An Endpoint Telemetry Blueprint for Security Teams &mdash; Victor Lyuboslavsky</h2>
<p>With open source we can inspect something that is broken, we can change the
defaults. With security we are used to the opposite; it&rsquo;s a black box. We are
not used to owning the data. The data exists on the endpoints, but ownership is
transferred to a different team. How can we add more security in a way engineers
understand and can use?</p>
<p>Victor presents a blueprint with the following layers:</p>
<ul>
<li>Endpoint agents</li>
<li>Control layer</li>
<li>Ingestion, streaming &amp; storage</li>
<li>Detection</li>
<li>Correlation, intelligence and response</li>
</ul>
<p>The value is not in the layers themselves, but the boundaries. For example, the
ingestion should move the data reliably but should not care which tool collected
it. This makes them loosely coupled.</p>
<p>For endpoint agents Victor suggests
<a href="https://github.com/osquery/osquery">osquery</a> which allows basic questions about
endpoints. Data is structured and consistent. It aligns with open source values.
(Alternatives: scripts &amp; cron, log shippers like filebeat or tools like auditd
or Event Tracing for Windows.)</p>
<p>Controlling the data (the next layer) means that you want to have:</p>
<ul>
<li>Central config</li>
<li>Live queries</li>
<li>Consistent schemas</li>
</ul>
<p><a href="https://github.com/fleetdm/fleet">Fleet</a> (disclaimer: Victor works here) is
built to manage <code>osquery</code> at scale and a good candidate for this layer.</p>
<p>The control layer needs to work hand-in-hand with ingestion layer. The ingestion
layer moves data to downstream system. E.g. <a href="https://github.com/vectordotdev/vector">Vector</a> or
<a href="https://www.elastic.co/logstash">Logstash</a> can be used here.</p>
<blockquote>
<p>Ingestion isn&rsquo;t where you get clever. It&rsquo;s where you get reliable.</p></blockquote>
<p>Streaming decouples users from consumers and e.g. allows replay. Note that this
is an optional step and it would come <em>after</em> ingestion, not <em>in place of</em> it.
For instance <a href="https://kafka.apache.org/">Apache Kafka</a> can be used in this
layer. Ingestion absorbs the mess. Streaming preserves flexibility.</p>
<p>The storage layer is where telemetry becomes durable. It&rsquo;s about being able to
ask hard questions later. Examples of useful tools:
<a href="https://github.com/ClickHouse/ClickHouse">ClickHouse</a>,
<a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> (which is better at text
search) and <a href="https://github.com/apache/iceberg">Iceberg</a> (which is slower for
active investigation).</p>
<p>For the detection layer you might want to use
<a href="https://github.com/SigmaHQ/Sigma">Sigma</a>. It provided portability. Rules are
translated to native SQL running on ClickHouse. Intent (Sigma signatures)
becomes execution (SQL query to get the data).</p>
<p>Finally the correlation layer: <a href="https://github.com/grafana/grafana">Grafana</a>
can be used for correlation and visualisation. Grafana can query ClickHouse.
Grafana also has alerting.</p>
<p>Note that response isn&rsquo;t just about automation. It&rsquo;s also to pause and ask
better questions. The correlation layer should focus on enabling humans to act.</p>
<p>Open endpoint telemetry is <strong>not</strong> an &ldquo;EDR killer&rdquo;. It does not replace it. It adds
diversity and complements other tools. It provides a second set of eyes.</p>
<p><a href="https://fosdem.org/2026/schedule/event/HYXTPH-endpoint-telemetry-blueprint/">Link to the conference page</a></p>
<h2 id="the-bakery-how-pep810-sped-up-my-bread-operations-business--jacob-coffee">The Bakery: How PEP810 sped up my bread operations business &mdash; Jacob Coffee</h2>
<p>Python loads imports eagerly by default. This leads to memory bloat and cold
start issues. Explicit lazy imports (see
<a href="https://peps.python.org/pep-0810/">PEP 810</a>) only import a module when it&rsquo;s
first accessed not when the import statement is executed.</p>
<p>Lazy import is scheduled to be included in Python 3.15 and looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">lazy</span> <span class="kn">import</span> <span class="nn">foo</span> <span class="kn">from</span> <span class="nn">bar</span>
</span></span></code></pre></div><p>The design principles applied are that lazy imports are:</p>
<ul>
<li>Explicit</li>
<li>Local</li>
<li>Granular</li>
</ul>
<p>When parsing the Python code a proxy module is created. Only when the module is
actually used, the proxy is transparently replaced by the real package. You will
not always see improvements, so do not blindly replace all imports with lazy
imports.</p>
<p>PEP 810 also eliminates the need for <code>TYPE_CHECKING</code> guards. (See the <a href="https://docs.python.org/3/library/typing.html#typing.TYPE_CHECKING">typing
docs</a>, in
short: importing a module that is expensive and only contains types used for
type checking in an &ldquo;<code>if TYPE_CHECKING:</code>&rdquo; block.) It also helps for faster test
discovery and collection, less memory usage, decrease cold start slowness in
e.g. AWS Lambda functions, CLI applications, etc.</p>
<p>Meta (with Cinder) saw a 70% startup time reduction and 40% memory savings.
PySide has a 35% startup improvement.</p>
<p>About CLI tools: when using lazy imports you might notice the difference when
using <code>--help</code>. There&rsquo;s no need to load all dependencies to just output the help
text of a tool.</p>
<p>Some notes:</p>
<ul>
<li>Import time side effects (e.g. logging configuration, DB connections) are also
delayed!</li>
<li>Type checkers need to be updated</li>
<li>Import errors move to first use (so in runtime, not at launch). Keep that in
mind when debugging</li>
<li>It&rsquo;s not always faster, so profile your application before migrating and see
where you can potentially benefit</li>
<li>Document your lazy imports!</li>
<li>You cannot do lazy imports in functions</li>
</ul>
<p>Circular imports are probably still a problem, but they just show up later.</p>
<p><a href="https://github.com/JacobCoffee/breadctl">Link to the repo for this talk</a></p>
<p><a href="https://fosdem.org/2026/schedule/event/HAAABD-the_bakery_how_pep810_sped_up_my_bread_operations_business/">Link to the conference page</a></p>
<h2 id="modern-python-monorepo-with-uv-workspaces-prek-and-shared-libraries--jarek-potiuk">Modern Python monorepo with <code>uv</code>, <code>workspaces</code>, <code>prek</code> and shared libraries &mdash; Jarek Potiuk</h2>
<p>Jarek is, besides his other roles, the number 1 Apache Airflow contributor. The
<a href="https://github.com/apache/airflow">Apache Airflow repo</a> is the monorepo he
talks about today. There is also a series of blog posts about this topic: see
<a href="https://medium.com/apache-airflow/modern-python-monorepo-for-apache-airflow-part-1-1fe84863e1e1">part 1</a>,
which links to the other parts.</p>
<p>Airflow drove early requirements for
<a href="https://docs.astral.sh/uv/concepts/projects/workspaces/">uv workspaces</a>. They now
manage 120+ distributions seamlessly with it. It allows them to combine
distributions to work together in a workspace. Also used to import from one
distribution in another one.</p>
<p>The project shares a single virtual environment used by <code>uv</code> in root of project.
If you run &ldquo;<code>uv sync</code>&rdquo; from the top level you get everything. If you run it in a
subdirectory (e.g. <code>airflow-core</code>) you only get what is needed for that
distribution.</p>
<p>Benefits of the <code>uv</code> workspaces:</p>
<ul>
<li>Isolated</li>
<li>Explicit</li>
<li>Flexible</li>
</ul>
<p><a href="https://hatch.pypa.io/1.12/">Hatch</a> has (or will have, at the time of writing)
largely compatible workspaces.</p>
<p>However <a href="https://pre-commit.com/">pre-commit</a> became a bottleneck. They needed
to run 170+ pre commit hooks <strong>on every commit</strong>.
<a href="https://github.com/j178/prek">Prek</a> is drop-in replacement for pre-commit and
works fantastic. It is optimized for speed and monorepos.</p>
<p>Airflow uses symlinked shares libraries (where a shared lib is also a
distribution). The Hatchling build backend needs to replace links with physical
copies during packaging. They use Prek to maintain consistency.</p>
<p><code>uv sync</code> detects conflicts between merged requirements files and Prek hooks
enforce relative imports in shared code to prevent cross coupling issues (IIRC)</p>
<p><a href="https://fosdem.org/2026/schedule/event/WE7NHM-modern-python-monorepo-apache-airflow/">Link to the conference page</a></p>
<h2 id="pyinfra-because-your-infrastructure-deserves-real-code-in-python-not-yaml-soup--loïc-wowi42-tosser">PyInfra: Because Your Infrastructure Deserves Real Code in Python, Not YAML Soup &mdash; Loïc &ldquo;wowi42&rdquo; Tosser</h2>
<p>Loïc is a Frenchmen (which, as he himself states, means he <strong>must</strong> have
opinions) and not a YAML fan to put it mildly. That is: YAML as a programming
language, e.g. how it is used in <a href="https://github.com/ansible/ansible">Ansible</a>.</p>
<figure><img src="/images/fosdem2026_loic_tosser.jpg"
    alt="Photo of Loïc Tosser showing a complex Ansible task in YAML"><figcaption>
      <p>Loïc Tosser demonstrating what happens when you ask a config file to be a programming language</p>
    </figcaption>
</figure>

<p><a href="https://pyinfra.com/">PyInfra</a> is an infrastructure as code library to write
Python code which is then translated to shell scripts to run on the target
hosts. So, in contrast to Ansible, you do not need Python on the target. The
target machine only needs SSH and a POSIX shell. You can also configure Docker
containers with PyInfra.</p>
<blockquote>
<p>If it has SSH, PyInfra can talk to it.</p></blockquote>
<p>PyInfra has idempotent operations and built-in diff checking. Declarative
infrastructure with actual code and not YAML. You can use inventory from
Terraform, Coolify or any API.</p>
<p>You can leverage the entire Python packaging ecosystem. Slack integration? Just
use the right Python package.</p>
<p>PyInfra is not only a CLI tool, you can also use it as a library.</p>
<p>PyInfra is 10 times faster than Ansible, uses 70% less code, has proper code
reuse via <code>import</code> and proper loops instead of <code>with_items</code>. It can have actual
unit tests and can scale to thousands of servers. Also you no longer have error
messages stating that <q>the error appears to be in &hellip; <strong>but may be
elsewhere in the file</strong> &hellip;</q> (looking at you Ansible). PyInfra has
clear error messages without having to specify <code>-vvvv</code> and wading through
hundreds of lines of output.</p>
<p>The suggested migration path:</p>
<ul>
<li>Start small, one playbook at a time</li>
<li>Use your IDE for autocomplete and refactoring</li>
<li>Leverage Python&rsquo;s standard library and the ecosystem with all its packages</li>
<li>Sleep better because you don&rsquo;t have to debug at 3 AM.</li>
</ul>
<p>Is PyInfra production ready? Yes! It has a stable API, is already in use in
production, it&rsquo;s actively maintained and is MIT licensed (so no commercial
entity behind it to steer its direction).</p>
<p>You can get started today with a simple &ldquo;<code>pip install pyinfra</code>&rdquo;.</p>
<p><a href="https://fosdem.org/2026/schedule/event/VEQTLH-infrastructure-as-python/">Link to the conference page</a></p>
<p>(Note from me, Mark, I found Loïc a great speaker: he has lots of energy, is
funny and can transfer his enthusiasm to the room. If the topic interests you
and the video becomes available, I would recommend watching this talk as a great
sales pitch to get started with PyInfra.)</p>
<h2 id="ducks-to-the-rescue---etl-using-python-and-duckdb--marc-andré-lemburg">Ducks to the rescue - ETL using Python and DuckDB &mdash; Marc-André Lemburg</h2>
<p>ETL stands for Extract, Transform, Load. Nowadays we usually do Extract, Load,
Transform because databases are efficient in processing.</p>
<p>DuckDB is open source, in-process analytics data storage (OLAP). It is similar
to SQLite, but for OLAP workloads. It has great Python support and uses SQL as
standard query language. It&rsquo;s pip installable, column based
(<a href="https://arrow.apache.org/">Apache Arrow</a>). It&rsquo;s single writer but allows for
multiple readers, so it&rsquo;s not a distributed database.</p>
<p><a href="https://github.com/pola-rs/polars">Polars</a>&rsquo; streaming can help with processing
your data as a line-by-line stream so you don&rsquo;t have to load the whole file in
memory at once.</p>
<p>Example to load a CSV file into DuckDB extremely fast:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">read_csv</span><span class="p">(...)</span><span class="w">
</span></span></span></code></pre></div><p>You can load the data into staging tables first to prepare everything and not
mess up e.g. existing data. You can then transform data in DuckDB, e.g. filter
out unneeded and duplicate data, validate data, fill in missing data, convert
data types, etc. You can do the transforms in SQL. You can even use native
integrations to write to PostgreSQL, MySQL, etc. Or worst case stream to Python.</p>
<p>Guidelines:</p>
<ul>
<li>Know your queries, that is: know how your data is going to be used</li>
<li>Use the Pareto principle (80/20 rule): optimize for queries that are used
often</li>
<li>Keep a healthy balance between performance and space requirements (which are
often trade-offs)</li>
</ul>
<p>Huge datasets: use the <a href="https://github.com/duckdb/ducklake">DuckLake</a> extension.</p>
<p>To get started: &ldquo;<code>uv add duckdb</code>&rdquo;. Do some experiments and see how it works for
you.</p>
<p><a href="https://fosdem.org/2026/schedule/event/S7RELZ-ducks_to_the_rescue_-_etl_using_python_and_duckdb/">Link to the conference page</a></p>
<h2 id="my-takeaways">My takeaways</h2>
<ul>
<li>Yes, FOSDEM is crowded and you may not be able to get into every talk you want
to see in person, but it&rsquo;s still nice to be there. It&rsquo;s well organised and
there&rsquo;s a friendly atmosphere. Lots of interesting projects to see and people
to talk to. And it&rsquo;s convenient if you want to sponsor your favorite projects
by buying some merchandise.</li>
<li>It&rsquo;s worth investigating signing Docker images (in the right way) further.</li>
<li>Lazy imports look useful! Once Python 3.15 lands it&rsquo;s worth doing profiling on
the projects I work on to see if we can use those to speed things up on
startup and save some memory.</li>
<li>At work we recently decided to go for a monorepo for a project. I want to see
if/how <code>uv</code> workspaces and <code>prek</code> can help us.</li>
<li>I&rsquo;ve written a bunch of Ansible roles to configure my humble homelab and
laptop. Perhaps it&rsquo;s time to switch to PyInfra? It sounds promising and might
be worth the investment of migrating to.</li>
</ul>
<h2 id="about-the-trip">About the trip</h2>
<p><figure class="float-right"><img src="/images/fosdem2026_atomium.jpg"
    alt="Picture of the Atomium at night" width="200px"><figcaption>
      <p>The <a href="https://en.wikipedia.org/wiki/Atomium">Atomium</a> at night</p>
    </figcaption>
</figure>

Last year I drove to Brussels on Friday and stayed at the city center in the
<a href="https://cityboxhotels.com/hotels/brussels/citybox-brussels">Citybox Brussels
hotel</a> for one
night, since I had to be home on Sunday. The upside: it was just a short (15
minute?) tram ride to the FOSDEM location. Unfortunately it did mean I had to
drive home that evening.</p>
<p>This year I had more time, so I booked a room at
<a href="https://www.falkohotel.be/">Falko Hotel</a> for two nights. It&rsquo;s about a 20&ndash;30
minute drive (depending on traffic) to the <a href="https://www.interparking.be/en/parkings/brussels/toison-d-or/">parking
garage</a> I used.
And from there about 20 minutes with pubic transport to the Université libre de
Bruxelles.</p>
<p>Staying another night meant I had more time for sightseeing, had the time to
write this post from my notes and could drive home well rested the next day.</p>
<p>As for tech: besides a phone and laptop, I also brought along two items that
made the trip more comfortable:</p>
<ul>
<li>A <a href="https://mojogear.eu/en/products/mojogear-mini-evo-10-000-mah-power-bank-22-5w">MOJOGEAR Mini
Evo</a>
powerbank to give my phone extra juice to make it through the day. With 10.000
mAh and up to 22.5W of power it&rsquo;s more than sufficient for a day at a
conference. With its small size and less than 175 grams in weight, it&rsquo;s also
easy to carry around.</li>
<li>A <a href="https://www.gl-inet.com/products/gl-sft1200/">GL.iNet Opal (GL-SFT1200)</a>
travel router. I plug it in, hook it up to the hotel internet, start a VPN
connection and all my other devices automatically connect to it and can use
the internet without the hotel snooping on my traffic. (Not that I have an
indication that my hotel would do that, but theoretically they could if I
would not use a VPN.)</li>
</ul>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Proxmox Backup Server]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2025/11/29/proxmox-backup-server/" type="text/html" />
    <id>https://markvanlent.dev/2025/11/29/proxmox-backup-server/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="backups" />
    <category term="proxmox" />
    <category term="tools" />
    
    <updated>2025-11-29T22:47:31Z</updated>
    <published>2025-11-29T00:00:00Z</published>
    <content type="html"><![CDATA[<p>I have been using <a href="https://www.proxmox.com/en/products/proxmox-virtual-environment/overview">Proxmox Virtual
Environment</a>
for a few years now, but I only recently started using <a href="https://www.proxmox.com/en/products/proxmox-backup-server/overview">Proxmox Backup
Server</a>. I
wish I had done that sooner!</p>
<h2 id="backup-via-nfs">Backup via NFS</h2>
<p>When I started using Proxmox to host some services, I configured VM backups
almost immediately. For years, I have used an NFS mount to put the backups on my
NAS. This was a functioning solution and I have also successfully used those
backups to restore machines.</p>
<p>I had scheduled weekly backups early Monday morning and had retention configured
so that exactly one old backup was kept. So if disaster struck on Monday, I
could pick either the backup made earlier that day. But if I needed to restore
something on a Sunday, the most recent backup was one week old.</p>
<p>While this was an acceptable risk for my situation, it was also not really
efficient. The last time I made backups to the NAS (mid-November), this meant a
total of 122 GB in size for 10 VMs and two backups per VM.</p>
<h1 id="proxmox-backup-server">Proxmox Backup Server</h1>
<p>A couple of months ago, in the same period I was working on my
<a href="/2025/09/13/restic-at-the-center-of-my-backups/">Restic backup solution</a>,
I also stumbled upon
<a href="https://www.proxmox.com/en/products/proxmox-backup-server/overview">Proxmox Backup Server</a>
(or PBS for short).</p>
<p>I heard good things about it, so I decided to install it on a separate machine
and give it a try. And I&rsquo;m really happy with it. First of all, I have more
backups, and still use about the same disc space.</p>
<p><img src="/images/pbs_datastore_summary.png" alt="Screenshot of a Proxmox Backup Server datastore summary, showing 23 VMs and containers, with a total of 227 snapshots"></p>
<p>My datastore is currently using about 140 GB of disc space, which admittedly is
about 15% more than the mid-November set of backups on my NAS. But it&rsquo;s not a
fair comparison.</p>
<p>For starters: instead of backing up only the 10 most important
VMs, I am now creating backups for all of them. Currently this means 22 VMs and
1 container as you can see in the screenshot above.</p>
<p>Not only am I backing up more machines, instead of having only two backups, I
now have 6 daily backups, 3 weekly and 2 monthly. Put differently, I can go back
in time further than before and I only lose one day of data at most when I mess
something up now (assuming I notice the problem immediately).</p>
<p><img src="/images/pbs_backup_file_restore.png" alt="Screenshot of Proxmox VE window where one can select the file to restore from a backup"></p>
<p>Something else I like is that I can restore individual files from backups. So
I do not have to restore a whole VM just to get your hands on a single file.
While this was not my main driver to start using PBS, it <em>is</em> a really nice
bonus.</p>
<p>And I haven&rsquo;t even mentioned yet that PBS can <a href="https://pbs.proxmox.com/docs/maintenance.html#verification">verify snapshots</a>
for you. Or that you can have a <a href="https://pbs.proxmox.com/docs/managing-remotes.html">remote PBS with which to sync datastores</a>.</p>
<p>Summarised: with Proxmox Backup Server I now have:</p>
<ul>
<li>backups of more than double the number of VMs (10 → 22)</li>
<li>more backups (2 weekly backups → 6 daily, 3 weekly and 2 monthly)</li>
<li>more features</li>
</ul>
<p>This only costs me about 15% extra disc space in my specific situation. If
that isn&rsquo;t a good deal, I don&rsquo;t know what is.</p>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Restic at the center of my backups]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2025/09/13/restic-at-the-center-of-my-backups/" type="text/html" />
    <id>https://markvanlent.dev/2025/09/13/restic-at-the-center-of-my-backups/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="backups" />
    <category term="restic" />
    <category term="tools" />
    
    <updated>2025-11-27T19:59:36Z</updated>
    <published>2025-09-13T00:00:00Z</published>
    <content type="html"><![CDATA[<p>Some time ago I started using <code>restic</code> for creating backups of my home directory
of my laptop. Now it has become my main solution for backing up my most valued data.</p>
<h2 id="the-old-script">The old script</h2>
<p>In the olden days, when I was still using CD-ROMs (or actually
<a href="https://en.wikipedia.org/wiki/CD-RW">CD-RWs</a>) to store backups, I had three
snapshots (each a set of two CDs). When I created a new backup, I rotated them by
overwriting the oldest disks.</p>
<figure><img src="/images/cd-rw_backups.jpg"
    alt="Photo of my old sets of backup CDs"><figcaption>
      <p>My actual sets of CDs, last used in 2005: red was the most recent backup, yellow one version older and the set of CDs in the green case contains the oldest backup</p>
    </figcaption>
</figure>

<p>Later on, when I was using an external hard disk, I had a script that made
incremental backups using <a href="https://en.wikipedia.org/wiki/Hard_link">hard links</a>
so I could have multiple full backups, but not need <em>x</em> times the space.
Unfortunately I lost that script.</p>
<p>After not creating backups for a while, I wrote a Bash script which handled the
whole backup process:</p>
<ul>
<li>opening the (LUKS encrypted) external drive,</li>
<li>mounting it,</li>
<li>performing the backup, and finally</li>
<li>unmounting and closing the device again.</li>
</ul>
<p>The following line was handling the actual backing up:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">rsync -av --delete --exclude-from<span class="o">=</span>backup.excludes /home <span class="nv">$MOUNT_DIR</span>/<span class="k">$(</span>hostname -s<span class="k">)</span>/
</span></span></code></pre></div><p>Plain and simple.</p>
<p>This protected me from losing my data if my machine got stolen or if the hard
drive failed. And it served me well for a number of years. But on multiple
occasions I would have loved to have an older version of a file that got changed
or even deleted before my last backup. This triggered me to look for another
solution a few years back.</p>
<h2 id="restic-to-the-rescue">Restic to the rescue</h2>
<p>Since I didn&rsquo;t want to reinvent the wheel myself again, I was looking for backup
solutions and ran across <a href="https://restic.net/">restic</a>. After a bit of reading
and experimenting, I basically replaced the <code>rsync</code> line from the previous
script with the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">restic -r <span class="nv">$MOUNT_DIR</span> --verbose backup <span class="s2">&#34;/home/</span><span class="si">${</span><span class="nv">USER</span><span class="si">}</span><span class="s2">&#34;</span> --exclude-file<span class="o">=</span><span class="s2">&#34;/home/</span><span class="si">${</span><span class="nv">USER</span><span class="si">}</span><span class="s2">/backup.excludes&#34;</span>
</span></span></code></pre></div><p>And then I added a few more commands to the script:</p>
<ul>
<li>&ldquo;<code>restic -r $MOUNT_DIR check</code>&rdquo; to check the repository for errors</li>
<li>&ldquo;<code>restic -r $MOUNT_DIR forget --keep-last 7 --prune</code>&rdquo; to only keep the
last 7 snapshots</li>
</ul>
<h3 id="benefits">Benefits</h3>
<p>What did this change bring me?</p>
<p>For starters, if we assume I make a backup each week, I can go back at least 7
weeks in time. In practice I&rsquo;m not that consistent anymore so I can go back even
further in time.</p>
<p>What&rsquo;s more: according to restic each of my backups is about 105&ndash;100 GB of
data. But due to the compression and deduplication these 7 (incremental) backups
only use 69 GB <strong>combined</strong>.</p>
<p>Another benefit is that the backups are encrypted. So if I would want to store
them in the cloud, I can be sure the cloud provider cannot access my data.</p>
<p>And speaking of which: while I use <a href="https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html#local">local storage</a>
(my external disk) and a remote <a href="https://restic.readthedocs.io/en/stable/030_preparing_a_new_repo.html#rest-server">REST Server</a>
(see below), restic supports a bunch of other backends: Amazon S3, Azure blob
storage and Google Cloud Storage to name a few.</p>
<p>A backup is worth nothing if you cannot restore from it. Besides restoring a
full snapshot, with restic I can also mount the snapshots to navigate the files
and view them individualy. This allows me to browse around in my snapshot just
like any other file system.</p>
<p>In case you had not noticed yet: I&rsquo;ve become a fan of restic.</p>
<h2 id="next-level">Next level</h2>
<p>Initially I was only using restic to make backups of my laptop to an external
hard drive. But recently I&rsquo;ve taken it to a next level and use it for offsite
backups as well.</p>
<p><figure class="float-left"><img src="/images/restic_via_vpn.svg"
    alt="Schematic of my backup solution: restic -&gt; HAproxy -&gt; VPN tunnel -&gt; Nginx -&gt; Rest Server" width="150px">
</figure>

I&rsquo;ve put a computer at a remote location and run the <a href="https://github.com/restic/rest-server">Rest
Server</a>. In my local network I have
a VM that acts as a transparent HTTP proxy and is connected to the remote box
via VPN.</p>
<p>This way any local machine (like my NAS) only needs to be able to run restic and
I&rsquo;m good to go. I just point restic to the internal VM and it handles the VPN
for me.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">restic -r rest:https://&lt;haproxy_host&gt;/&lt;repository&gt;
</span></span></code></pre></div><p>The reason I put Nginx in front of the Rest Server is so that it can handle the
authentication and TLS termination. Sure, the Rest Server itself can also do
this, but I feel Nginx is more battle tested.</p>
<p>This setup allows me to automatically run my backups each night since I do not
have to attach a USB disk. It also gives me an offsite backup, without a monthly
bill from a storage provider. What&rsquo;s more: it gives me a sense of control. I know
exactly how everything works and if disaser strikes and I need to restore all my
data, I can bring the remote machine home and have local access to it.</p>
<p>Setting this up was something I have been putting off for a while now, but this
project is finally finished.</p>
<h2 id="useful-resources">Useful resources</h2>
<p>Some of the resources that helped me:</p>
<ul>
<li>The <a href="https://restic.readthedocs.io/en/stable/">restic documentation</a></li>
<li><a href="https://blog.stabel.family/restic-on-synology/">Restic on Synology</a></li>
<li><a href="https://braincoke.fr/blog/2020/06/backup-all-the-things-with-restic">Backup all the things with restic</a></li>
</ul>
<p>To be honest, I do not recall why I chose restic over
<a href="https://www.borgbackup.org/">BorgBackup</a> (Borg for short) at the time. But
if you are in the market for a tool to create backups with deduplication,
compression and encryption, you might want to check out both projects and see
which fits your situation best.</p>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[Full circle: rediscovering my joy in software engineering]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2025/06/08/full-circle-rediscovering-my-joy-in-software-engineering/" type="text/html" />
    <id>https://markvanlent.dev/2025/06/08/full-circle-rediscovering-my-joy-in-software-engineering/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="development" />
    <category term="go" />
    <category term="personal" />
    <category term="python" />
    
    <updated>2025-06-08T16:08:56Z</updated>
    <published>2025-06-08T00:00:00Z</published>
    <content type="html"><![CDATA[<p>This is a different kind of post than I normally write here. Most other posts
are about a problem I ran into or a conference I visited. This time is more a
story telling post. I though it would be nice to have a sort of summary of what
kind of work I have been doing for the last decade.</p>
<p>For years I&rsquo;ve have had a page <a href="/about/me">about me</a> which tells a bit of my
background and past and present jobs. In this post I would like to zoom in on,
roughly, the last thirteen years and write a bit about how my work and interests
evolved.</p>
<h2 id="software-developer">Software developer</h2>
<p>Let&rsquo;s start with my job at <a href="https://www.fox-it.com/">Fox-IT</a>, the company I
joined as a
<a href="https://www.djangoproject.com/">Django</a>/<a href="https://www.python.org/">Python</a>
developer mid 2013. I started building a portal for the customers of their
managed <a href="https://en.wikipedia.org/wiki/Security_operations_center">SOC</a> service
and I figured that&mdash;once this portal was done&mdash;I would find something else
within Fox to work on. However, this project only grew in functionality and even
became the tool used by the SOC analysts to get alerted on new incidents and
start their analysis in. I think all in all I spent the first four to five years
at Fox working on this platform on a daily basis.</p>
<p>Meanwile, due to organisational changes, my team was supposed to become less
dependant on a different business unit and as a ressult we would need to manage
our own infrastructure more. I&rsquo;ve always been interested in that kind of work,
so I started picking that up. And due to my background as a developer I wanted
to automate as much as possible. As a result my days started to become more an
more about creating and maintaining a testing environment. (I also have to admit
that messing around with physical servers was fun, especially initially.)</p>
<h2 id="infrastructure-developer">Infrastructure developer</h2>
<p>Slowly my work had become more about the infrastructure surrounding the product
we were developing, than writing code for the product itself. In hindsight I
think it was about 2018 when I was effectively no longer a developer on the
product. Instead of implementing features I was using
<a href="https://www.packer.io/">Packer</a> to create template for machine images, writing
<a href="https://www.terraform.io/">Terraform</a> to use these images (and managing other
infrastructure) and using
<a href="https://en.wikipedia.org/wiki/Ansible_(software)">Ansible</a> to help deploy the
product, et cetera.</p>
<p>Did I overengineer it? Probably. Did I like it and have I learned a lot from it?
Definitely!</p>
<p>Because I dislike the term &ldquo;DevOps engineer&rdquo;, I decided to call myself an
&ldquo;infrastructure developer&rdquo;. (Though I have to admit that on my CV and social
media profiles I used the title DevOps engineer when I was applying for a new
job since&mdash;whether I liked it or not&mdash;that is a more familiar term.) Looking
back to this now, there was also a clue hidden in there, but I&rsquo;ll get back to
that.</p>
<p>Since I was the only person doing this kind of work for my team, the
organisation figured it would be good to pair up with a colleague who was doing
similar kind of work for a different team to have some redundancy. While in
practice this did not work that well (yes, we had a similar role, but the
platforms and infrastructure were too diverse), it did lead to a new
opportunity.</p>
<p>A different team needed an extra person to help create a self-service, on-demand
environment to perform digital forensic investigations in. And given my interest
in cloud infrastructure (AWS) and my experience, I was a nice fit. I really
liked that project, learned a lot and enjoyed myself. And I wanted to do more of
this kind of work. However, this meant I had to look elsewhere.</p>
<h2 id="mission-critical-engineer">Mission Critical Engineer</h2>
<p>And that is how I ended up at <a href="https://schubergphilis.com/">Schuberg Philis</a> as
a mission critical engineer. As I had expected, this role is heavily operations
focussed. In my case, I helped to <a href="https://schubergphilis.com/how-we-work/plan-build-run">plan, build and
run</a> AWS infrastructure
for one of our customers. Unfortunately it was mostly &ldquo;run&rdquo; though. Don&rsquo;t get me
wrong, I definitely leveled up my AWS skills and genuinely enjoyed my time in
that team. But&hellip;</p>
<p>At a certain point in time our customer wanted to add an existing application to
their mission critical environment. Since it was a Python application (a Lambda
actually) I volunteered to help improve the application so it would be in a
state where we felt comfortable to offer 100% uptime and 24/7 support. Only then
did I realise what I was missing: software development and the joy that it gave
me.</p>
<p>Sure, I had been doing operations related work in the past, but in hindsight
most of the time I was still developing. Not building an application perhaps,
but infrastructure. I had always been more of a developer than an administrator.
I guess that was also why I liked the title &ldquo;infrastructure <strong>developer</strong>&rdquo;.</p>
<p>Lucky for me I was able to switch to a different team.</p>
<h2 id="mission-critical-software-engineer">Mission Critical Software Engineer</h2>
<p>And that&rsquo;s how I ended up where I am today. Little over a year ago I switched to
a role where I can focus on writing software again. And we, as a group of
software engineers, are also responsible for running, monitoring and supporting
our own services. So thinking about infrastructure is still a (small) part of
the job.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> But the main chunk of work is software engineering.</p>
<p>One thing did change though. Where my previous development jobs had been Python
oriented, in this team we use <a href="https://go.dev/">Go</a> to write our services. This
was part of my plan: by joining a Go team, I could broaden my horizon by
learning a new language.</p>
<p>Go was not completely new to me. I had done a
<a href="/2018/06/27/devopsdays-amsterdam-2018-workshops/#go-for-ops--michael-hausenblas-red-hat">Go workshop in 2018</a>.
And I had also made an attempt to rewrite an internal Python command line
application in Go. However, I had not properly learned the language, let
alone work with it as part of my job.</p>
<p>I might write more about learning and working with Go in a future post, but that
is beyond the scope of this one. I do want to say I thouroughly enjoy being a
software engineer again and learning how to do things in a differeny language.</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>We also have a couple of people in our team who are responsible for
setting up and maintaining the infrastructure we are running our services
on.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[FOSDEM 2025]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2025/02/01/fosdem-2025/" type="text/html" />
    <id>https://markvanlent.dev/2025/02/01/fosdem-2025/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="conference" />
    <category term="go" />
    <category term="kubernetes" />
    
    <updated>2025-06-08T15:27:48Z</updated>
    <published>2025-02-01T00:00:00Z</published>
    <content type="html"><![CDATA[<p>After years of thinking &ldquo;I should have gone&rdquo; after the fact, I finally went to
FOSDEM!</p>
<p>FOSDEM&mdash;which stands for Free and Open source Software Developers’ European
Meeting&mdash;is a free, no registration required, event held in Brussels each year.
Thousands of developers gather there to connect and share ideas. This was the
25th edition.</p>
<p>Below are the notes I took during the talks I attended.</p>
<h2 id="the-state-of-go--maartje-eyskens">The state of Go &mdash; Maartje Eyskens</h2>
<p>This is the eleventh edition of the Go devroom at FOSDEM. It&rsquo;s the first time
the Go devroom is bigger than the Python devroom. (By the way: Rust has the biggest one this
year.)</p>
<p>Go itself is 15 years now (so it is definitely not a new language anymore) and
has had 25 point zero releases. Go has a stable API, a strong and stable
standard library, dependency management and generics.</p>
<p>Go 1.23 was released on August 13th, 2024. Go version 1.24 will be released this
month. So what has changed since last year? To name a few (Maartje mentioned
more, but I wasn&rsquo;t able to write them all down. Watch the recording if you want
to see them all):</p>
<ul>
<li><strong>Language changes</strong>
<ul>
<li>Go can now loop over three new types</li>
<li>Generic types can now be used in type aliases.</li>
</ul>
</li>
<li><strong>Tools</strong>
<ul>
<li>&ldquo;<code>go vet</code>&rdquo;: many new warnings</li>
<li>Go tooling now support JSON (e.g. &ldquo;<code>go test --json</code>&rdquo;).</li>
<li>Go sets binary version based on VCS: &ldquo;<code>debug.ReadBuildInfo()</code>&rdquo; (use
&ldquo;<code>--buildvcs=false</code>&rdquo; to disable the <code>dirty</code> flag)</li>
<li>&ldquo;<code>go tool</code>&rdquo;: add tools used in builds in <code>go.mod</code>.</li>
<li>Go telemetry is still opt-in. Use &ldquo;<code>go telemetry on</code>&rdquo; and &ldquo;<code>go telemetry off</code>&rdquo; to
switch it on and off. The Go team believes that telemetry will play a
critical role in helping Go development.</li>
</ul>
</li>
<li><strong>Standard lib</strong>
<ul>
<li>Several new helpers for <code>iter</code> functions</li>
<li>Support for quantum proof key exchanges</li>
<li>Bunch of modern algorithms (e.g. PBKDF2 and SHA3) moved to stable library</li>
<li>&ldquo;<code>os.OpenRoot(&quot;path&quot;)</code>&rdquo; gives you a safe file system. Even protects against symbolic links outside of path.</li>
<li>JSON encoding supports new <code>omitzero</code></li>
<li>New <code>unique</code> package for faster comparison.</li>
</ul>
</li>
<li><strong>Runtime</strong>
<ul>
<li>Swiss maps</li>
</ul>
</li>
<li><strong>Ports</strong>
<ul>
<li>Go 1.25 will require macOS Monterey or later</li>
<li>Go 1.24 has no support for Windows windows/arm</li>
<li>Go 1.24 requires Linux kernel version 3.2 or later (released in 2012)</li>
</ul>
</li>
</ul>
<p>Go conferences this year:</p>
<ul>
<li>Go devroom @ FOSDEM 2025 (today)</li>
<li>Gophercon Latam Brazil (May 5–6)</li>
<li>GopherCon Europe Berlin (June 16–19)</li>
<li>Gophercon New York (August 26–28)</li>
</ul>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5353-the-state-of-go/">The state of Go</a></p>
<h2 id="the-inner-workings-of-go-generics--anton-sankov">The Inner Workings of Go Generics &mdash; Anton Sankov</h2>
<p>Generic allow you to work with different types, while keeping type safety.</p>
<p>Example:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span><span class="w"> </span><span class="nx">ToSlice</span><span class="p">[</span><span class="nx">T</span><span class="w"> </span><span class="kt">any</span><span class="p">](</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="w"> </span><span class="nx">T</span><span class="p">)</span><span class="w"> </span><span class="nx">T</span><span class="p">[]</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="k">return</span><span class="w"> </span><span class="p">[]</span><span class="nx">T</span><span class="p">{</span><span class="nx">a</span><span class="p">,</span><span class="w"> </span><span class="nx">b</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">intSlice</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ToSlice</span><span class="p">[</span><span class="kt">int</span><span class="p">](</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">floatSlice</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">ToSlice</span><span class="p">[</span><span class="kt">float32</span><span class="p">](</span><span class="mf">1.5</span><span class="p">,</span><span class="w"> </span><span class="mf">2.5</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Alternative, use type inference:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">intSlice2</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">ToSlice</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">2</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// Type safety: this will NOT compile:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nx">wrongSlice</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nf">ToSlice</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;string&#34;</span><span class="p">)</span><span class="w">
</span></span></span></code></pre></div><p>Why was Go created:</p>
<ul>
<li>Simplicity (over C++)</li>
<li>Fast compilation times (over C++)</li>
<li>Fast runtime (over C++)</li>
</ul>
<p>Generics complicates all three. However, people started complaining about the
lack of generics the day after Go was introduced to the world. Generics were
proposed a bunch of times and only in 2021 a proposal was accepted.</p>

  <figure>

<blockquote cite="https://research.swtch.com/generic">
The generic dilemma is this: do you want slow programmers, slow compilers and bloated binaries, or slow execution times?
</blockquote>

  <figcaption>
    &mdash;Russ Cox, <cite><a href="https://research.swtch.com/generic">The Generic Dilemma</a></cite>
  </figcaption>
  </figure>


<p>None of the proposals contained an implementation. So more proposals were
needed. Three proposals were written, and the last one was accepted: &ldquo;GC shape
stenciling&rdquo;, which is a middle ground between stenciling (proposal 1, the C++ way) and dictionaries (proposal 2, the Java way).</p>
<p>Anton showed an example of how this works. See <a href="https://asankov.dev/go-generics/">https://asankov.dev/go-generics/</a>
for the slides, which includes a full example, or his GitHub repo:
<a href="https://github.com/asankov/go-generics">https://github.com/asankov/go-generics</a></p>
<p>This proposal still has some drawbacks though: a performance penalty in compile time and in runtime. However, there&rsquo;s only little performance impact on compile time and usually only little performance penalty in runtime.</p>
<p>The exception to the latter is when you are passing interfaces to generic
methods. In this situation, generics can have a <em>big</em> performance impact. If
this matters to you: don&rsquo;t use generics.</p>
<figure><img src="/images/fosdem2025_anton_sankov_go_generics.png"
    alt="Picture comparing generics in C, C&#43;&#43;, Java and Go"><figcaption>
      <p>Anton comparing generics in different languages (image taken from <a href="https://asankov.dev/go-generics/36?clicks=3">his slides</a>)</p>
    </figcaption>
</figure>

<p>All in all, Go is in a good place with generics.</p>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5329-the-inner-workings-of-go-generics/">The Inner Workings of Go Generics</a></p>
<h2 id="swiss-maps-in-go--bryan-boreham">Swiss Maps in Go &mdash; Bryan Boreham</h2>
<p>Swiss Map is a new map implementation in Go 1.24.</p>
<p>The name of the way it&rsquo;s implemented is <strong>C</strong>losed <strong>H</strong>ashing. The story goes
that this is where the name &ldquo;Swiss map&rdquo; comes from: CH is the country code of
Switzerland.</p>
<p>There were lots of visuals in the presentation to explain how it works, which I
found hard to take notes on that can be understood without copying the whole
presentation alongside it. It was an interesting presentation, do watch the
video if you want to know more about the topic.</p>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-6049-swiss-maps-in-go/">Swiss Maps in Go</a></p>
<h2 id="go-ing-easy-on-memory-writing-gc-friendly-code--sümer-cip">Go-ing Easy on Memory: Writing GC-Friendly code &mdash; Sümer Cip</h2>
<p>There&rsquo;s a lot of theoretical info around garbage collection (GC), but less actual
tips and tricks. This presentation aims to be as practical as possible.</p>
<p>An example of why this topic is important: Datadog switched from Go to Rust
because their service spent 30% of CPU resources on GC
(<a href="https://www.datadoghq.com/blog/engineering/timeseries-indexing-at-scale/">source</a>).</p>
<p>Some of the tips from Sümer:</p>
<ul>
<li>Reducing size almost always has compounding benefits</li>
<li>Returning escapes to heap, calling does not (note: stack is better than heap for performance)</li>
<li><code>interface{}</code> and generics escape to heap</li>
<li>Avoid pointers! GC overhead is linear with the number of pointers.</li>
<li>Try keeping map key/values sizes under 128 bytes</li>
<li>&ldquo;Copying is expensive&rdquo; is a myth. Copying cache lines is the same as copying a pointer</li>
<li>Remember zero allocation libraries? Use them!</li>
<li>Reuse slices (e.g. <code>a = append(a[0:], 10, 20)</code> instead of <code>a = append(a, 10, 20)</code>)</li>
<li>Tune GC by using <code>GOGC</code> and <code>GOMEMLIMIT</code></li>
<li>Profile and benchmark your code</li>
</ul>
<p>Execution tracer is an underrated tool. It&rsquo;s a great cinematic visualization.
It&rsquo;s (kind of) safe to use on production: with Go 1.21 overhead drops to ~1-2%.
For more information see <a href="https://go.dev/blog/execution-traces-2024">More powerful Go execution
traces</a></p>
<p>As always: for more tips, details and background information (e.g. on memory and
garbage collection) watch the recording.</p>
<p>Other interesting talk: <a href="https://archive.fosdem.org/2018/schedule/event/faster/">Make your Go Faster</a> by Bryan
Boreham, FOSDEM 2018.</p>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5343-go-ing-easy-on-memory-writing-gc-friendly-code/">Go-ing Easy on Memory: Writing GC-Friendly code</a></p>
<h2 id="build-better-go-release-binaries--dimitri-john-ledkov">Build better Go release binaries &mdash; Dimitri John Ledkov</h2>
<p>The focus of this talk is on Linux binaries, but may also be applicable to other
environments. It&rsquo;s basically a list of tips. I wrote these down as reminders to
later look into these in more depth.</p>
<ul>
<li>&ldquo;<code>go build -ldflags -w</code>&rdquo; to remove debug information&mdash;which is on by
default&mdash;since it is often unused in production anyway but can be quite large
in size.</li>
<li>&ldquo;<code>go build -trimpath</code>&rdquo; to prevent leaking full file paths into the binary, to
not take up space an not doing it leads to non-reproducible builds.</li>
<li>&ldquo;<code>go build -tags netgo,osusergo</code>&rdquo; (for container/portable binaries,
&ldquo;<code>CGO_ENABLED=1 go build</code>&rdquo; for explicit host OS resolution)</li>
<li>&ldquo;<code>GOAMD64=v2 GOARM64=v8.0</code>&rdquo; for production hardware that is not 20 years old,
this will improve performance of your binaries</li>
<li>&ldquo;<code>go build -buildmode=pie</code>&rdquo;: position independent code/executable can improve
security. Use this for dynamic libraries.</li>
<li>&ldquo;<code>go build -ldflags=&quot;-X main.Version=$(git describe ...)&quot;</code>&rdquo;</li>
<li>Go toolchain doesn&rsquo;t respect <code>CFLAGS</code>/<code>CXXFLAGS</code>. Use <code>CGO_CFLAGS</code>,
<code>CGO_CXXFLAGS</code>, etc.</li>
<li>Use &ldquo;<code>govulncheck -mode=binary</code>&rdquo; to report module level CVEs. If your binary has
symbol tables, it reports symbol level CVEs. If you keep your symbols in your
binaries, the vulnerability checker could check better if the vulnerability is
actually affecting you.</li>
<li>Do <strong>not</strong> use &ldquo;<code>go build -ldflags -s</code>&rdquo; Do <strong>not</strong> use &ldquo;<code>strip --strip-all</code>&rdquo;.
Verify with &ldquo;<code>go tool nm</code>&rdquo;.</li>
<li>Bump your &ldquo;<code>toolchain go1.x.y</code>&rdquo; stanza regularly. The same code built with a new
go toolchain can be safer.</li>
</ul>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4406-build-better-go-release-binaries/">Build better Go release binaries</a></p>
<h2 id="kubernetes-outside-of-the-cloud-lessons-learned-after-3-years--nadia-santalla">Kubernetes outside of the cloud: Lessons learned after 3 years &mdash; Nadia Santalla</h2>
<p>For Nadia a self managed Kubernetes cluster, out of the cloud, is: managing your
own hardware, managing your own control plane, and not relying on external
services (like DNS).</p>
<p>A Kubernetes node is a properly configured and running <code>kubelet</code> process. A
control plane is a series of services that make Kubernetes work: an API, a
database (etcd often) and a bunch of clients using the API. These services often
run on Kubernetes itself (often on one or more dedicated machines).</p>
<p>There&rsquo;s an inception problem: if the services that run the control plane also
run in that control plane, how do we start? The answer: with a static manifest.</p>
<p>Useful tools if you want to run your own cluster:</p>
<ul>
<li>Kubeadm: generate static manifests, generating consistent config files, create
RBAC objects, create TLS certs, etc.</li>
<li>Kine (instead of etcd): etcd is not friendly to SSD lifespan. Kine can also be
deployed with Kubeadm.</li>
<li>Cilium as the CNI plugin: well documented, less hard to debug, lots of knobs
to tweak.</li>
<li>Cilium egress gateway: useful for multitenancy.</li>
<li>MetalLB: You&rsquo;ll probably need a load balancer, this is a nice one. Implements
failover using Gossip.</li>
<li>External-dns: creates A and AAAA records for ingress, load balancer servies and
custom resources. But it will <strong>not</strong> service the DNS records itself.</li>
<li>txqueuelen/stateless-dns: combines external-dns with PowerDNS. It makes
PowerDNS stateless.</li>
</ul>
<blockquote>
<p>Gitops is what Kubernetes makes it worth it.</p></blockquote>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4387-kubernetes-outside-of-the-cloud-lessons-learned-after-3-years/">Kubernetes outside of the cloud: Lessons learned after 3 years</a></p>
<h2 id="return-of-go-without-wires--ron-evans">Return Of Go Without Wires &mdash; Ron Evans</h2>
<p>I might be selling him short by summarizing it like this, but Ron showed his
adventures with his home made &ldquo;find my&rdquo; device, using
<a href="https://tinygo.org/">TinyGo</a>. If you like fiddling around with bluetooth
devices, definitely watch the recording.</p>
<p>Related link: <a href="https://github.com/hybridgroup/go-haystack">https://github.com/hybridgroup/go-haystack</a></p>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-5907-return-of-go-without-wires/">Return Of Go Without Wires</a></p>
<h2 id="go-lightning-talks">Go Lightning Talks</h2>
<p>These are short talks (8 minutes if I recall correctly), and I only took short
&ldquo;check this out later&rdquo; notes:</p>
<ul>
<li>Check what the <a href="https://pkg.go.dev/sync">sync package</a> has to offer.</li>
<li>A go links implementation: <a href="https://github.com/tobiaskohlbau/golinks">https://github.com/tobiaskohlbau/golinks</a></li>
<li><a href="https://github.com/nikolayk812/pgx-outbox">pgx-outbox</a>, a solution for the
<a href="https://thorben-janssen.com/dual-writes/">dual write problem</a> using the
<a href="https://microservices.io/patterns/data/transactional-outbox.html">transactional outbox pattern</a></li>
<li>Generate RESTful HTTP handlers for a resource/entity with
<a href="https://github.com/dolanor/rip">https://github.com/dolanor/rip</a></li>
<li>gno, &ldquo;go for dapps, design for modularity, composability and safety&rdquo;:
<a href="https://gno.land/">https://gno.land/</a></li>
</ul>
<p>Recording: <a href="https://fosdem.org/2025/schedule/event/fosdem-2025-4609-go-lightning-talks/">Go Lightning Talks</a></p>
<h2 id="summary--reflections">Summary / reflections</h2>
<p><em>This section was added on 2025-02-02 after I have had time to reflect on what
I&rsquo;ve heard and seen.</em></p>
<p>Looking back at yesterday, I must say I liked FOSDEM. I&rsquo;ve had a great day and
was able to listen to a bunch of interesting talks (more on those in a bit). I
usually go to these kind of conferences to get inspired and that also did happen
yesterday.</p>
<p>And while I&rsquo;m not really a <a href="https://en.wiktionary.org/wiki/hallway_track">hallway track</a>
kind of person, I did find the atmosphere at the stands pleasant. More than the
&ldquo;normal&rdquo;, commercial vendor booths you see at most conferences.</p>
<p>Now, about those talks. I think my biggest takeaways are:</p>
<ul>
<li>Using generics in Go is not something to shy away from for performance reasons
(at least in most cases). I&rsquo;ll keep them more in my mind when writing code.</li>
<li>Be more aware of garbage collection. While the services I&rsquo;m working on at work
do not have any performance issues (yet), it is good to be aware of some thinks,
like reusing slices. Especially for a person rather new to Go (me) it is
useful to form good habits.</li>
<li>There are lots of ways to tweak your Go binary builds. These are definitely
worth investigating and including them in our code base.</li>
<li>On a personal note: as the owner of a Raspberry Pi Pico W I&rsquo;ve learned that I
could use TinyGo instead of MicroPython to tinker with it.</li>
</ul>]]></content>
  </entry>
  <entry>
    <title type="html"><![CDATA[PyGrunn 2024]]></title>
    <link rel="alternate" href="https://markvanlent.dev/2024/05/17/pygrunn-2024/" type="text/html" />
    <id>https://markvanlent.dev/2024/05/17/pygrunn-2024/</id>
    <author>
      <name>map[name:Mark van Lent uri:https://markvanlent.dev/about/]</name>
    </author>
    <category term="conference" />
    <category term="python" />
    
    <updated>2024-05-17T16:42:29Z</updated>
    <published>2024-05-17T00:00:00Z</published>
    <content type="html"><![CDATA[<p>Notes from my day at the 12th edition of PyGrunn.</p>
<p><a href="https://pygrunn.org/">PyGrunn</a> is a Python focussed, one day conference held in
Groningen<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>. Or as the organizers more eloquently phrase this:</p>
<figure class="float-right"><img src="/images/pygrunn24_banner.jpg"
    alt="PyGrunn banner" width="150px">
</figure>

<blockquote>
<p>PyGrunn is the &ldquo;Python and friends&rdquo; developer conference with a local
footprint and global mindset. Firmly rooted in the open source culture, it
aims to provide the leaders in advanced internet technologies a platform to
inform, inspire and impress their peers.</p></blockquote>
<p>Before I start with my notes I want to give a shout-out to Reinout van Rees.
<a href="https://reinout.vanrees.org/weblog/tags/pygrunn.html">His (PyGrunn) summaries</a>
are excellent. I&rsquo;m always impressed by the quality of them and how little time
he needs to write them. Where I&rsquo;m only able to make notes and need to write them
out afterwards, Reinout has the summary (as a coherent story) ready before the
speaker has unhooked their laptop. So if you are interested in one of the talks
he has attended, head over
<a href="https://reinout.vanrees.org/weblog/tags/pygrunn.html">there</a> first.</p>
<h2 id="platform-engineering-python-perspective--andrii-mishkovskyi">Platform Engineering: Python Perspective &mdash; Andrii Mishkovskyi</h2>
<p>Why do platform engineering teams exist? We&rsquo;ve seen a &ldquo;shift left&rdquo; of
responsibilities towards the developer e.g. QA, operations (DevOps). But we
(software developers) are trained to write code, not to e.g. monitor it.</p>
<p>So where does a humble developer start? There is an abundance of choices on the
tools to use. What do you pick for package management? Or continuous
integration? Or deployment, code quality, observability, etc.  This freedom of
choice comes with a cost. We start with discussing which tools to use instead of
the problem the customer is facing. And depending on which choice we make, the
result may make reasoning about the software more complex.</p>
<p>So what should you do?</p>
<p>Andrii broke it down into three parts:</p>
<ul>
<li>You observe (i.e. you read a lot to get the lay of the land)</li>
<li>You execute (this is the actual software development part)</li>
<li>And then you collect feedback (you reach out to teams, you observe how their work has changed)</li>
</ul>
<p>Some of the platform engineering team deliverables:</p>
<ul>
<li>Documentation</li>
<li>Self service portal (tip: look into <a href="https://backstage.io/">Backstage</a>)</li>
<li>Boilerplates</li>
<li>APIs</li>
</ul>
<p>The &ldquo;consumers&rdquo; are developers, compliance teams and other platform teams. The
goal is to:</p>
<ul>
<li>Have reasonable defaults</li>
<li>Remove redundancy</li>
<li>Keep things consistent</li>
</ul>
<p>To get a feel for the scale of things: at Andrii&rsquo;s company there are over 160
services, developed on by 300+ developers in 500+ repositories. They total up
to 3 million lines of code and 6 million lines of YAML.</p>
<p>Templates (boilerplates) provide a paved path. The goal is to have teams spend
as little time as possible when starting a project. The templates use a certain
set tools that are supported. Teams are free to use different tools though if
they want to.</p>
<p>Andrii uses <a href="https://github.com/cookiecutter/cookiecutter">cookiecutter</a>
templates at work. It&rsquo;s not his choice perse, but it&rsquo;s what was already in place
when he joined. There are currently three templates in use. They have evolved
over time. For example in the last nine years, over 800 changes have been made
(that is more than one change per week on average).</p>
<p>The evolution has left its marks: the templates have a lot of code duplication.
There is also code specific to a tiny minority of projects (only about 8 out of
the 500+). This means that most projects start with deleting code after using
the boilerplate.</p>
<p>And that also relates the downside of using cookiecutter the way they do. Instead of
just using it to get started with a project, they also use it incrementally. But
cookiecutter does not have versioning built-in. So if you remove a file and then
reapply cookiecutter again, the file is happily created again.</p>
<p>While Andrii is aware of the issues (and thinks
<a href="https://github.com/copier-org/copier">copier</a> might be a better alternative),
it is hard to replace practices that are already in use. And cookiecutter is
great for getting started with a project.</p>
<p>With regard to the standardization, they use the following in the templates:</p>
<ul>
<li><code>pyproject.toml</code> for all projects</li>
<li>Poetry (instead of setuptools + pip-tools)</li>
<li>sprinkle <a href="https://github.com/renovatebot/renovate">Renovate</a> on top for
automatically updating dependencies</li>
</ul>
<p>As it currently stands, there are 99 project that migrated to <code>pyproject.toml</code> and
Poetry in the last 2 years. It makes sense because it takes time for projects to
transition. Plus they are not <em>required</em> to migrate; again: the template are there
to help, not to limit the users. Renovate has been adopted more quickly.</p>
<p>Migrating from e.g. <code>pkg_resources</code> to <code>pkgutil</code> or
<a href="https://peps.python.org/pep-0420/">PEP-420</a> for namespaces packages is hard.
Templates can help with that. However, cookiecutter does not actually <em>manage</em>
files. So if a file has been removed from a template, rerunning cookiecutter
does not remove the file from the project. So that requires some care.</p>
<p>When they migrated from a monolithic application to a microservices
architecture, authentication/authorization became an issue. There was no
visibility for teams, no transparency and no accountability. To combat this, they
created an API where applications can declare the required access and scopes
in a YAML file. Maintainers can approve this access. And this also allows for
CI/CD to check access. A CLI tool can verify the validity of the YAML and check
if access is actually approved.</p>
<h2 id="securing-your-team-solution-and-company-to-embrace-chaos--edzo-botjes">Securing your team, solution and company to embrace chaos &mdash; Edzo Botjes</h2>
<p>Edzo started by sharing a link to
<a href="https://docs.google.com/presentation/d/1j7HgfiZXd51QdPHD1_yptzbxx8s9O2BNsA0dCG-Ajes/edit#slide=id.g2dd9784acd0_0_108">his slides</a>
and warned us that it usually takes two weeks for people to digest the contents
of his talk. He would overload us with information. And that&rsquo;s where I decided
to solely concentrate on the talk and not on note taking.</p>
<blockquote>
<p>Nobody knows what they are doing. Embrace this.</p></blockquote>
<p>Even a simple, deterministic system like a <a href="https://en.wikipedia.org/wiki/Double_pendulum">double
pendulum</a> has a nondeterministic
outcome. In other (my) words: the whole world is in chaos and unpredictable.</p>
<p>When presented with information everyone processes it differently and
understands something else (also see viral phenomenon of <a href="https://en.wikipedia.org/wiki/The_dress">the dress</a>).</p>
<p><img src="/images/pygrunn24_edzo_botjes.png" alt="Different perspectives: one person sees a circle, another a rectangle"></p>
<p>What worked for Edzo was to embrace chaos. To do this he let go of his desire to
control and trying to create a predictable outcome.</p>
<h2 id="descriptors-decoding-the-magic--alex-dijkstra">Descriptors: Decoding the Magic &mdash; Alex Dijkstra</h2>
<p>Many people have used descriptors without them even being aware of it.</p>
<p>From the documentation:</p>

  <figure>

<blockquote cite="https://docs.python.org/3/howto/descriptor.html">
Descriptors let objects customize attribute lookup, storage, and deletion.
</blockquote>

  <figcaption>
    &mdash;<cite><a href="https://docs.python.org/3/howto/descriptor.html">Descriptor Guide</a></cite>
  </figcaption>
  </figure>


<p>You can view descriptors as reusable @properties. A descriptor implements the <code>__get__</code> and
<code>__set__</code> methods (and when needed <code>__delete__</code>).</p>
<p>Alex showed a bunch of examples. This is the template he showed to introduce
descriptors:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyDescriptor</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__get__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">owner</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># owner is the class to which the instance belongs</span>
</span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">obj</span><span class="o">.</span><span class="vm">__dict__</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="fm">__set__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">obj</span><span class="p">,</span> <span class="n">val</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="c1"># self is descriptor instance.</span>
</span></span><span class="line"><span class="cl">        <span class="n">obj</span><span class="o">.</span><span class="vm">__dict__</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">private_name</span><span class="p">]</span> <span class="o">=</span> <span class="n">val</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">def</span> <span class="nf">__set_name__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">owner</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">public_name</span> <span class="o">=</span> <span class="n">name</span>
</span></span><span class="line"><span class="cl">        <span class="bp">self</span><span class="o">.</span><span class="n">private_name</span> <span class="o">=</span> <span class="sa">f</span><span class="s1">&#39;_</span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s1">&#39;</span>
</span></span></code></pre></div><p>His example of how to use this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="k">class</span> <span class="nc">MyClass</span><span class="p">:</span>
</span></span><span class="line"><span class="cl">    <span class="n">value</span> <span class="o">=</span> <span class="n">MyDescriptor</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="n">myinstance</span> <span class="o">=</span> <span class="n">MyClass</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="n">myinstance</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="mi">5</span>  <span class="c1"># MyDescriptor.__set__</span>
</span></span><span class="line"><span class="cl"><span class="n">myinstance</span><span class="o">.</span><span class="n">value</span>      <span class="c1"># MyDescriptor.__get__</span>
</span></span></code></pre></div><p>Using descriptors you can do things  when getting or setting the value. E.g. in a
class you can enforce that an attribute has a certain type.</p>
<p>The <code>__set_name__</code> method was introduced in Python 3.6. It is not needed to use this in all of your
descriptors, but also doesn&rsquo;t hurt.</p>
<p>Do you want to use descriptors all over the place? No. They can be useful: you
can create a clean APIs with them and this helps if the API is used frequently.
However, it does create some overhead and the code is a bit more complex.</p>
<p>Resources:</p>
<ul>
<li><a href="https://docs.python.org/3/howto/descriptor.html">https://docs.python.org/3/howto/descriptor.html</a></li>
<li>Luciano Ramalho&rsquo;s book
<a href="https://www.oreilly.com/library/view/fluent-python-2nd/9781492056348/">Fluent Python</a>
(Luciano also did a few talks about descriptors)</li>
</ul>
<h2 id="release-the-krakend--erik-jan-blanksma">Release the KrakenD &mdash; Erik-Jan Blanksma</h2>
<p><a href="https://www.krakend.io/">KrakenD</a> is an API gateway product. Erik-Jan likes it
so much he wanted to share his experience with it.</p>
<p>Projects can start out simple, with a monolith that is accessed via a web
client. Before you know it, there are several services and multiple types of
clients. The solution is to introduce an API Gateway in the middle. It can then
handle the incoming requests.</p>
<p>API Gateway in short:</p>
<ul>
<li>It sits between clients and backend (as a sort of portal).</li>
<li>It hides internal complexity of backend for the clients.</li>
<li>The gateway is a great place to introduce things like
authorization/authentication, logging, load balancing, caching, etc.</li>
</ul>
<p>KrakenD is one of the available API Gateways. It is open source, but there&rsquo;s
also an enterprise version with extra features. KrakenD is implemented in Go and
offers a bunch of features out of the box (monitoring, throttling, request and
response manipulation). KrakenD offers integrations with e.g. tools (Jaeger,
Grafana, the Elastic stack), authorization/authentication services and queues
(RabbitMQ).</p>
<p>KrakenD is a stateless process, so no database is needed. It takes JSON (or
YAML) config. It can combine the results of multiple backends API calls and
return it as a single response.</p>
<p>Tips:</p>
<ul>
<li>Use the KrakenDesigner (makes it easy to explore what&rsquo;s possible). Note that
you do not want to use this in production.</li>
<li>You&rsquo;ll want to split up the configuration when it grows. By using flexible
configuration you can combine so called partials, settings and templates.</li>
<li>KrakenD can check and even audit you configuration.</li>
<li>Since the configuration is in JSON, you can generate
OpenAPI.json from the KrakenD config. You can use this for Swagger.</li>
</ul>
<p>KrakenD is a great tool to manage your APIs. It is lightweight, fast, easy to
configure and has lots of functions out of the box. It is versatile and
extensible. By using it you can make your architecture more agile.</p>
<p>However, it also means that you will have to manage the API Gateway
configuration. And a change in the configuration means you will have to restart
the process.</p>
<h2 id="general-tips">General tips</h2>
<p>Some general notes:</p>
<ul>
<li>Have a look at:
<ul>
<li><a href="https://docs.pydantic.dev/latest/">Pydantic</a></li>
<li><a href="https://github.com/aws/chalice">Chalice</a></li>
<li><a href="https://xata.io/">Xata</a></li>
</ul>
</li>
<li>It can be helpful use functional programming (e.g. using closures) instead of
by default using classes and methods.</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>&ldquo;Grunn&rdquo; is what Groningen is called in the regional language&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>]]></content>
  </entry>
</feed>
