<?xml version='1.0' encoding='utf-8'?>
<feed xml:lang="en" xmlns="http://www.w3.org/2005/Atom"><title>Matthias Kestenholz: Posts about Maintenance</title><link href="https://406.ch/writing/category-maintenance/atom.xml" rel="self" /><link href="https://406.ch/writing/category-maintenance/" rel="alternate" /><id>https://406.ch/writing/category-maintenance/</id><updated>2026-10-21T12:00:00Z</updated><author><name>Matthias Kestenholz</name></author><entry><title>How I decide which Python and Django versions I want to support</title><link href="https://406.ch/writing/how-i-decide-which-python-and-django-versions-i-want-to-support/" rel="alternate" /><id>https://406.ch/writing/how-i-decide-which-python-and-django-versions-i-want-to-support/</id><published>2026-10-21T12:00:00Z</published><updated>2026-10-21T12:00:00Z</updated><summary type="html">&lt;h1 id="how-i-decide-which-python-and-django-versions-i-want-to-support"&gt;&lt;a class="toclink" href="#how-i-decide-which-python-and-django-versions-i-want-to-support"&gt;How I decide which Python and Django versions I want to support&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A recent post on Mastodon got me thinking about what my philosophy for dropping
support for old versions of Python and Django is. Here’s the post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;🚨 Important Announcement 🚨&lt;/p&gt;
&lt;p&gt;Going forward all my packages will only “officially” support maintained #Python versions. Meaning since October only 3.14 and now 3.15.&lt;/p&gt;
&lt;p&gt;This has no effect on Django version support.&lt;/p&gt;
&lt;p&gt;CVEs will be backported to older 🐍 releases.&lt;/p&gt;
&lt;p&gt;– &lt;a href="https://fosstodon.org/@codingjoe/117418591209854460"&gt;@codingjoe&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I tend to support versions long past their &lt;a href="https://pypi.org/project/norwegianblue/"&gt;End of
Life&lt;/a&gt; date. I jokingly said that I
should spend time upgrading old websites instead of spending time writing a
rationale why not. I don’t want to argue for my position, but maybe this post
will serve as the place to point people towards when the question arises again
in the future.&lt;/p&gt;
&lt;p&gt;The short answer: it depends on whether I’m working on a project, on my own
machine or on a library.&lt;/p&gt;
&lt;h2 id="projects"&gt;&lt;a class="toclink" href="#projects"&gt;Projects&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When working on projects I’m always updating to the current stable version or even the bleeding edge. I want the projects to be as future-proof as possible and I also want to give &lt;a href="/writing/weeknotes-2023-week-39/#adding-scheduled-tests"&gt;early feedback when stuff breaks&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As long as the server software allows it, I’m just going to use &lt;a href="https://docs.astral.sh/uv/"&gt;uv&lt;/a&gt; to install the newest Python version that all the packages I’m using support (or at least ship binary wheels for), because I enjoy not compiling software when deploying too damn much.&lt;/p&gt;
&lt;h2 id="developer-workstations"&gt;&lt;a class="toclink" href="#developer-workstations"&gt;Developer workstations&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When it comes to developer workstations I don’t really have the patience for outdated software. I don’t see a reason why developer tooling can’t be up to date. This came up recently in the &lt;a href="https://github.com/django-commons/django-debug-toolbar/issues/2506"&gt;django-debug-toolbar issue tracker&lt;/a&gt;. I’m totally fine with requiring an up-to-date version of Python for our pre-commit hooks, for example.&lt;/p&gt;
&lt;h2 id="libraries"&gt;&lt;a class="toclink" href="#libraries"&gt;Libraries&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The more foundational and widely used a library (potentially) is, the more important it is to me that it supports basically everything which is reasonable.&lt;/p&gt;
&lt;p&gt;I’m still using my libraries in older projects myself, so they have to work there. I want to use them in all environments, at least when I’m not paying a big price in terms of language features.&lt;/p&gt;
&lt;p&gt;Other people may be stuck on older versions too. People can upgrade their software, but there may be situations (for example in managed hosting environments) where people cannot freely update the underlying managed server software. That isn’t necessarily a problem security-wise as long as there are other measures in place protecting those services.&lt;/p&gt;
&lt;p&gt;When people use my libraries, I might get additional eyes on the code, and possibly even contributions.&lt;/p&gt;
&lt;p&gt;Supporting a large range of Django versions, for example 3.2 (reached EOL on 2024-04-01) up to the current main development branch, can be surprisingly easy when you do not reach too much into Django’s internals or depend on features added only recently.&lt;/p&gt;
&lt;p&gt;That said, when do I actually drop support for an old version of Python or Django? Generally speaking, I have two reasons:&lt;/p&gt;
&lt;h3 id="keeping-ci-time-down"&gt;&lt;a class="toclink" href="#keeping-ci-time-down"&gt;🌳 Keeping CI time down 🌳&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I like pruning the CI matrix so as to not spend too much compute and energy. The exceptions are packages such as django-debug-toolbar and django-tree-queries: they are widely used and have to work on a wide range of Python and Django versions and database engines, so the additional CI runs are worth it.&lt;/p&gt;
&lt;p&gt;When I remove a version from the CI matrix I mostly raise the minimum version in the package metadata as well. I almost never use upper bounds for version ranges, but lower bounds I definitely do use.&lt;/p&gt;
&lt;h3 id="using-new-features-either-because-i-want-them-or-because-i-need-them"&gt;&lt;a class="toclink" href="#using-new-features-either-because-i-want-them-or-because-i-need-them"&gt;Using new features, either because I want them or because I need them&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes, I just really want to merge dictionaries using the nice &lt;a href="https://github.com/feincms/feincms3-forms/commit/2a758a4e4c34d1118d5e20f42a13c47ce601b029"&gt;&lt;code&gt;a | b&lt;/code&gt; syntax&lt;/a&gt;. If this reasoning sounds whimsical to you, it is, but only partially. Programming cannot just be a job, everybody needs to have fun sometimes.&lt;/p&gt;
&lt;p&gt;Sometimes, I really need some API or behavior Django has only introduced recently, such as the way bound fields work in forms. For example, &lt;a href="https://github.com/mosparo/django-integration/pull/58"&gt;form validation didn’t go through bound fields before 4.0&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="what-that-means-right-now"&gt;&lt;a class="toclink" href="#what-that-means-right-now"&gt;What that means right now&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Most of my packages currently require Python 3.10 or newer and Django 3.2 or 4.2 or newer. Some of the less actively developed ones still go back as far as Python 3.8, simply because I haven’t had a reason to touch them. Python and Django support are linked: Django 4.2 supports Python 3.8 to 3.12 while Django 6.0 requires Python 3.12, so the oldest Django version I support pretty much decides the oldest Python version worth testing.&lt;/p&gt;
&lt;h2 id="what-is-the-downside"&gt;&lt;a class="toclink" href="#what-is-the-downside"&gt;What is the downside?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Supporting old versions makes it easier for people to put off upgrading. Some people just don’t want to upgrade at all, and my libraries continuing to work on old versions certainly doesn’t push them to do it. When they suddenly have to because all hell breaks loose (because of security problems or something like that) it’s so much harder: they have a long way to go until they reach supported versions. That said, in my experience that’s not a big problem with Django at all. On the other hand, when the projects still use npm packages from 2015… oh my.&lt;/p&gt;
&lt;p&gt;The code is also less clean than it could be. Supporting old versions means compatibility shims, and it means not using some of the nicer features newer versions offer.&lt;/p&gt;
&lt;p&gt;In the end, both approaches are reasonable, and both have their costs. You should do what brings you more joy.&lt;/p&gt;
</summary></entry><entry><title>«Anything new?»</title><link href="https://406.ch/writing/anything-new/" rel="alternate" /><id>https://406.ch/writing/anything-new/</id><published>2026-06-03T12:00:00Z</published><updated>2026-06-03T12:00:00Z</updated><summary type="html">&lt;h1 id="anything-new"&gt;&lt;a class="toclink" href="#anything-new"&gt;«Anything new?»&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A lot of time has passed since I officially announced that I want to &lt;a href="https://406.ch/writing/weeknotes-2021-week-10/#django-mptt-is-not-maintained-anymore"&gt;step down from maintaining django-mptt&lt;/a&gt;. I started contributing around 2009, tagged the &lt;a href="https://github.com/django-mptt/django-mptt/tree/0.3.0"&gt;0.3 release in April 2010&lt;/a&gt;, and have been the sole active maintainer since somewhere around 2019. The post about &lt;a href="https://406.ch/writing/django-tree-queries/"&gt;django-tree-queries&lt;/a&gt; has more background, but that’s not today’s topic.&lt;/p&gt;
&lt;h2 id="stepping-away-isnt-easy"&gt;&lt;a class="toclink" href="#stepping-away-isnt-easy"&gt;Stepping away isn’t easy&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For me, abandoning a project is a bit like stepping out of a relationship: negative emotions end up being a somewhat necessary driver, because the absence of positive events alone rarely provides enough force on its own. I get a lot of satisfaction from a job well done, and walking away means letting that go.&lt;/p&gt;
&lt;p&gt;Even with time set aside for open source in my work day, I still have to choose where that time goes. django-mptt stopped being where it needed to go.&lt;/p&gt;
&lt;h2 id="the-sense-of-entitlement"&gt;&lt;a class="toclink" href="#the-sense-of-entitlement"&gt;The sense of entitlement&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a project is obviously unmaintained, asking for free labor is walking a tightrope. It takes real care not to rekindle exactly the frustrations that led maintainers away in the first place.&lt;/p&gt;
&lt;p&gt;It takes energy not to clap back when someone is being rude or insensitive in the issue tracker. Asking “Anything new?” on a ticket where the next steps were outlined clearly and obviously nothing happened in the meantime is just one variant of this.&lt;/p&gt;
&lt;p&gt;Quietly quitting isn’t what I want to do — and as a user of django-mptt myself, I can’t really do that either. Taking the high road is the professional choice. But it costs something.&lt;/p&gt;
&lt;p&gt;I keep coming back to &lt;a href="https://youtu.be/Amj3QG2s1BI?t=226"&gt;Mona Eltahawy on refusing to be civil&lt;/a&gt;. She’s speaking about something quite different, and I’m aware I write this as a white man. The situations aren’t the same at all. But she articulated something I haven’t managed to put into words as well myself and I like the idea of speaking up and taking the fight to those who awaken these feelings instead of taking the high road.&lt;/p&gt;
&lt;h2 id="doing-it-with-ai"&gt;&lt;a class="toclink" href="#doing-it-with-ai"&gt;Doing it with AI&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;No post these days is complete without the obligatory AI mention, but there’s some relevancy to it.&lt;/p&gt;
&lt;p&gt;I fixed and closed almost all open django-mptt issues in a two-hour Claude session. I’ve previously written about using &lt;a href="https://406.ch/writing/llms-for-open-source-maintenance-a-cautious-case/"&gt;LLMs for open source maintenance&lt;/a&gt;, and the productivity gain is real whatever the detractors say. And the quality isn’t suddenly getting worse. Code wasn’t perfect before either. The test suite allows a certain degree of trust in the result and according to &lt;a href="https://406.ch/writing/my-rules-for-releasing-open-source-software/"&gt;my rules for releasing Open Source software&lt;/a&gt; we don’t have to require more than that.&lt;/p&gt;
&lt;p&gt;It doesn’t change the underlying dynamic, though. &lt;a href="https://medium.com/@tridge60/rsync-and-outrage-d9849599e5a0"&gt;rsync and outrage&lt;/a&gt; illustrates the trap neatly: Tridgell got flooded with AI-generated security reports, used AI to handle them, and then got criticized for using AI. The tools that created the workload aren’t allowed to address it. The expectation is that the work has to involve sweat and tears and uncountable unpaid hours.&lt;/p&gt;
&lt;p&gt;The common goal should be more and better open source software. What we get as Open Source maintainers is shit from both sides: One side took our free work and trained models on it without asking, the other side complains about the supposedly unethical use of AI while acting in unethical ways themselves.&lt;/p&gt;
&lt;p&gt;There’s something Kantian about how open source contribution gets framed. &lt;a href="https://en.wikipedia.org/wiki/Immanuel_Kant"&gt;Kant&lt;/a&gt;’s argument was that the only truly moral acts are those driven by duty and good will — not by desire, inclination, or any expectation of compensation. By that logic, I’m only acting morally if I keep going despite the burnout and the entitlement. If I stop, I’m not.&lt;/p&gt;
&lt;p&gt;It’s bleak. The problems with AI are real. The people controlling the large models are assholes. But I have to work in the world as it is while also trying to change it for the better.&lt;/p&gt;
</summary></entry><entry><title>LLMs for Open Source maintenance: a cautious case</title><link href="https://406.ch/writing/llms-for-open-source-maintenance-a-cautious-case/" rel="alternate" /><id>https://406.ch/writing/llms-for-open-source-maintenance-a-cautious-case/</id><published>2026-03-25T12:00:00Z</published><updated>2026-03-25T12:00:00Z</updated><summary type="html">&lt;h1 id="llms-for-open-source-maintenance-a-cautious-case"&gt;&lt;a class="toclink" href="#llms-for-open-source-maintenance-a-cautious-case"&gt;LLMs for Open Source maintenance: a cautious case&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When ChatGPT appeared on the scene I was very annoyed at all the hype surrounding it. Since I’m working in the fast moving and low margin business of communication and campaigning agencies I’m surrounded by people eager to jump on the hype train when a tool promises to lessen the workload and take stuff from everyone’s plate.&lt;/p&gt;
&lt;p&gt;These discussions coupled with the fact that the training of these tools required unfathomable amounts of &lt;strong&gt;stealing&lt;/strong&gt; were the reason for a big reluctance on my part when trying them out. I’m using the word stealing here on purpose, since that’s exactly the crime &lt;a href="https://en.wikipedia.org/wiki/Aaron_Swartz"&gt;Aaron Swartz&lt;/a&gt; &lt;a href="https://web.archive.org/web/20120526080523/http://www.justice.gov/usao/ma/news/2011/July/SwartzAaronPR.html"&gt;was accused of by the attorney’s office of the district of Massachusetts&lt;/a&gt;. It’s frustrating that some people can get away with the same crime when it is so much bigger. For example, OpenAI and Anthropic downloaded much more data than Aaron ever did.&lt;/p&gt;
&lt;p&gt;A somewhat related thing happened with the too-big-to-fail banks: There, the people at the top were even compensated with &lt;a href="https://en.wikipedia.org/wiki/Golden_parachute"&gt;golden parachutes&lt;/a&gt; at the end. LLM companies seem to be above accountability too.&lt;/p&gt;
&lt;p&gt;Despite all this, I have slowly started integrating these tools into my workflows. I don’t remember the exact point in time, but since some time in 2025 my opinions on their utility has started to change. At the beginning, I always removed the attribution and took great care to write and rewrite the code myself, only using the LLMs for inspiration and maybe to generate integration tests. More and more I have to admit that they are useful, especially in time constrained projects with a clear focus and purpose.&lt;/p&gt;
&lt;p&gt;Last month I fixed and/or closed all open issues in the &lt;a href="https://github.com/feincms/django-tree-queries"&gt;django-tree-queries&lt;/a&gt; repository with the help of Claude Code. Is that a good thing? It could be argued I should have done the work myself. But I wouldn’t have — I have other things I want to do with my time. I don’t want to (always) work on Open Source software in the evening. I definitely also have leaned heavily on LLMs when working on &lt;a href="https://github.com/feincms/django-prose-editor"&gt;django-prose-editor&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="is-faster-better"&gt;&lt;a class="toclink" href="#is-faster-better"&gt;Is faster better?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We can produce more code, more features and close tickets faster than before. In my experience the speed up isn’t as big as some people may want us to believe, but it’s there. And contrary to what people in my LinkedIn feed say, that’s not an obviously good thing. Is it a race to the bottom where we drown in LLM-generated slop in quantities impossible to maintain? It doesn’t feel like that — but it’s a race that could go both ways. Throwaway code can be thrown away though, and well tested code does what the tests say, which is good enough according to my &lt;a href="https://406.ch/writing/my-rules-for-releasing-open-source-software/"&gt;rules for releasing open source software&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Speaking as someone who has put more into the training set than they’ve taken out so far, I don’t feel all that bad using the tools. Coding agents can already be run locally with reasonable hardware requirements, at least during inference, which is where the ongoing cost sits. Maybe using them is still rationalization. But contribution and profit needing to stay in some rough balance feels like the right frame. Total abstinence isn’t the only ethical choice we have.&lt;/p&gt;
&lt;h2 id="community-tensions"&gt;&lt;a class="toclink" href="#community-tensions"&gt;Community tensions&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What makes me less comfortable is how communities are reacting. There are real concerns within the Django world, and not just the practical one of overworked maintainers wading through hastily generated patches that don’t actually fix anything. The deeper worry is about the communal nature of contribution: that working on Django is supposed to be a learning experience, a way into the community, and that using an LLM as a vehicle rather than a tool hollows out that process. Reviewers end up interacting with what is essentially a facade, unable to tell whether anyone actually understood the problem. That’s a real concern and I don’t want to dismiss it.&lt;/p&gt;
&lt;p&gt;But it maps onto a different situation from what I’ve been describing. Using Claude Code to close issues in projects I maintain and understand is not the same as using it to paper over gaps in comprehension on a ticket in someone else’s project. Whether LLM-assisted contributions to Django itself are appropriate is a difficult question; whether it’s appropriate to use them when maintaining your own software less so.&lt;/p&gt;
&lt;p&gt;There’s also a harder tension around quality. Django’s conservatism has real value: rigorous review, minimal magic, a coherent philosophy. The ORM and template system don’t need to reinvent themselves, they work well, are still evolving while staying rock-solid for all my use cases. And reading the release notes always brings me joy. But it could be more exciting more often. Quality isn’t a strictly positive thing. Everything has costs. It’s not great if the price of the bar is that legitimate bugs sit open for years because nobody has a few evenings to spend on them. It happened with django-tree-queries before I went through it with Claude Code. I think the bar for contributing to Django is too high. I would value a little more motion and a little less stability, even as someone running dozens of Django websites and apps.&lt;/p&gt;
&lt;p&gt;Then there’s the pile-on dynamic that plays out on Mastodon and GitHub. When the Harfbuzz and chardet maintainers disclosed LLM usage, the reaction from some corners was something to behold. People expressing what amounted to personal grievance over tooling choices in projects they may not even use. There’s a particular kind of entitlement in telling a maintainer – who is keeping software alive, possibly even in their spare time – that the way they choose to do that work is an affront. Open source is a gift, whether paid or not, and nobody has to accept it, but disclosing your tooling isn’t an invitation for complaints. The ethical concerns about training data, resource use and other negative externalities are legitimate and worth raising. Performative outrage directed at individual maintainers is not the same thing.&lt;/p&gt;
&lt;p&gt;I don’t have an easy conclusion. The tools are useful, the ethics are murky, and communities are still figuring out how to respond. A cautious, honest use of them feels better to me than the alternatives.&lt;/p&gt;
</summary></entry><entry><title>Weeknotes (2026 week 11)</title><link href="https://406.ch/writing/weeknotes-2026-week-11/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2026-week-11/</id><published>2026-03-11T12:00:00Z</published><updated>2026-03-11T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2026-week-11"&gt;&lt;a class="toclink" href="#weeknotes-2026-week-11"&gt;Weeknotes (2026 week 11)&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Last time I wrote that I seem to be publishing weeknotes monthly. Now, a quarter of a year has passed since the &lt;a href="https://406.ch/writing/weeknotes-2025-week-49/"&gt;last entry&lt;/a&gt;. I do enjoy the fact that I have published more posts focused on a single topic. That said, what has been going on in open source land is certainly interesting too.&lt;/p&gt;
&lt;h2 id="llms-in-open-source"&gt;&lt;a class="toclink" href="#llms-in-open-source"&gt;LLMs in Open Source&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have started a longer piece to think about my stance regarding using LLMs in Open Source. The argument I’m thinking about is that there’s a balance between LLMs having ingested all of my published open source code and myself using them now to help myself and others again.&lt;/p&gt;
&lt;p&gt;The happenings in the last two weeks (think Pentagon, Iran, and the bombings of schools) have again brought to the foreground the perils of using those tools. I therefore haven’t been motivated to pursue this train of thought for the moment. When the upsides are somewhat questionable and tentative and the downsides are so clear and impossible to miss, it’s hard to use my voice to speak in favor of these tools.&lt;/p&gt;
&lt;p&gt;That said, all the shaming when someone uses an LLM that I see in my Mastodon feed also annoys me. I’ll quote part of a post here which I liked and leave it at that for the moment:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The AI hype-cyclone is bad, but so is the anti-AI witch hunt. Commits co-authored by Claude do not mean that a project has “abandoned engineering as a serious endeavor”&lt;/p&gt;
&lt;p&gt;[…]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;– &lt;a href="https://hachyderm.io/@nedbat/116133445557306539"&gt;@nedbat on Mastodon&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="other-goings-on"&gt;&lt;a class="toclink" href="#other-goings-on"&gt;Other goings-on&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Health:&lt;/strong&gt; My back continues to improve. Some days are still bad, but the idea that the &lt;a href="https://406.ch/writing/my-2025-in-review/#sports-and-health"&gt;herniation&lt;/a&gt; may go away entirely doesn’t sound totally unreasonable anymore.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gardening:&lt;/strong&gt; We started weeding the garden last week. Lots to do! Being outside is fun. Weeding isn’t the greatest part ever, but it’s meditative.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="releases-since-december"&gt;&lt;a class="toclink" href="#releases-since-december"&gt;Releases since December&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-json-schema-editor/"&gt;django-json-schema-editor 0.12.1&lt;/a&gt;: CSS fixes. I have again looked at other, more modern JSON schema editor implementations but all of them are more limited than is acceptable to act as a replacement.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-debug-toolbar/"&gt;django-debug-toolbar 6.2&lt;/a&gt;: I haven’t done much work here! Just some reviewing.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-content-editor/"&gt;django-content-editor 8.1&lt;/a&gt;: Started emitting warnings when using non-abstract base classes for plugins. Using multi table inheritance is mostly an accident and not intended in my experience when using django-content-editor, therefore we have started detecting this case and emitting system checks (warnings, not errors).&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-imagefield/"&gt;django-imagefield 0.23.0a3&lt;/a&gt;: We have done some work on supporting &lt;a href="https://www.libvips.org/"&gt;libvips&lt;/a&gt; as an alternative backend to Pillow because I hoped that memory usage in Kubernetes pods might go down a bit. Results are not conclusive yet, and I’m not yet convinced the additional code complexity is worth it. Debugging and monitoring continues.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/FeinCMS/"&gt;FeinCMS 26.2.1&lt;/a&gt;: Released a few bugfixes. FeinCMS is still being maintained ~17 years later!&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-auto-admin-fieldsets/"&gt;django-auto-admin-fieldsets 0.3&lt;/a&gt;: Added a helper to remove fields from the fieldsets structure.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-tree-queries/"&gt;django-tree-queries 0.23.1&lt;/a&gt;: Shipped a small bugfix for &lt;code&gt;{% recursetree %}&lt;/code&gt; which unintentionally cached children across invocations.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3-downloads/"&gt;feincms3-downloads&lt;/a&gt;: Used &lt;code&gt;PATH&lt;/code&gt; from the environment instead of using a very restricted allowlist so that &lt;code&gt;convert&lt;/code&gt; and &lt;code&gt;pdftocairo&lt;/code&gt; are detected in more locations. This should help with local development for example on macOS.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-prose-editor/"&gt;django-prose-editor 0.24.1&lt;/a&gt;: Read the &lt;a href="https://django-prose-editor.readthedocs.io/en/latest/changelog.html"&gt;CHANGELOG&lt;/a&gt;; there’s too much in there for a short notice.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/form-designer/"&gt;form-designer 0.27.3&lt;/a&gt;: Mosparo captcha support, bugfixes and additional translations.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3/"&gt;feincms3 5.5&lt;/a&gt;: Started using the &lt;code&gt;OrderableTreeNode&lt;/code&gt; from django-tree-queries.&lt;/li&gt;
&lt;/ul&gt;
</summary></entry><entry><title>How I handle versioning</title><link href="https://406.ch/writing/how-i-handle-versioning/" rel="alternate" /><id>https://406.ch/writing/how-i-handle-versioning/</id><published>2024-08-20T12:00:00Z</published><updated>2024-08-20T12:00:00Z</updated><summary type="html">&lt;h1 id="how-i-handle-versioning"&gt;&lt;a class="toclink" href="#how-i-handle-versioning"&gt;How I handle versioning&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I have been reading up on versioning methods a bit, and I noticed that I never
shared my a bit unorthodox versioning method. I previously wrote about my &lt;a href="https://406.ch/writing/my-rules-for-releasing-open-source-software/"&gt;rules for releasing open source software&lt;/a&gt; but skipped everything related to versioning.&lt;/p&gt;
&lt;p&gt;I use something close to the ideas of &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt; but it’s not quite that. Note that the versioning scheme has nothing to do with production readiness, it’s more about communicating the state of the project.&lt;/p&gt;
&lt;h2 id="00x-everything-breaks"&gt;&lt;a class="toclink" href="#00x-everything-breaks"&gt;0.0.x – Everything breaks&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I don’t trust my choices a lot. Everything may radically change, and I may also abandon the project with no hard feelings.&lt;/p&gt;
&lt;h2 id="010-changelogging"&gt;&lt;a class="toclink" href="#010-changelogging"&gt;0.1.0 – Changelogging&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I generally start writing release notes or rather a &lt;a href="https://en.wikipedia.org/wiki/Changelog"&gt;Changelog&lt;/a&gt;, since writing full release notes is much more work. You absolutely have to test the software and I do not guarantee anything related to backwards compatibility – just that you’ll know about breaking changes when you read the CHANGELOG.&lt;/p&gt;
&lt;h2 id="x-bugfixes-and-pure-additions"&gt;&lt;a class="toclink" href="#x-bugfixes-and-pure-additions"&gt;?.?.x – Bugfixes and pure additions&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Strictly speaking, patch version increments are only for bugfixes. However, when I add new features which are purely additional to the package or if there’s no way (famous last words) that anything will break, I’ll upload a patch release.&lt;/p&gt;
&lt;p&gt;Sometimes it might be better to increment the minor version, yes. But, being very lazy, I sometimes only want to add a line to the CHANGELOG instead of adding a new heading for the new release. Also, if I don’t quickly release new additions there’s a real danger that I’ll only come back to the project weeks or months later, and I don’t want anyone (myself included) waiting on these updates. Also, if I inadvertently introduced new bugs it’s better to fix them quickly instead of forgetting about it first and having to rediscover the buggy code later.&lt;/p&gt;
&lt;h2 id="100-im-happy"&gt;&lt;a class="toclink" href="#100-im-happy"&gt;1.0.0 – I’m happy&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’m happy with the package and I commit to start using semver more properly. If I have to (or want to) break backwards compatibility you get a migration path, especially if I need it myself. Which is often the case since I’m maintaining dozens of Django-based websites and webapps, and I’m always &lt;a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food"&gt;dogfooding&lt;/a&gt; my stuff.&lt;/p&gt;
&lt;p&gt;The pure additions case from the last section still applies, since slapping a 1.0 version on something doesn’t suddenly make me less lazy.&lt;/p&gt;
&lt;h2 id="outliers"&gt;&lt;a class="toclink" href="#outliers"&gt;Outliers&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/feincms/feincms"&gt;FeinCMS&lt;/a&gt; uses a variant of &lt;a href="https://calver.org/"&gt;calendar versioning&lt;/a&gt;; the version at the time of writing is 24.8.2 which means the second release in August 2024.&lt;/p&gt;
&lt;p&gt;There are probably other outliers I forgot about.&lt;/p&gt;
</summary></entry><entry><title>Weeknotes (2023 week 44)</title><link href="https://406.ch/writing/weeknotes-2023-week-44/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2023-week-44/</id><published>2023-11-02T12:00:00Z</published><updated>2023-11-02T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2023-week-44"&gt;&lt;a class="toclink" href="#weeknotes-2023-week-44"&gt;Weeknotes (2023 week 44)&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="unmaintained-but-maintained-packages"&gt;&lt;a class="toclink" href="#unmaintained-but-maintained-packages"&gt;Unmaintained but maintained packages&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There’s a discussion going on in the &lt;a href="https://github.com/django-mptt/django-mptt/issues/833"&gt;django-mptt issue tracker&lt;/a&gt; about the maintenance state of django-mptt. &lt;a href="https://github.com/django-mptt/django-mptt/commit/6f6c1c485f3adc1d579f8d22e0279ce1d52334f6"&gt;I have marked the project as unmaintained in March 2021&lt;/a&gt; and haven’t regretted this decision at all. I haven’t had to fix &lt;a href="https://github.com/django-mptt/django-mptt/labels/Broken%20Tree"&gt;inconsistencies in the tree structure&lt;/a&gt; once since switching to &lt;a href="https://406.ch/writing/django-tree-queries/"&gt;django-tree-queries&lt;/a&gt;. And if that wasn’t enough, I get little but only warm and thankful feedback for the latter, so that’s extra nice.&lt;/p&gt;
&lt;p&gt;Despite marking django-mptt as unmaintained I seem to be doing a little bit of maintenance still. I’m still using it in old paid projects and so the things I do to make the package work for me is paid work. I’m not personally invested in the package anymore, so I’m able to tell people that there are absolutely no guarantees about the maintenance, and that feels good.&lt;/p&gt;
&lt;h2 id="read-the-docs"&gt;&lt;a class="toclink" href="#read-the-docs"&gt;Read the Docs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I do understand why the &lt;code&gt;.readthedocs.yaml&lt;/code&gt; file is now necessary. I wish that I wouldn’t have to do all the busywork of adding one to projects. I have just resubscribed to the Read the Docs Gold Membership which probably has expired at some point in the past. Read the Docs is excellent and everybody who can should support them.&lt;/p&gt;
&lt;h2 id="releases"&gt;&lt;a class="toclink" href="#releases"&gt;Releases&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3/"&gt;feincms3 4.5&lt;/a&gt;, &lt;a href="https://pypi.org/project/feincms3-sites/"&gt;feincms3-sites 0.20&lt;/a&gt; and &lt;a href="https://github.com/feincms/feincms3-language-sites"&gt;feincms3-language-sites 0.3&lt;/a&gt;: Fixed the check which only allows adding an application through the CMS to the page tree (yes, that’s right) once; feincms3 worked fine, feincms3-language-sites by accident but feincms3-sites didn’t.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/towel/"&gt;towel 0.31&lt;/a&gt;: Towel is one of my oldest packages which is still being used in real-world projects. Towel is a tool for building CRUD-type applications and is designed to keep you DRY while doing that. The project has been heavily inspired by a Django-based agency software I built many years back. The package even has &lt;a href="https://towel.readthedocs.io/en/latest/"&gt;docs&lt;/a&gt;! I’m still quite proud of the mostly transparent support for multitenancy, but apart from that I haven’t used it in many new projects.&lt;/li&gt;
&lt;/ul&gt;
</summary></entry><entry><title>Weeknotes (2023 week 39)</title><link href="https://406.ch/writing/weeknotes-2023-week-39/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2023-week-39/</id><published>2023-09-28T12:00:00Z</published><updated>2023-09-28T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2023-week-39"&gt;&lt;a class="toclink" href="#weeknotes-2023-week-39"&gt;Weeknotes (2023 week 39)&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Again a few weeks have passed since the last weeknotes entry :-)&lt;/p&gt;
&lt;h2 id="moving-feincms3-repositories-into-the-feincms-organization"&gt;&lt;a class="toclink" href="#moving-feincms3-repositories-into-the-feincms-organization"&gt;Moving feincms3 repositories into the feincms organization&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/feincms"&gt;feincms&lt;/a&gt; GitHub organization has seen more
active days when FeinCMS 1.x was still actively developed. Since my interest
has moved to feincms3 some years ago I haven’t kept the organization up to
date. That has changed this week, and I have moved most feincms3-related
repositories into the organization.&lt;/p&gt;
&lt;p&gt;This move doesn’t change much though, but it certainly feels more official now.&lt;/p&gt;
&lt;h2 id="adding-scheduled-tests"&gt;&lt;a class="toclink" href="#adding-scheduled-tests"&gt;Adding scheduled tests&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have started using the cronjob schedule feature of GitHub actions to ensure that tests run at least once a month in a few important projects. I want to get notified of changes in Django@main affecting my packages not only when actively working on them. I try to keep up with Django@main in all packages I maintain.&lt;/p&gt;
&lt;h2 id="releases"&gt;&lt;a class="toclink" href="#releases"&gt;Releases&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3-sites/"&gt;feincms3-sites 0.18.2&lt;/a&gt;: Many releases in the last weeks. Stopped using permanent redirects in DEBUG mode. Avoid migrations when Django adds more languages. Added utilities which allow restricting model relations to objects in the same site (trickier than it sounds). Added utilities for building full URLs to other sites without taxing the database as much.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3-language-sites/"&gt;feincms3-language-sites 0.2.0&lt;/a&gt;: No biggie. No permanent redirects in DEBUG mode anymore.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3-cookiecontrol/"&gt;feincms3-cookiecontrol 1.4.5&lt;/a&gt;: Reduced the byte size of the CSS and JavaScript some more. Added spanish translations.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-authlib/"&gt;django-authlib 0.16.3&lt;/a&gt;: I have published a post last week describing the new &lt;a href="https://406.ch/writing/keep-content-managers-admin-access-up-to-date-with-role-based-permissions/"&gt;role-based permissions feature&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-imagefield/"&gt;django-imagefield 0.17&lt;/a&gt;: The &lt;code&gt;process_imagefields&lt;/code&gt; management command now allows specifying globs. If you wanted to prerender all imagefields in the pages app you can use &lt;code&gt;./manage.py process_imagefields pages.*&lt;/code&gt; now instead of listing all image fields’ labels explicitly.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/feincms3/"&gt;feincms3 4.4.1&lt;/a&gt;: I’m enormously unhappy but I had to go back to the classic CKEditor instead of using the inline editor. The latter looked much nicer but overriding the Django admin CSS was very very painful. Also, I can totally understand why CKEditor 5 is completely different and why CKEditor 4 is only maintained in a paid LTS plan. It still is making me look for alternatives.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/django-mptt/"&gt;django-mptt 0.15&lt;/a&gt;: I unfortunately am still using this despite the fact that I have marked it as officially unmaintained since march 2021. I did a mediocre job of making the library run on Django@main again. Parts of the library do not work, but since I’m not using them I don’t care too much. I’m still wondering if someone wants to take over maintenance of the library since it still seems to be actively used in projects of others as well. When I don’t have to use django-mptt I’m still really happy with &lt;a href="https://406.ch/writing/django-tree-queries/"&gt;django-tree-queries&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pypi.org/project/form-designer/"&gt;form-designer 0.22&lt;/a&gt;: This is probably my oldest actively developed project these days. 13 years! (Except for django-content-editor of course.) I have modernized the package, switched to hatchling and put out a new release.&lt;/li&gt;
&lt;/ul&gt;
</summary></entry><entry><title>Weeknotes (2023 week 16)</title><link href="https://406.ch/writing/weeknotes-2023-week-16/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2023-week-16/</id><published>2023-04-21T12:00:00Z</published><updated>2023-04-21T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2023-week-16"&gt;&lt;a class="toclink" href="#weeknotes-2023-week-16"&gt;Weeknotes (2023 week 16)&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id="experiments-with-stable-diffusion"&gt;&lt;a class="toclink" href="#experiments-with-stable-diffusion"&gt;Experiments with Stable Diffusion&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A friend and myself threw a few scripts together to automatically finetune a Stable Diffusion model using images downloaded from Google Image search. It’s terrifying how easy and fast generating fake news including photorealistic images can be and will be already. And seeing how fast those models improve it’s just a matter of time until we can trust photos even less than now. Manipulating images has been possible for a long time of course, but it hasn’t been a “commodity” until now.&lt;/p&gt;
&lt;p&gt;I definitely also see upsides in the new machine learning technologies but I fear that there’s a real danger to trust, and in extension to democracy.&lt;/p&gt;
&lt;p&gt;This technology and what we did will be a part of an upcoming &lt;a href="https://www.srf.ch/play/tv/sendung/kulturplatz?id=d70e9bb9-0cee-46b6-8d87-7cbd8317a9c7"&gt;SRF Kulturplatz&lt;/a&gt; broadcast, or so I hope. It’s high time that the public knows what’s possible. It’s about &lt;strong&gt;media literacy&lt;/strong&gt; really.&lt;/p&gt;
&lt;p&gt;I’m not that pessimistic though. I just hope that this time the thoughtfulness will prevail over pure profit seeking. (Did I really write that.)&lt;/p&gt;
&lt;h2 id="django-ckeditor"&gt;&lt;a class="toclink" href="#django-ckeditor"&gt;&lt;a href="https://github.com/django-ckeditor/django-ckeditor"&gt;django-ckeditor&lt;/a&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Many people are noticing that the CKEditor 4 integration for Django doesn’t work that well when using the dark color scheme of the Django admin panel. That’s not surprising. What does surprise me is the number of reports and the total absence of pull requests. Subjectively, most other packages I help maintain have a comfortable ratio of issues and pull requests.&lt;/p&gt;
&lt;p&gt;I’m not complaining and people aren’t complaining, almost everyone is nice and tries to be helpful. Since I’m not a heavy user of django-ckeditor anymore I don’t really find the motivation to fix this issue myself since it’s not all that enjoyable work to me. I would just hope that &lt;a href="https://github.com/django-ckeditor/django-ckeditor/issues/670"&gt;after all this time&lt;/a&gt; someone would finally come and do the necessary work – if they cared enough.&lt;/p&gt;
&lt;h2 id="django-mptt"&gt;&lt;a class="toclink" href="#django-mptt"&gt;django-mptt&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have marked &lt;a href="https://github.com/django-mptt/django-mptt"&gt;django-mptt&lt;/a&gt; as unmaintained two years ago. I’m still looking at pull requests from time to time but without feeling an obligation to do so and without feeling bad when I miss something.&lt;/p&gt;
&lt;p&gt;I wonder if there are still many people out there who still depend on this library and if any of them would be willing to pick up the maintenance? On the off chance that someone is out there who has the time, ability and motivation and just didn’t know that django-mptt could use some love: Here’s your invite!&lt;/p&gt;
</summary></entry><entry><title>Weeknotes (2023 week 5)</title><link href="https://406.ch/writing/weeknotes-2023-week-5/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2023-week-5/</id><published>2023-02-04T12:00:00Z</published><updated>2023-02-04T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2023-week-5"&gt;&lt;a class="toclink" href="#weeknotes-2023-week-5"&gt;Weeknotes (2023 week 5)&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A long time has passed since I tried writing week notes. Oh well, here we go again. No committment here, just hope.&lt;/p&gt;
&lt;h2 id="podcasts"&gt;&lt;a class="toclink" href="#podcasts"&gt;Podcasts&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Really got into listening to podcasts in the last few weeks. That’s new for me, I never enjoyed listening to people talk when I could also listen to music. So, that’s interesting.&lt;/p&gt;
&lt;p&gt;Podcasts I like a lot: &lt;a href="https://www.nytimes.com/column/ezra-klein-podcast"&gt;The Ezra Klein Show&lt;/a&gt;, &lt;a href="https://djangochat.com/"&gt;Django Chat&lt;/a&gt; and some others where I haven’t yet listened enough to list them here.&lt;/p&gt;
&lt;h2 id="advent-of-code"&gt;&lt;a class="toclink" href="#advent-of-code"&gt;Advent of Code&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I knew that Advent of Code existed quite long already but I never dug into it. I participated in the 2022 Advent of Code and enjoyed it a lot. I have long been unsecure regarding my programming skills since I do not have a formal CS background so it felt really good to be able to solve many problems with only a little help. Definitely not all of them though. It definitely was fun and I’m still working through other years a bit. Python is the most fun, JavaScript is sometimes nice. Rust gives me a hard time (or I’m giving myself a hard time) but the upside is that solving even easy puzzles feels very rewarding.&lt;/p&gt;
&lt;h2 id="open-source"&gt;&lt;a class="toclink" href="#open-source"&gt;Open Source&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This week I released new versions of &lt;a href="https://pypi.org/project/feincms3/"&gt;feincms3&lt;/a&gt; and &lt;a href="https://pypi.org/project/django-tree-queries/"&gt;django-tree-queries&lt;/a&gt;, only patch releases. I didn’t do that much open source work in the last weeks or months; it really depends a lot on the work I’m doing during the day at &lt;a href="https://feinheit.ch/"&gt;Feinheit&lt;/a&gt;. The activity in the open source project space is mostly always higher when I’m starting many small new projects and not so much when working on big old projects.&lt;/p&gt;
&lt;p&gt;Django 4.2a1 has been released, and the &lt;code&gt;main&lt;/code&gt; branch of Django will be the upcoming 5.0 release which will break backwards compatibility again. The minimum Python requirement has been bumped to Python 3.10, which caused and still causes some work in updating the CI job lists. Good to see that the world is moving forward here.&lt;/p&gt;
&lt;p&gt;As an aside, I always try deploying the last and best version of Django in my current projects, but since one of our servers is still using Ubuntu 18.04 with an old PostgreSQL version I cannot even use Django 4.1 with many projects. That makes me sad but maybe the sadness will bring with it the energy to finally move to containers or something which makes it easier to jump to new versions. We’ll see. It’s not really an issue most of the time so maybe it still isn’t worth it even though I feel like our hosting strategy is stuck in 2010.&lt;/p&gt;
&lt;h2 id="machine-learning"&gt;&lt;a class="toclink" href="#machine-learning"&gt;Machine Learning&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I did some playing around with Dall-E and ChatGPT (as everyone seems to do). I had a few interesting discussions with people re. if predicting the next word will lead to Artificial General Intelligence (I don’t think so).&lt;/p&gt;
&lt;p&gt;What was fun about it was that I got to throw a hack together with &lt;a href="https://docs.python.org/3/library/asyncio.html"&gt;asyncio&lt;/a&gt; respectively &lt;a href="https://www.uvicorn.org/"&gt;uvicorn&lt;/a&gt; and &lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;. The Starlette source code is a breath of fresh air. It’s so beautiful to read and I just love how much it does with a minimal amount of code.&lt;/p&gt;
</summary></entry><entry><title>Managing complexity and technical debt by releasing Open Source Software</title><link href="https://406.ch/writing/managing-complexity-and-technical-debt-by-releasing-open-source-software/" rel="alternate" /><id>https://406.ch/writing/managing-complexity-and-technical-debt-by-releasing-open-source-software/</id><published>2022-08-19T12:00:00Z</published><updated>2022-08-19T12:00:00Z</updated><summary type="html">&lt;h1 id="managing-complexity-and-technical-debt-by-releasing-open-source-software"&gt;&lt;a class="toclink" href="#managing-complexity-and-technical-debt-by-releasing-open-source-software"&gt;Managing complexity and technical debt by releasing Open Source Software&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;When working on projects for &lt;a href="https://feinheit.ch"&gt;our&lt;/a&gt; clients I often try to keep as much code as possible in third party packages; I often even create packages for Django apps even when I do not expect to use the code in more than one project at all. One of those packages (which, in the meantime &lt;em&gt;has&lt;/em&gt; been used more than once is &lt;a href="https://django-spark.readthedocs.io/"&gt;django-spark&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;At first sight it looks like more work to package, document and test the solution. Defining boundaries is also much harder when there are many packages involved – it’s not possible to just add a hack at the right place. You have to define extension points or hooks or you have to design a library which can be used in various ways – there’s not really a way around that when moving code into open source packages.&lt;/p&gt;
&lt;p&gt;It isn’t the case that this approach necessarily means more work though, neither in the initial project nor during &lt;a href="https://406.ch/writing/low-maintenance-software/"&gt;maintenance&lt;/a&gt;. Here’s a list of reasons why that is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once the logical units of functionality are identified separating them is relatively straightforward. Tricky pieces of code can be isolated, documented and tested using unit tests. I maintain that unit tests are more robust &lt;em&gt;and&lt;/em&gt; easier to write than integration tests.&lt;/li&gt;
&lt;li&gt;Moving code into Open Source packages keeps the packages themselves relatively clean of project-specific concerns. This means that the promise of reusability of Django’s apps is actually achievable.&lt;/li&gt;
&lt;li&gt;Compatibility concerns aren’t such a big topic. The third party package (initially) only has to work for the duration of the project.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There’s never a guarantee that any open source library will be maintained in the future. I’m thinking that it may be useful to communicate the &lt;em&gt;intented maintenance&lt;/em&gt; better by adding an additional classifier when publishing a library. I’m thinking about something along these lines:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Actively maintained, used in multiple projects&lt;/li&gt;
&lt;li&gt;Actively maintained, used in few projects which may even be in maintenance mode&lt;/li&gt;
&lt;li&gt;Unmaintained right now, future maintenance is probable&lt;/li&gt;
&lt;li&gt;Unmaintained right now, future maintenance is improbable&lt;/li&gt;
&lt;li&gt;Definitely a dead project&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;But yeah, I’m not doing this classification work right now and haven’t done it in the past. But it would certainly make it easier to know whether relying on a third party library is a good idea or not, as an additional signal to reading the code and skimming the issues and pull/merge requests.&lt;/p&gt;
&lt;p&gt;&lt;small&gt;I have been sitting on this blog post for almost two years, and the ideas here have been gestating even longer. I’m sure someone else has said everything in here more eloquently in the meantime. But anyway, feedback is welcome as always and I don’t think the post is so bad that it should sit in the draft folder forever.&lt;/small&gt;&lt;/p&gt;
</summary></entry><entry><title>Weeknotes (2021 week 10)</title><link href="https://406.ch/writing/weeknotes-2021-week-10/" rel="alternate" /><id>https://406.ch/writing/weeknotes-2021-week-10/</id><published>2021-03-14T12:00:00Z</published><updated>2021-03-14T12:00:00Z</updated><summary type="html">&lt;h1 id="weeknotes-2021-week-10"&gt;&lt;a class="toclink" href="#weeknotes-2021-week-10"&gt;Weeknotes (2021 week 10)&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I’m trying out the weeknotes format to hopefully write a bit more 😄.&lt;/p&gt;
&lt;h2 id="feincms3-page-types-and-moving-torwards-a-10-release"&gt;&lt;a class="toclink" href="#feincms3-page-types-and-moving-torwards-a-10-release"&gt;feincms3: Page types and moving torwards a 1.0 release&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/matthiask/feincms3/blob/main/CHANGELOG.rst"&gt;current beta release of feincms3 (0.90b*)&lt;/a&gt; introduces support for &lt;strong&gt;page types&lt;/strong&gt;. This new concept unifies templates and apps. Since feincms3 apps can basically do what they want it didn’t make much sense to force managers to choose a template when they already selected an app. Sometimes, we offered managers a “projects” template and a “projects” app and they had to select both. This duplication was hard to explain and justify.&lt;/p&gt;
&lt;p&gt;This isn’t the only backwards-incompatible change in 0.90b*. The default image plugins now always have a caption and an alternative text field; the versatileimagefield-based plugin is gone as are the shims in &lt;code&gt;feincms3.apps&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The idea is to release a 1.0 of feincms3 soon-ish. It’s time to do this. feincms3 is in its fifth year of existence, several sites built with it have already been archived already, so it’s definitely past due. (django-content-editor (resp. FeinCMS 1.x) more than 12 years old now.)&lt;/p&gt;
&lt;h2 id="django-mptt-is-not-maintained-anymore"&gt;&lt;a class="toclink" href="#django-mptt-is-not-maintained-anymore"&gt;django-mptt is not maintained anymore&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We, the maintainers of django-mptt &lt;a href="https://github.com/django-mptt/django-mptt/pull/776"&gt;have marked django-mptt as unmaintained&lt;/a&gt;. I have released the current development version as 0.12.0 before proposing this.&lt;/p&gt;
&lt;h2 id="postgresqls-ltree-extension-vs-django-tree-queries-recursive-ctes"&gt;&lt;a class="toclink" href="#postgresqls-ltree-extension-vs-django-tree-queries-recursive-ctes"&gt;PostgreSQL’s ltree extension vs. django-tree-queries’ recursive CTEs&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I got the (excellent) question why I wrote that PostgreSQL’s ltree extension didn’t seem a good solution &lt;a href="https://406.ch/writing/django-tree-queries/"&gt;in my post about django-tree-queries&lt;/a&gt;. &lt;a href="https://github.com/matthiask/406-comments/issues/2#issuecomment-797981910"&gt;Here’s my current thinking on this.&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="going-all-in-on-css-grid-and-postcss"&gt;&lt;a class="toclink" href="#going-all-in-on-css-grid-and-postcss"&gt;Going all-in on CSS Grid and PostCSS&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have been using &lt;a href="https://github.com/postcss/autoprefixer"&gt;autoprefixer&lt;/a&gt; for years already. It’s probably the best-known &lt;a href="https://postcss.org/"&gt;PostCSS&lt;/a&gt; plugin. It automatically adds vendor-specific prefixes to your CSS code (e.g. &lt;code&gt;-moz-*&lt;/code&gt;, &lt;code&gt;-webkit-*&lt;/code&gt;, &lt;code&gt;-ms-*&lt;/code&gt; etc.) depending on a list of browsers you want to support and the &lt;a href="https://caniuse.com/"&gt;Can I use…&lt;/a&gt; database.&lt;/p&gt;
&lt;p&gt;I wanted to bring bundling times down and searched for alternatives to Webpack. Webpack isn’t the hot new thing anymore but it works really well and performs good enough as long as you do not have extreme amounts of code and plugins. I started with a new, minimal Webpack configuration and was pleasantly surprised that compilation times are more than acceptable when only using babel-loader to transpile JavaScript and some PostCSS. (I may write about the setup at a later time, for now &lt;a href="https://406.ch/writing/our-approach-to-configuring-django-webpack-and-manifeststaticfilesstorage/"&gt;this approach&lt;/a&gt; still serves &lt;a href="https://feinheit.ch/"&gt;us&lt;/a&gt; well and we have even packaged it up as &lt;a href="https://github.com/feinheit/fh-webpack-config/"&gt;fh-webpack-config&lt;/a&gt;.)&lt;/p&gt;
&lt;h2 id="changing-the-main-branch-to-main"&gt;&lt;a class="toclink" href="#changing-the-main-branch-to-main"&gt;Changing the main branch to main&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I added the &lt;a href="https://github.com/matthiask/dotfiles/commit/3a2e3ff58de38aa8e03ca1d684504ebea02cdd6c"&gt;following alias to my &lt;code&gt;.gitconfig&lt;/code&gt;&lt;/a&gt; to fix the problem where the &lt;code&gt;refs/remotes/origin/HEAD&lt;/code&gt; of local repositories would point to &lt;code&gt;master&lt;/code&gt; instead of &lt;code&gt;main&lt;/code&gt; after renaming the branch on GitHub:&lt;/p&gt;
&lt;div class="chl"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;git&lt;span class="w"&gt; &lt;/span&gt;config&lt;span class="w"&gt; &lt;/span&gt;--global&lt;span class="w"&gt; &lt;/span&gt;alias.mainfix&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"symbolic-ref refs/remotes/origin/HEAD refs/remotes/origin/main"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;git mainfix&lt;/code&gt; is easier to type than &lt;code&gt;vim .git/refs/remotes/origin/HEAD&lt;/code&gt; and replacing whatever the previous main branch was called with &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="neovim"&gt;&lt;a class="toclink" href="#neovim"&gt;neovim&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I have switched from vim to neovim. I don’t exactly remember why but the transition has been seamless. I’m still happy with tmux and vim.&lt;/p&gt;
</summary></entry><entry><title>The future of FeinCMS</title><link href="https://406.ch/writing/the-future-of-feincms/" rel="alternate" /><id>https://406.ch/writing/the-future-of-feincms/</id><published>2018-09-22T12:00:00Z</published><updated>2018-09-22T12:00:00Z</updated><summary type="html">&lt;h1 id="the-future-of-feincms"&gt;&lt;a class="toclink" href="#the-future-of-feincms"&gt;The future of FeinCMS&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;A recent exchange in an &lt;a href="https://github.com/feincms/feincms-elephantblog/issues/68"&gt;issue in the elephantblog repository on Github asking whether the project was still alive&lt;/a&gt; and my own work on &lt;a href="https://feincms3.readthedocs.io"&gt;feincms3&lt;/a&gt; made me think about the need to clarify my position on the future of &lt;a href="https://github.com/feincms/feincms"&gt;FeinCMS 1.x&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the issue I wrote that it (meaning elephantblog) is mostly in maintenance mode and not actively developed anymore, and I also pointed people towards feincms3 for new projects.&lt;/p&gt;
&lt;p&gt;There are a lot of things FeinCMS 1.x got right and there are things that could be better or I could have handled better.&lt;/p&gt;
&lt;p&gt;FeinCMS is used in a few flagship projects which we’re still actively developing, which means that FeinCMS won’t be going away for years to come. That being said, for new CMS projects I’m more likely to use django-content-editor and feincms3. This series of blog posts offers an appreciation for my most successful open source project and hopefully also offers insights into my plans and the reasoning behind them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://406.ch/writing/the-state-of-things-in-feincms-1x-land/"&gt;The state of things in FeinCMS 1.x land&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://406.ch/writing/what-did-feincms-get-right/"&gt;What did FeinCMS get right?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://406.ch/writing/the-other-future-of-feincms-django-content-editor-and-feincms3/"&gt;The other future of FeinCMS: django-content-editor and feincms3&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://406.ch/writing/bringing-feincms-and-django-content-editorfeincms3-closer-together/"&gt;Bringing FeinCMS and django-content-editor/feincms3 closer together&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here’s a link to all posts in the &lt;a href="https://406.ch/writing/category-feincms/?o=chronological"&gt;feincms category&lt;/a&gt; if you want to follow along.&lt;/p&gt;
</summary></entry><entry><title>My rules for releasing open source software</title><link href="https://406.ch/writing/my-rules-for-releasing-open-source-software/" rel="alternate" /><id>https://406.ch/writing/my-rules-for-releasing-open-source-software/</id><published>2017-12-06T12:00:00Z</published><updated>2017-12-06T12:00:00Z</updated><summary type="html">&lt;h1 id="my-rules-for-releasing-open-source-software"&gt;&lt;a class="toclink" href="#my-rules-for-releasing-open-source-software"&gt;My rules for releasing open source software&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I maintain and help maintain quite a few Open Source Python packages. Possibly well-known packages include &lt;strong&gt;django-debug-toolbar&lt;/strong&gt;, &lt;strong&gt;django-ckeditor&lt;/strong&gt;, &lt;strong&gt;django-mptt&lt;/strong&gt;, and &lt;strong&gt;FeinCMS&lt;/strong&gt; resp. &lt;strong&gt;feincms3&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Open source development used to stress me greatly. I was always worrying whether the code is polished enough, whether I didn’t introduce new bugs and whether the documentation is sufficient.&lt;/p&gt;
&lt;p&gt;These days I still think about these things, but I do not worry as much as I used to. The reason for this are the following principles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A fully passing test suite on Travis CI is a sufficient quality guarantee for a release.&lt;/li&gt;
&lt;li&gt;Do not worry about release notes, but always keep the &lt;a href="https://django-content-editor.readthedocs.io/en/latest/#change-log"&gt;CHANGELOG&lt;/a&gt; up to date.&lt;/li&gt;
&lt;li&gt;Put out patch releases even for the smallest bugfixes and feature additions (as long as they are backwards compatible). Nobody wants to wait for the next big release, it always takes longer than intended.&lt;/li&gt;
&lt;li&gt;Good enough is perfection.&lt;/li&gt;
&lt;/ol&gt;
</summary></entry><entry><title>Low maintenance software</title><link href="https://406.ch/writing/low-maintenance-software/" rel="alternate" /><id>https://406.ch/writing/low-maintenance-software/</id><published>2017-06-05T12:00:00Z</published><updated>2017-06-05T12:00:00Z</updated><summary type="html">&lt;h1 id="low-maintenance-software"&gt;&lt;a class="toclink" href="#low-maintenance-software"&gt;Low maintenance software&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;I’m passionate about writing low maintenance software.&lt;/p&gt;
&lt;p&gt;To achieve this software must be simple, opinionated, and often written as a library, not a framework. While it seems easier to write a framework with extension points it is, in the long run, more useful to build powerful tools.&lt;/p&gt;
&lt;p&gt;Applying these principles to Django apps means that often it isn’t very useful to supply views and URLconf modules, but instead supply forms and helpers that make writing your own views more straightforward.&lt;/p&gt;
&lt;p&gt;Glue code is often different between projects anyway, and in my opinion views are just that: Glue code.&lt;/p&gt;
&lt;p&gt;That is why the first release of &lt;a href="https://django-authlib.readthedocs.io/"&gt;django-authlib&lt;/a&gt; did not ship with any views; only with helpers that make writing your own views for implementing authentication based on verified email addresses more straightforward.&lt;/p&gt;
&lt;p&gt;I now ship views; but those views are not configurable or extensible. The idea is that if those views are not exactly what you need, you should write your own (and in fact we often do exactly that when using authlib in our projects)&lt;/p&gt;
&lt;p&gt;Another illustration of these principles is the comparison between &lt;a href="https://github.com/feincms/feincms/blob/master/feincms/module/mixins.py"&gt;FeinCMS’ view code&lt;/a&gt; and &lt;a href="https://github.com/matthiask/feincms3-example/blob/master/app/pages/views.py"&gt;feincms3’s view code&lt;/a&gt;. The former uses the framework approach: Build a complex (and complicated) machinery with plugin points, processors and whatnot, the latter offers powerful tools which require very little glue code. This makes the location for adding custom behavior more obvious – you can place it directly in the view code. The alternative of using inversion of control, and understanding when and how you can add your own code makes the barrier of entry much higher.&lt;/p&gt;
</summary></entry></feed>