Congratulations!

[Valid Atom 1.0] This is a valid Atom 1.0 feed.

Recommendations

This feed is valid, but interoperability with the widest range of feed readers could be improved by implementing the following recommendations.

Source: http://reinout.vanrees.org/weblog/atom.xml

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <feed xmlns="http://www.w3.org/2005/Atom"
  3.      xmlns:dc="http://purl.org/dc/elements/1.1/"
  4.      xml:base="http://reinout.vanrees.org/" xml:lang="en">
  5.  <link rel="self"
  6.        href="http://reinout.vanrees.org/weblog/atom.xml" />
  7.  <link href="http://reinout.vanrees.org/weblog/"
  8.        rel="alternate" type="text/html" />
  9.  
  10.  <div xmlns="http://www.w3.org/1999/xhtml">
  11.    <a href="http://www.atomenabled.org/feedvalidator/check.cgi?url=http%3A%2F%2Freinout.vanrees.org%2Fweblog%2Fatom.xml">
  12.      <img title="Validate my Atom feed" width="88"
  13.           height="31"
  14.           src="http://www.atomenabled.org/feedvalidator/images/valid-atom.png"
  15.           alt="[Valid Atom]" border="0px" />
  16.    </a>
  17.    <p>
  18.      <span>
  19.        This is an Atom formatted XML site feed. It is intended to be viewed in
  20.        a Newsreader or syndicated to another site. Please visit
  21.      </span>
  22.      <a href="http://www.atomenabled.org/">Atom Enabled</a>
  23.      <span>
  24.        for more info.
  25.      </span>
  26.    </p>
  27.  </div>
  28.  
  29.  <title type="html">Reinout van Rees' weblog</title>
  30.  <subtitle>Python, grok, books, history, faith, etc.</subtitle>
  31.  <updated>2009-04-04T21:44:00+01:00</updated>
  32.  <id>urn:syndication:a55644db8591c020bd38852775819a9a</id>
  33.  
  34.  
  35.  <entry>
  36.    <title>Pycon NL: keynote: how not to get fooled by your data while AI engineering - Sofie van Landeghem</title>
  37.    <link rel="alternate" type="text/html"
  38.          href="http://reinout.vanrees.org/weblog/2025/10/16/8-fooled-data-ai.html" />
  39.      <id>http://reinout.vanrees.org/weblog/2025/10/16/8-fooled-data-ai.html</id>
  40.      <author>
  41.        <name>Reinout van Rees</name>
  42.      </author>
  43.      <published>2025-10-16T00:00:00+01:00</published>
  44.      <updated>2025-10-16T14:50:00+01:00</updated>
  45.  
  46.      
  47.      <category term="python" />
  48.      
  49.      <category term="pycon" />
  50.      
  51.  
  52.      <content type="html"><![CDATA[
  53.        <div class="document">
  54. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  55. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  56. <p>(Sofie helps maintain FastAPI, Typer and spaCy; this talk is all about AI).</p>
  57. <p>Sofie started with an example of a chatbot getting confused about the actual winner of
  58. an F1 race after disqualification of the winner. So you need to have a domain expert on
  59. board who can double-check the data and the results.</p>
  60. <p>Let's say you want your chatbot output to link to Wikipedia for important terms. That's
  61. actually a hard task, as it has to do normalization of terms, differentiating between
  62. Hamilton-the-driver, Hamilton-the-town, Hamilton-the-founding-father and more.</p>
  63. <p>There's a measure for quality of output that's called an &quot;F-score&quot;. She used some AI
  64. model to find the correct page and got a 79.2% F-score. How good or bad is it?</p>
  65. <p>For this, you can try to determine a reasonable bottom line. &quot;Guessing already means
  66. 50%&quot; is what you might think. No, there are 7 million Wikipedia pages, so random
  67. guessing gives 0% F-score. Let's pick all the pages which actually mention the word
  68. &quot;Hamilton&quot;. If we then look at more words like &quot;Alexander Hamilton&quot; or &quot;Lewis Hamilton&quot;,
  69. we can reason that a basic non-AI regular approach should get 78% at least, so the AI
  70. model's 79.2% isn't impressive.</p>
  71. <p>The highest reachable quality depends on the data itself and what people
  72. expect. &quot;Hamilton won at Spa&quot;, do you expect Spa to point at the town or at the circuit?
  73. The room voted 60/40, so even the best answer itself can't be 100% correct :-)</p>
  74. <p>A tip: if you get a bad result, investigate the training data to see if you can spot
  75. some structural problem (which you can then fix). Especially if you have your own
  76. annotated data. In her example, some of the annotators annotated circuit names
  77. <em>including</em> the &quot;GP&quot; or &quot;grand prix&quot; name (&quot;Monaco GP&quot;) and others just the town name
  78. (&quot;Spa&quot;).</p>
  79. <p>Some more tips:</p>
  80. <ul class="simple">
  81. <li>Ensure your label scheme is consistent.</li>
  82. <li>Draft clear annotation guidelines.</li>
  83. <li>Measure inter-annotator agreement (IAA). So measure how much your annotators agree on
  84. terms. An article on F1 and politics: how many annotate it as politics and how many as
  85. F1?</li>
  86. <li>Consider reframing your task/guidelines if the IAA is low.</li>
  87. <li>Model uncertainty in your annotation workflow.</li>
  88. <li>Identify structural data errors.</li>
  89. <li>Apply to truly unseen data to measure your model's performance.</li>
  90. <li>Make sure you climb the right hill.</li>
  91. </ul>
  92. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-8.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-8.jpeg" />
  93. <p><em>Unrelated photo from our 2025 holiday in Austria: just over the border in Germany, we
  94. stayed two days in Passau. View from the 'Oberhaus' castle on three rivers combining,
  95. with visibly different colors. From the left, the small, dark 'Ilz'. The big,
  96. drab-colored one in the middle is the 'Donau' (so 'schöne blaue Donau' should be taken
  97. with a grain of salt). From the right, also big, the much lighter 'Inn' (lots of granite sediment from the Alps, here).</em></p>
  98. </div>
  99.  
  100.        ]]>
  101.      </content>
  102.  
  103.    </entry>
  104.    
  105.  <entry>
  106.    <title>Pycon NL: workshop: measuring and elevating quality in engineering practice - Daniele Procida</title>
  107.    <link rel="alternate" type="text/html"
  108.          href="http://reinout.vanrees.org/weblog/2025/10/16/7-measuring-elevating-quality.html" />
  109.      <id>http://reinout.vanrees.org/weblog/2025/10/16/7-measuring-elevating-quality.html</id>
  110.      <author>
  111.        <name>Reinout van Rees</name>
  112.      </author>
  113.      <published>2025-10-16T00:00:00+01:00</published>
  114.      <updated>2025-10-16T13:55:00+01:00</updated>
  115.  
  116.      
  117.      <category term="python" />
  118.      
  119.      <category term="pycon" />
  120.      
  121.      <category term="django" />
  122.      
  123.  
  124.      <content type="html"><![CDATA[
  125.        <div class="document">
  126. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  127. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  128. <p>Daniele works as director of engineering at Canonical (the company behind
  129. Ubuntu). What he wants to talk about today is how to <strong>define, measure and elevate
  130. engineering quality at scale</strong>. That's his job. He needs to influence/change that in an
  131. organization with a thousand technical people in dozens of teams with 100+
  132. projects. They ideally must converge on the standards of quality <em>he</em> has defined <em>and
  133. there's only one of me</em>. <strong>Engineering people are opinionated people</strong> :-)</p>
  134. <p>Your personal charm and charisma wears thin after a while: there needs to be a different
  135. way. So: how can you get 1000+ to do what you want, the way you want. Ideally somewhat
  136. willingly? You cannot <em>make</em> people do it. You'll have to be really enthousiastic about
  137. it.</p>
  138. <p>He suggests three things:</p>
  139. <ul class="simple">
  140. <li><strong>Principle</strong>. Description of quality as objective conditions, allowing it to be defined
  141. and measured.</li>
  142. <li><strong>Tool</strong>. A simple dashboard, that reinforces your vision of quality and reflects it back
  143. to your teams. Daniele focuses on documentation, and showed a dashboard/spreadsheet
  144. that showed the documentation status/progress of various projects. You can do the same
  145. for &quot;security&quot; for instance.</li>
  146. <li><strong>Method</strong>. A way of drawing your teams into your vision, so that they actively want to
  147. participate in.</li>
  148. </ul>
  149. <p>It being a workshop, we worked through a few examples. Someone mentioned &quot;improved test
  150. coverage in our software&quot;.</p>
  151. <ul class="simple">
  152. <li>Describe your aim(s). What do you want. What is the background documentation. What is
  153. your reason.</li>
  154. <li>You need objectives on various levels. &quot;Started&quot;, &quot;first results&quot;, &quot;mature&quot;. And you
  155. can have those levels for each of your aims/categories. Start small and start
  156. specific.<ul>
  157. <li><strong>Started</strong>. &quot;The team understands the significance of automated testing&quot;. &quot;We have
  158. coverage information about tests&quot;.</li>
  159. <li><strong>First results</strong>. &quot;There is a significant increase in test coverage&quot;. Note:
  160. &quot;significant&quot; means you have something to talk about. You can be reasonable on the
  161. one hand, but you can also call out low numbers. Human-sized words with value, like
  162. &quot;significant&quot;, help internalize it. More than a number like &quot;25%&quot; would ever do. You
  163. don't want to check off the box &quot;25%&quot;, you want to be able to claim that your team
  164. now has significant test coverage!</li>
  165. <li><strong>Mature</strong>. Let's keep it simple with &quot;100% test coverage&quot;.</li>
  166. </ul>
  167. </li>
  168. <li>Measure the level projects are at at the moment. Show it in a dashboard. He used a
  169. Google spreadsheet previously, now it is a Django website. He'll make it a
  170. world-public website soon. So it is visible for everybody. This helps draw teams into
  171. it.</li>
  172. </ul>
  173. <p>Why does this work with human beings?</p>
  174. <ul class="simple">
  175. <li><strong>Peer pressure</strong>. People see their peers doing the right thing. People want to be seen
  176. doing the right thing.</li>
  177. <li><strong>Objectification</strong>. The contract and the results are described objectively. The
  178. conditions and evidence stand <em>outside</em> you: it is not personal anymore, so it is not
  179. a threat.</li>
  180. </ul>
  181. <p>Humans are funny creatures. As soon as they believe in something, it will carry them
  182. over many bumps in the road.</p>
  183. <p>People love to see their work recognized. So if you maintain a spreadsheet with all the
  184. projects' results and progress, you won't have to ask them for an update: they will bug
  185. you if the spreadsheet hasn't been updated in a while. They really want to see the work
  186. they've put in!</p>
  187. <p>You can get a positive feedback loop. If the work you need to do is clear, if the value
  188. is clear and if there is recognition, you'll want to do it almost automatically. And if
  189. you do it, you mention it in presentations and discussions with others. Then the others
  190. are automatically more motivated to work on it, too.</p>
  191. <p>Giving kids a sticker when they do something successfully really helps. It also works
  192. for hard-core programmers and team managers!</p>
  193. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-7.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-7.jpeg" />
  194. <p><em>Unrelated photo from our 2025 holiday in Austria: just over the border in Germany,
  195. Passau has a nice cathedral.</em></p>
  196. </div>
  197.  
  198.        ]]>
  199.      </content>
  200.  
  201.    </entry>
  202.    
  203.  <entry>
  204.    <title>Pycon NL: kedro, lessons from maintaining an open source framework - Merel Theisen</title>
  205.    <link rel="alternate" type="text/html"
  206.          href="http://reinout.vanrees.org/weblog/2025/10/16/6-kedro-maintaining-framework.html" />
  207.      <id>http://reinout.vanrees.org/weblog/2025/10/16/6-kedro-maintaining-framework.html</id>
  208.      <author>
  209.        <name>Reinout van Rees</name>
  210.      </author>
  211.      <published>2025-10-16T00:00:00+01:00</published>
  212.      <updated>2025-10-16T12:31:00+01:00</updated>
  213.  
  214.      
  215.      <category term="python" />
  216.      
  217.      <category term="pycon" />
  218.      
  219.  
  220.      <content type="html"><![CDATA[
  221.        <div class="document">
  222. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  223. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  224. <p>Full title: <em>leading kedro: lessons from maintaining an open source python framework</em>.</p>
  225. <p>Merel is the tech lead of the python open source framework <a class="reference external" href="https://kedro.org/">kedro</a>.</p>
  226. <p>What is open source? Ok, the source code is publicly available for anyone to use, modify
  227. and share. But it is also a concept of sharing. Developing together. &quot;Peer
  228. production&quot;. It also means sharing of technical information and documentation. In the
  229. 1990s the actual term &quot;open source&quot; was coined. Also, an important milestone: Github was
  230. launched in 2008, greatly easing open source development.</p>
  231. <p>Kedro is a python toolbox that applies software engineering principles to data science
  232. code, making it easier to go from prototype to production. Started in 2017, it was open
  233. sourced in 2019. (Note: Kedro has now been donated to the Linux foundation). This made
  234. it much easier to collaborate with others outside the original company (Quantumblack).</p>
  235. <p>Open source also means <strong>maintenance challenges</strong>. It is not just code. Code is the
  236. simple part. How to attract contributors? How to get good quality contributions? What to
  237. accept/reject? How to balance quick wins with the long term vision of the project? How
  238. to make contributors come back?</p>
  239. <p>What lessons did they learn?</p>
  240. <ul class="simple">
  241. <li>Importance of contributor guidance. They themselves had high standards with good
  242. programming practices. How much can you ask from new contributors? They noticed they
  243. needed to improve their documentation a lot. And they had to improve their tooling. If
  244. you want well-formatted code, you need easy tools to do the formatting, for
  245. instance. And you need to actually <em>document</em> your formatting guidelines :-)</li>
  246. <li>Response time is important. Response time for issues, pull requests and support. If
  247. you don't get a timely answer, you'll lose interest as contributor. Also: tickets need
  248. to be polished and made clearer so that new contributors can help fixing them.</li>
  249. <li>Sharing pain points is a contribution, too. More contributors and users automatically
  250. mean more feature requests. But you don't want your project to become a Frankenstein
  251. monster... A configuration file, for instance, can quickly become too cluttered
  252. because of all the options. Sometimes you need to evolve the architecture to deal with
  253. common problems. Users will tell you what they want, but perhaps it can be solved
  254. differently.</li>
  255. <li>The importance of finding contribution models that fit. Perhaps a plugin mechanism for
  256. new functionality? Perhaps a section of the code marked &quot;community&quot; without the
  257. regular project's guarantees about maintenance and longevity?</li>
  258. <li>Be patient and kind. &quot;Open source&quot; means &quot;people&quot;. Code is the easy part, people add
  259. complexity. Maintainers can be defensive and contributors can be demanding.</li>
  260. </ul>
  261. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-6.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-6.jpeg" />
  262. <p><em>Unrelated photo from our 2025 holiday in Austria: Neufelden has a dam+reservoir, the
  263. water travels downstream by underground pipe to the hydropower plant. At this point the
  264. pipe comes to the surface and crosses the river on a concrete construction. Nearby, the
  265. highest road bridge in this region also crosses.</em></p>
  266. </div>
  267.  
  268.        ]]>
  269.      </content>
  270.  
  271.    </entry>
  272.    
  273.  <entry>
  274.    <title>Pycon NL: don't panic, a developer's guide to security - Sebastiaan Zeeff</title>
  275.    <link rel="alternate" type="text/html"
  276.          href="http://reinout.vanrees.org/weblog/2025/10/16/5-developer-guide-security.html" />
  277.      <id>http://reinout.vanrees.org/weblog/2025/10/16/5-developer-guide-security.html</id>
  278.      <author>
  279.        <name>Reinout van Rees</name>
  280.      </author>
  281.      <published>2025-10-16T00:00:00+01:00</published>
  282.      <updated>2025-10-16T11:58:00+01:00</updated>
  283.  
  284.      
  285.      <category term="python" />
  286.      
  287.      <category term="pycon" />
  288.      
  289.  
  290.      <content type="html"><![CDATA[
  291.        <div class="document">
  292. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  293. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  294. <p>He showed a drawing of <a class="reference external" href="https://en.wikipedia.org/wiki/Cornelis_Jol">Cornelis &quot;wooden leg&quot; Jol</a>, a pirate from the 17th century from
  295. Sebastiaan's hometown. Why is he a pirate? He dresses like one, has a wooden leg,
  296. murders people like pirate and even has a parrot, so he's probably a pirate. For python
  297. programmers used to duck typing, this is familiar.</p>
  298. <p>The 17th century, the Netherlands were economically wealthy. And had a big sea-faring
  299. empire. But they wanted a way to expand their might without paying for
  300. it. So... privatization to the rescue. You give pirates a <em>vrijbrief</em>, a government
  301. letter saying they've got some kind of &quot;permission&quot; from the Dutch government to rob and
  302. pillage and kill everybody as long it aren't Dutch people and ships. <strong>A privateer</strong>.So
  303. it looks like a pirate and behaves like a pirate, but it isn't technically a real
  304. pirate.</p>
  305. <p>Now on to today. There are a lot of cyber threats. Often state-sponsored. You <em>might</em> have
  306. a false sense of security in working for a relatively small company instead of for a
  307. juicy government target. But... privateers are back! Lots of hacking companies have
  308. coverage of governments - as long as they hack other countries. And hacking small
  309. companies can also be profitable.</p>
  310. <p>&quot;I care about security&quot;. <strong>Do you really?</strong> What do real security people think? They
  311. think developers don't really pay much attention to it. Eye-roll at best, disinterest at
  312. worst. Basically, &quot;it is somebody else's problem&quot;.</p>
  313. <p>What you need is a <strong>security culture</strong>. A buy-in at every level. You can draw an
  314. analogy with <em>safety culture</em> at physically dangerous companies like petrochemical. So:
  315. you as developer, should argue for security with your boss. You are a developer, so you
  316. have a duty to speak up. Just like a generic employee at a chemical plant has the duty
  317. to speak when seeing something risky.</p>
  318. <p>You don't have to become a security export (on top of everything else), but you do have
  319. to pay attention. Here are some pointers:</p>
  320. <ul class="simple">
  321. <li>&quot;Shift left&quot;. A term meaning you have to do it earlier rather than later. Don't try to
  322. secure your app just before shipping, but take it into account from the
  323. beginning. <strong>Defense in depth</strong>.</li>
  324. <li>&quot;Swiss cheese model&quot;. You have multiple layers in your setup. Every layer only needs
  325. one hole for the total to be penetrated.</li>
  326. <li>Learn secure design principles. &quot;Deny by default&quot;, &quot;fail securely&quot;, &quot;avoid security by
  327. obscurity&quot;, &quot;minimize your attack surface&quot;, etc. <strong>Deny by default</strong> is a problem in
  328. the python world. We're beginner-friendly, so often everything is open...</li>
  329. <li>Adopt mature security practices. Ignore ISO 27001, that's too hard to understand. Look
  330. at <a class="reference external" href="https://owasp.org/">OWASP</a> instead. OWASP DevSecOps maturity model (&quot;pin your
  331. artifacts&quot;, for instance).</li>
  332. <li>Know common vulnerabilities. Look at the popular &quot;top 10&quot; lists. Today, SQL injection
  333. <em>still</em> makes victims...</li>
  334. </ul>
  335. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-5.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-5.jpeg" />
  336. <p><em>Unrelated photo from our 2025 holiday in Austria: center of Neufelden, nicely restored
  337. and beautifully painted.</em></p>
  338. </div>
  339.  
  340.        ]]>
  341.      </content>
  342.  
  343.    </entry>
  344.    
  345.  <entry>
  346.    <title>Pycon NL: tooling with purpose - Aris Nivortis</title>
  347.    <link rel="alternate" type="text/html"
  348.          href="http://reinout.vanrees.org/weblog/2025/10/16/4-tooling-with-purpose.html" />
  349.      <id>http://reinout.vanrees.org/weblog/2025/10/16/4-tooling-with-purpose.html</id>
  350.      <author>
  351.        <name>Reinout van Rees</name>
  352.      </author>
  353.      <published>2025-10-16T00:00:00+01:00</published>
  354.      <updated>2025-10-16T10:30:00+01:00</updated>
  355.  
  356.      
  357.      <category term="python" />
  358.      
  359.      <category term="pycon" />
  360.      
  361.  
  362.      <content type="html"><![CDATA[
  363.        <div class="document">
  364. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  365. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  366. <p>Full title: <em>tooling with purpose: making smart choices as you build</em>.</p>
  367. <p>Aris uses python and data to answers research questions about everything under the
  368. ground (as geophysicist).</p>
  369. <p>As a programmer you have to make lots of choices. Python environment, core project
  370. tooling, project-specific tooling, etc.</p>
  371. <p>First: <strong>python environment</strong> management: pyenv/venv/pip, poetry, uv. And conda/pixi for
  372. the scientific python world. A show of hands showed <tt class="docutils literal">uv</tt> to be real popular.</p>
  373. <p>Now core <strong>project tooling</strong>. Which project structure? Do you use a
  374. template/cookiecutter for it?  Subdirectories? A testing framework? Pytest is the
  375. default, start with that. (He mentioned &quot;doctests&quot; becoming very popular: that surprised
  376. me, as they were popular before 2010 and started to be considered old and deprecated
  377. after 2010. I'll need to investigate a bit more).</p>
  378. <p>Linting and type checking? Start with <tt class="docutils literal">ruff</tt> for formatting/checking. Mypy is the
  379. standard type checker, but pyright/vscode and pyre are options. And the new <tt class="docutils literal">ty</tt> is
  380. alpha, but looks promising.</p>
  381. <p>Also, part of the core tooling: do you document your code? At least a README.</p>
  382. <p>For <strong>domain specific tooling</strong> there are <em>so many choices</em>. It is easy to get
  383. lost. What to use for data storage? Web/API? Visualization tools. Scientific libraries.</p>
  384. <p>Choose wisely! With great power comes great responsibility, but with great power also
  385. comes the <em>burden of decision-making</em>. Try to standardize. Enforce policies. Try to keep
  386. it simple.</p>
  387. <p>Be aware of over-engineering. Over-engineering often comes with good
  388. intentions. And... sometimes complexity <em>is</em> the right path. As an example, look at
  389. database choices. You might wonder between SQL or a no-sql database and whether you need
  390. to shard your database. But often a simple sqlite database file is fast enough!</p>
  391. <p>Configuration management: start with a simple <tt class="docutils literal">os.getenv()</tt> and grab settings from
  392. environment variables. Only start using .toml files when that no longer fits your use
  393. case.</p>
  394. <p>Web/api: start simple. You probably don't need authentication from the start if it is
  395. just a quick prototype. Get something useful working, first. Once it works, you can
  396. start working on deployment or a nicer frontend.</p>
  397. <p>Async code is often said to be faster. But debugging is time-consuming and hard. Error
  398. handling is different. It only really pays off when you have many, many concurrent
  399. operations. Profile your code before you start switching to async. It won't speed up
  400. CPU-bound code.</p>
  401. <p>Logging: just start using with the built-in logging module. Basic logging is better than
  402. no logging. Don't start the Perfect Fancy Logging Setup until you have the basics
  403. running.</p>
  404. <p>Testing is good and recommended, but don't go overboard. Don't &quot;mock&quot; everything to get
  405. 100% coverage. Those kinds of tests break often. And often the tests test the mock
  406. instead of your actual code. <strong>Aim for the same amount of test code compared to your
  407. actual code</strong>.</p>
  408. <p>Some closing comments:</p>
  409. <ul class="simple">
  410. <li>Sometimes simple choices are better.</li>
  411. <li>Don't let decision=making slow you down. Start making prototypes.</li>
  412. <li>One-size-fits-all solutions don't exist. Evaluate for your use case.</li>
  413. <li>If you are an experienced developer, help your colleagues. They have to make lots of
  414. choices.</li>
  415. <li>Early-career developer? Luckily a lot of choices are already made for you due to
  416. company policy or because the project you're working on already made most choices for
  417. you :-)</li>
  418. </ul>
  419. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-4.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-4.jpeg" />
  420. <p><em>Unrelated photo from our 2025 holiday in Austria: Neufelden station. From a 1991 train
  421. trip. I remembered the valley as being beautiful. As we now do our family holidays by
  422. train, I knew where to go as soon as Austria was chosen as destination.</em></p>
  423. </div>
  424.  
  425.        ]]>
  426.      </content>
  427.  
  428.    </entry>
  429.    
  430.  <entry>
  431.    <title>Pycon NL: from flask to fastapi - William Lacerda</title>
  432.    <link rel="alternate" type="text/html"
  433.          href="http://reinout.vanrees.org/weblog/2025/10/16/3-from-flask-to-fastapi.html" />
  434.      <id>http://reinout.vanrees.org/weblog/2025/10/16/3-from-flask-to-fastapi.html</id>
  435.      <author>
  436.        <name>Reinout van Rees</name>
  437.      </author>
  438.      <published>2025-10-16T00:00:00+01:00</published>
  439.      <updated>2025-10-16T09:50:00+01:00</updated>
  440.  
  441.      
  442.      <category term="python" />
  443.      
  444.      <category term="pycon" />
  445.      
  446.      <category term="django" />
  447.      
  448.  
  449.      <content type="html"><![CDATA[
  450.        <div class="document">
  451. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  452. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  453. <p>Full title: <em>from flask to fastapi: why and how we made the switch</em>.</p>
  454. <p>He works at &quot;polarsteps&quot;, a travel app. Especially a travel app that will be used in
  455. areas with really bad internet connectivity. So performance is top of mind.</p>
  456. <p>They used flask for a long time. Flask 2 added async, but it was still WSGI-bound. They
  457. really needed the async scaling possibility for their 4 million monthly users. Type
  458. hinting was also a big wish item for improved reliability.</p>
  459. <p>They switched to fastapi:</p>
  460. <ul class="simple">
  461. <li>True async support. It is ASGI-native</li>
  462. <li>Typing and validation with pydantic. Pydantic validates requests and responses. Type
  463. hints help a lot.</li>
  464. <li>Native auto-generated docs (openapi). Built-in swagger helps for the frontend team.</li>
  465. </ul>
  466. <p>This meant they gave up some things that Flask provided:</p>
  467. <ul class="simple">
  468. <li>Flask has a mature ecosystem. So they left a big community + handy heap of
  469. stackoverflow answers + lots of ready-made plugins behind.</li>
  470. <li>Integrated command-line dev tools. Flask is handy there.</li>
  471. <li>Simplicity, especially for new devs.</li>
  472. </ul>
  473. <p>They did a gradual migration. So they needed to build a custom fastapi middleware that
  474. could support both worlds. And some api versioning to keep the two code bases apart. It
  475. took <strong>a lot of time</strong> to port everything over.</p>
  476. <p>The middleware was key. Completely async in fastapi. Every request came through here. If
  477. needed, a request would be routed to Flask via wsgi, if possible it would go to the new
  478. fastapi part of the code.</p>
  479. <p>For the migration, they <strong>made a dashboard</strong> of all the endpoints and the traffic
  480. volume. They migrated high-traffic APIs first: early infra validation. Attention to
  481. improvements by checking if the queries were faster. Lots of monitoring of both
  482. performance and errors.</p>
  483. <p>Some lessons learned:</p>
  484. <ul class="simple">
  485. <li>Async adds complexity, but pays off at scale. They started the process with 4 million
  486. users, now they're at 20.</li>
  487. <li>Pydantic typing catches errors early.</li>
  488. <li>Versioned middleware made incremental delivery safe.</li>
  489. <li>Data-driven prioritization (=the dashboard) beats a big-bang rewrite.</li>
  490. <li>AI helps, but hallucinates too much on complex APIs.</li>
  491. </ul>
  492. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-3.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-3.jpeg" />
  493. <p><em>Unrelated photo from our 2025 holiday in Austria: the beautiful 'große Mühl' river
  494. valley.</em></p>
  495. </div>
  496.  
  497.        ]]>
  498.      </content>
  499.  
  500.    </entry>
  501.    
  502.  <entry>
  503.    <title>Pycon NL: typing your python code like a ninja - Thiago Bellini Ribeiro</title>
  504.    <link rel="alternate" type="text/html"
  505.          href="http://reinout.vanrees.org/weblog/2025/10/16/2-typing-python.html" />
  506.      <id>http://reinout.vanrees.org/weblog/2025/10/16/2-typing-python.html</id>
  507.      <author>
  508.        <name>Reinout van Rees</name>
  509.      </author>
  510.      <published>2025-10-16T00:00:00+01:00</published>
  511.      <updated>2025-10-16T09:15:00+01:00</updated>
  512.  
  513.      
  514.      <category term="python" />
  515.      
  516.      <category term="pycon" />
  517.      
  518.      <category term="django" />
  519.      
  520.  
  521.      <content type="html"><![CDATA[
  522.        <div class="document">
  523. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  524. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  525. <p>By now, the basics of <strong>python type hints</strong> are well known:</p>
  526. <pre class="literal-block">
  527. def something(x: int) -&gt; float:
  528.    ...
  529.  
  530. def get_person(name: str, age: int|None) -&gt; Person:
  531.    ...
  532. </pre>
  533. <p><strong>Note: I've tried typing (...) fast enough, but my examples will probably have errors
  534. in them, so check the typing documentation!</strong> His <a class="reference external" href="https://docs.google.com/presentation/d/e/2PACX-1vR-6ZxNN7YC6UGHMIhN-mMSRo8CWfsiPqbOKciQDJWFnxeOUzy3Ot4VyDVT8ZxFvtTPQBMt2WjZWcvf/pub?start=false&amp;loop=false&amp;delayms=3000#slide=id.g1e59c22d2f6_0_0">slides are here</a>
  535. so do check those :-)</p>
  536. <p>Sometimes you can have multiple types for some input. Often the output also changes
  537. then. You can accept both import types and suggest both output types, but with
  538. <tt class="docutils literal">&#64;overload</tt> you can be more specific:</p>
  539. <pre class="literal-block">
  540. from typing import overload
  541.  
  542. &#64;overload
  543. def something(x: str) -&gt; str:
  544.    ...
  545.  
  546. def something(x: int) -&gt; int:
  547.    ...
  548. </pre>
  549. <p>Tyou can do the same with a generic:</p>
  550. <pre class="literal-block">
  551. from typing import TypeVar
  552.  
  553. T = TypeVar(&quot;T&quot;)
  554.  
  555. &#64;overload
  556. def something(x: T) -&gt; T:
  557.    ...
  558.  
  559. # New syntax
  560. def something[T](x: T) -&gt; T:
  561.    ...
  562.  
  563. # Same, but restricted to two types
  564. def something[T: str|int](x: T) -&gt; T:
  565.    ...
  566. </pre>
  567. <p>Generic classes can be handy for, for instance, django:</p>
  568. <pre class="literal-block">
  569. class ModelManager[T: Model]:
  570.    def __init__(self, model_class: type[T]) -&gt; None:
  571.        ....
  572.  
  573.    def get(self, pk: int) -&gt; T:
  574.        ...
  575. </pre>
  576. <p>Type narrowing. Sometimes you accept a broad range of items, but if you return True, it
  577. means the input is of a specific type:</p>
  578. <pre class="literal-block">
  579. from typing import TypeGuard
  580.  
  581. def is_user(obj: Any) -&gt; TypeGuard[User]:
  582.    ....
  583.  
  584. def something(obj: Any):
  585.    if is_user(obj):
  586.        # From here on, typing knows obj is a User
  587. </pre>
  588. <p>Generic <tt class="docutils literal">**kwargs</tt> are a challenge, but there's support for it:</p>
  589. <pre class="literal-block">
  590. from typing import TypedDict, Required, Unpack
  591.  
  592. class SomethingArgs(TypedDict, total-False):
  593.    usernanme: Required(str)
  594.    age: int
  595.  
  596. def something(**kwargs: Unpack[SomethingArgs]):
  597.    ...
  598. </pre>
  599. <p>If you return &quot;self&quot; from some class method, you run into problems with subclasses, as
  600. normally the method says it returns the parent class. You can use <tt class="docutils literal">from typing import
  601. Self` and return the type ``Self</tt> instead.</p>
  602. <p><em>Nice talk, I learned quite a few new tricks!</em></p>
  603. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-2.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-2.jpeg" />
  604. <p><em>Unrelated photo from our 2025 holiday in Austria: church of Neufelden seen on the top
  605. of the hill.</em></p>
  606. </div>
  607.  
  608.        ]]>
  609.      </content>
  610.  
  611.    </entry>
  612.    
  613.  <entry>
  614.    <title>Pycon NL: programming, past and future - Steven Pemberton</title>
  615.    <link rel="alternate" type="text/html"
  616.          href="http://reinout.vanrees.org/weblog/2025/10/16/1-programming-past-future.html" />
  617.      <id>http://reinout.vanrees.org/weblog/2025/10/16/1-programming-past-future.html</id>
  618.      <author>
  619.        <name>Reinout van Rees</name>
  620.      </author>
  621.      <published>2025-10-16T00:00:00+01:00</published>
  622.      <updated>2025-10-16T08:11:00+01:00</updated>
  623.  
  624.      
  625.      <category term="python" />
  626.      
  627.      <category term="pycon" />
  628.      
  629.  
  630.      <content type="html"><![CDATA[
  631.        <div class="document">
  632. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/10/16/index.html">my summaries</a> of
  633. the <a class="reference external" href="https://pycon-nl.org/">Pycon NL one-day conference</a> in Utrecht, NL).</p>
  634. <p>(Note: I've heard a keynote by Steven <a class="reference external" href="https://reinout.vanrees.org/weblog/2016/05/13/8_future_of_programming.html">at pygrunn 2016</a>.)</p>
  635. <p>Steven is in the <a class="reference external" href="https://www.youtube.com/watch?v=GfH4QL4VqJ0">python documentary</a>, he
  636. co-designed the <tt class="docutils literal">abc</tt> programming language that was the predecessor to python. ABC was
  637. a research project that was designed for the programmer's needs. He also was the first
  638. user of the open internet in Europe in November 1988, as the CWI at the university had
  639. the first 64kbps connection in Europe. Co-designer of html, css, xhtml, rdf, etc.</p>
  640. <p>1988, that's 37 years ago. But only about 30 years earlier, the first municipality
  641. (Norwich, UK) got a computer. 21 huge crates. It ran continuously for 10 years. A modern
  642. Raspberry pi would take 5 minutes to do the same work!</p>
  643. <p>Those early computers were expensive: an hour of programming time was a year's salary
  644. for a programmer. So, early programming languages were designed to optimize for the
  645. computer. Nowadays, it is the other way around: computers are almost free and programmers
  646. are expensive. This hasn't really had an effect on the way we program.</p>
  647. <p>He's been working on <strong>declarative</strong> programming languages. One of the declarative
  648. systems is <strong>xforms</strong>, an xml-based declarative system for defining applications. It is
  649. a w3c standard, but you rarely see it mentioned. But quite some companies and government
  650. organisations use it, like the Dutch weather service (KNMI).</p>
  651. <p>The NHS (UK nationwide health service) had a &quot;Lorenzo&quot; system for UK patient records
  652. that cost billions of pounds, took 10 years to build and basically failed. Several
  653. hospitals (and now hospitals in Ukraine!) use an xforms-system written in three years by
  654. a single programmer. Runs, if needed, on a Raspberry pi.</p>
  655. <p>He thinks declarative programming allows programmers to be at least ten times more
  656. productive. He thinks, eventually everyone will program declaratively: fewer errors,
  657. more time, more productivity. (And there's a <a class="reference external" href="http://declarative.amsterdam">small conference in Amsterdam</a> in November).</p>
  658. <img alt="https://reinout.vanrees.org/images/2025/austria-vacation-1.jpeg" src="https://reinout.vanrees.org/images/2025/austria-vacation-1.jpeg" />
  659. <p><em>Unrelated photo from our 2025 holiday in Austria: in Vienna/Wien I visited the military
  660. museum. This is the car in which archduke Franz Ferdinand was shot in Sarajevo in
  661. 1914.</em></p>
  662. </div>
  663.  
  664.        ]]>
  665.      </content>
  666.  
  667.    </entry>
  668.    
  669.  <entry>
  670.    <title>Leiden python meetup: memory graph - Bas Terwijn</title>
  671.    <link rel="alternate" type="text/html"
  672.          href="http://reinout.vanrees.org/weblog/2025/09/04/3-memory-graph.html" />
  673.      <id>http://reinout.vanrees.org/weblog/2025/09/04/3-memory-graph.html</id>
  674.      <author>
  675.        <name>Reinout van Rees</name>
  676.      </author>
  677.      <published>2025-09-04T00:00:00+01:00</published>
  678.      <updated>2025-09-04T20:07:00+01:00</updated>
  679.  
  680.      
  681.      <category term="pun" />
  682.      
  683.      <category term="python" />
  684.      
  685.  
  686.      <content type="html"><![CDATA[
  687.        <div class="document">
  688. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/09/04/index.html">my summaries</a> of
  689. the <a class="reference external" href="https://www.meetup.com/python-leiden-user-group/events/308921156/">fifth Python meetup</a> in Leiden, NL).</p>
  690. <p>Full title of the talk: memory graph: <strong>teaching tool</strong> and <strong>debugging aid</strong> in context
  691. of references, mutable data types, and shallow and deep copy.</p>
  692. <p><a class="reference external" href="https://github.com/bterwijn/memory_graph">memory_graph</a> is a python debugging aid and
  693. teaching tool. It is a modern version of <em>python tutor</em>. (There is an <a class="reference external" href="https://memory-graph.com/#breakpoints=8&amp;continues=1&amp;timestep=1.0&amp;play">online demo</a>)</p>
  694. <p>Python has two categories of types:</p>
  695. <ul class="simple">
  696. <li>Immutable types: bool, int, float, str, tuple, etcetera. They cannot be mutated, so
  697. when a value is changed, a copy is made. If you add an item to a tuple, you get a
  698. <strong>new</strong> tuple with the extra item.</li>
  699. <li>Mutable types: dicts, lists. You can change the values without the actual dict/list
  700. changing. You can add items to a list and you still have the same list object.</li>
  701. </ul>
  702. <p>When you want an actual copy of a mutable type, you need to use <tt class="docutils literal">import copy</tt> and
  703. <tt class="docutils literal">copy.copy(your_list)</tt>. And <tt class="docutils literal">copy.deepcopy()</tt>.</p>
  704. <ul class="simple">
  705. <li><tt class="docutils literal">list2 = list1</tt> is an assignment.</li>
  706. <li><tt class="docutils literal">list2 = copy.copy(list1)</tt> gives you a second, separate, list object, but it points
  707. at the same values inside it as list1.</li>
  708. <li><tt class="docutils literal">list2 = copy.deepcopy(list1)</tt> gives you a second, separate, list object <em>and</em>
  709. separate copies of the values inside it.</li>
  710. </ul>
  711. <p>Watch out with the <tt class="docutils literal">list2 = list1</tt> assignment. When you add an item to list2, it is
  712. also &quot;added&quot; to list1 as it is the same.</p>
  713. <p>He had a couple of simple exercises for us, which were amusingly hard :-)</p>
  714. <p>Apart from the web online demo, there are also integrations for jupyter notebooks and
  715. lots of IDEs. Here's an animated gif from the github repo:</p>
  716. <img alt="https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/vscode_copying.gif" src="https://raw.githubusercontent.com/bterwijn/memory_graph/main/images/vscode_copying.gif" />
  717. </div>
  718.  
  719.        ]]>
  720.      </content>
  721.  
  722.    </entry>
  723.    
  724.  <entry>
  725.    <title>Leiden python meetup: HTMX - Jan Murre</title>
  726.    <link rel="alternate" type="text/html"
  727.          href="http://reinout.vanrees.org/weblog/2025/09/04/2-htmx.html" />
  728.      <id>http://reinout.vanrees.org/weblog/2025/09/04/2-htmx.html</id>
  729.      <author>
  730.        <name>Reinout van Rees</name>
  731.      </author>
  732.      <published>2025-09-04T00:00:00+01:00</published>
  733.      <updated>2025-09-05T07:48:00+01:00</updated>
  734.  
  735.      
  736.      <category term="pun" />
  737.      
  738.      <category term="python" />
  739.      
  740.      <category term="django" />
  741.      
  742.  
  743.      <content type="html"><![CDATA[
  744.        <div class="document">
  745. <p>(One of <a class="reference external" href="https://reinout.vanrees.org/weblog/2025/09/04/index.html">my summaries</a> of
  746. the <a class="reference external" href="https://www.meetup.com/python-leiden-user-group/events/308921156/">fifth Python meetup</a> in Leiden, NL).</p>
  747. <p>The person who invented <a class="reference external" href="https://htmx.org/">htmx</a> (Carson Gross) always begins with
  748. <strong>hypermedia</strong>. <em>Hypermedia is a media that includes non-linear branching from one
  749. location in the media to another, via hyperlinks.</em> HTML is hypermedia, the world's most
  750. succesful hypertext.</p>
  751. <p>Another important term: <a class="reference external" href="https://en.wikipedia.org/wiki/HATEOAS">HATEOAS</a>, <em>hypermedia
  752. as the engine of application state</em>. With hypermedia, state is on the server. So not in
  753. a javascript frontend. With traditional <em>single page apps</em> that you see nowadays, you
  754. only read some json and the frontend needs to know what to do with it. Lots of logic is
  755. on the client. And you get &quot;javascript fatigue&quot;.</p>
  756. <p>With hypermedia, you have server-side rendering. Minimal javascript. Progressive
  757. enhancement. SEO friendly. And... accessible by default. The content you get includes
  758. the behaviour (like a link to delete an item).</p>
  759. <p>HTMX extends HTML with modern interactivity using simple attributes. You can target
  760. specific elements on your page for an update, so you don't need to get a full page
  761. refresh. And you can use any http verb (get/post/put/delete/patch). And you're not
  762. limited to forms and links: any element can trigger a request.</p>
  763. <p>Some attribute examples:</p>
  764. <ul class="simple">
  765. <li><tt class="docutils literal"><span class="pre">hx-get</span></tt> issues a GET request to the server when you click on the element.</li>
  766. <li><tt class="docutils literal"><span class="pre">hx-post</span></tt>, same with POST.</li>
  767. <li><tt class="docutils literal"><span class="pre">hx-target</span></tt>, what you get back from your get/post, where do you want to place it?</li>
  768. <li><tt class="docutils literal"><span class="pre">hx-swap</span></tt>: just replace part of the page.</li>
  769. <li><tt class="docutils literal"><span class="pre">hx-trigger</span></tt>: when to do the request. Based on a click or based on a timer, for
  770. instance.</li>
  771. </ul>
  772. <p>An advantage of HTMX is <strong>maintainability</strong>. Complexity is way lower than a <em>single page
  773. app</em>. Familiar patterns and regular server-side logic. Much simpler. Accessible
  774. (provided you put in some effort).</p>
  775. <p>He showed a nice example with a generated list, a search field and a form. Nice: extra
  776. validation on one of the form fields via a <tt class="docutils literal"><span class="pre">hx-post</span></tt> and a <tt class="docutils literal"><span class="pre">hx-trigger=&quot;on-blur&quot;</span></tt>.</p>
  777. <p>Nice trick for <tt class="docutils literal"><span class="pre">hx-target</span></tt>: you can give it the value of <tt class="docutils literal">&quot;closest
  778. <span class="pre">.some-css-class&quot;</span></tt>, then it finds the closes enclosing element with that class.</p>
  779. <p>Other niceties: <tt class="docutils literal"><span class="pre">hx-indicator</span></tt> enables a spinner upon a POST and disables it once the
  780. POST succeeds. <tt class="docutils literal">&lt;div <span class="pre">hx-boost=&quot;true&quot;&gt;</span></tt> around your content tells HTMX to replace your
  781. whole page's content with the new page, the result is the same as normally, only without
  782. the temporary flicker when loading the page.</p>
  783. <p>HTMX is great for:</p>
  784. <ul class="simple">
  785. <li>CRUD applications.</li>
  786. <li>Content-heavy sites.</li>
  787. <li>Forms and validation.</li>
  788. <li>Server-side rendered apps.</li>
  789. <li>Progressive enhancement.</li>
  790. <li>Moderate interactivity.</li>
  791. </ul>
  792. <p>You can read a book about HTMX here: <a class="reference external" href="https://hypermedia.systems/">https://hypermedia.systems/</a></p>
  793. <p>In response to a question: the author considers htmx to be <strong>complete and
  794. finished</strong>. What works now should work in 20 years. So it will rarely change (unlike
  795. something like react that changes all the time).</p>
  796. </div>
  797.  
  798.        ]]>
  799.      </content>
  800.  
  801.    </entry>
  802.    
  803.  
  804. </feed>

If you would like to create a banner that links to this page (i.e. this validation result), do the following:

  1. Download the "valid Atom 1.0" banner.

  2. Upload the image to your own server. (This step is important. Please do not link directly to the image on this server.)

  3. Add this HTML to your page (change the image src attribute if necessary):

If you would like to create a text link instead, here is the URL you can use:

http://www.feedvalidator.org/check.cgi?url=http%3A//reinout.vanrees.org/weblog/atom.xml

Copyright © 2002-9 Sam Ruby, Mark Pilgrim, Joseph Walton, and Phil Ringnalda