18 Apr 2026

feedLXer Linux News

Fedora 44 Will Not Be Released Next Week

Fedora 44 final had been aiming for an early release target of 21 April, but due to outstanding blocker bugs, it's now revised to target a release on 28 April...

18 Apr 2026 1:09am GMT

17 Apr 2026

feedLXer Linux News

Archinstall 4.2 Arch Linux Installer Brings Granular KDE Plasma Configuration

Archinstall 4.2 text-based installer for Arch Linux is now available with granular KDE Plasma configuration, a new Pacman settings submenu with Color and Parallel Downloads, and other changes.

17 Apr 2026 11:38pm GMT

GIMP 3.2.4 Fixes Layer Fills, Text Tool Bugs, and File Import Issues

GIMP 3.2.4 image editor improves layer fills, text tool behavior, PDF export, and support for PSD, PNG, DDS, ORA, and other file formats.

17 Apr 2026 10:06pm GMT

feedDZone Java Zone

Training a Neural Network Model With Java and TensorFlow

Training, exporting, and using a TensorFlow model is a great way to gain a low-level understanding of the building blocks of the LLMs fueling the AI revolution.

Since I am comfortable with using Java, I will use it to define a neural network (NN) model, train it, export it in a language-agnostic format, and then import it into a Spring Boot project. Now, doing all this from scratch would not be advisable, since there are many advances in the field of NN that would take a long time to properly understand and implementing them would be difficult and error-prone. So, to both learn about NNs and make implementation easy, we will use a proven software platform: TensorFlow.

17 Apr 2026 6:00pm GMT

Multithreading in Modern Java: Advanced Benefits and Best Practices

Multithreading has always been one of core strengths of Java over years. From the early days of the JVM, Java was designed with built-in support for concurrent programming. But for many years, writing scalable multithreaded applications required careful tuning, thread pool management and constant attention to synchronization.

In the latest Java versions, the concurrency model has evolved significantly. Modern Java introduces improvements such as Virtual Threads, better executors, improved fork-join performance and more structured concurrency approaches. These features allow developers to build highly concurrent applications with simpler code and fewer scalability limitations.

17 Apr 2026 5:00pm GMT

feedDjango community aggregator: Community blog posts

Django News - 30% Off PyCharm Pro – 100% for Django - Apr 17th 2026

Introduction

Django News Newsletter is moving!

Just a quick heads up. We're planning to move our newsletter to a new platform next week.

If things look a little different when it shows up, it's still us.

Django Newsletter

News

PyCharm & Django annual fundraiser

JetBrains and the Django Software Foundation team up again to offer 30% off PyCharm while matching donations to fund Django's core development and community programs.

djangoproject.com

New Technical Governance - request for community feedback

Django proposes a simpler, more flexible technical governance model and is inviting community feedback ahead of a planned July 2026 rollout.

djangoproject.com

Could you host DjangoCon Europe 2027? Call for organizers

DjangoCon Europe 2026 is happening right now in Athens, Greece but plans for 2027 have already begun. This post lays out all the resources for any questions, support, and more for future organizers.

djangoproject.com

Reverting the incremental GC in Python 3.14 and 3.15 - Core Development

Python is rolling back its new incremental garbage collector in 3.14 and 3.15 after real-world memory issues, reverting to the proven generational model while rethinking a future reintroduction.

python.org

PEP 772: Packaging Council governance process (Round 3) - Packaging / Coordination

PEP 772 has officially been approved, creating a new Python Packaging Council to guide the future of packaging standards, tools, and ecosystem governance.

python.org

Django Software Foundation

Django Has Adopted Contributor Covenant 3

The 3.0 edition of the new Code of Conduct is here! This milestone represents the completion of a careful, community-driven process that began earlier this year.

djangoproject.com

DSF Board monthly meeting, April 9, 2026

The Django Software Foundation approved a modernized Code of Conduct, new working group charters, and key community initiatives, signaling a fresh push toward clearer governance and sustained project growth.

django.github.io

Python Software Foundation

PyCon US 2026: Why we're asking you to think about your hotel reservation

For many years, PyCon US has relied on hotel booking commissions to help pay for conference space. If you are attending this year, please use an official hotel to be both close to the venue.

pyfound.blogspot.com

Python Software Foundation News: Reflecting on Five Years as the PSF's First CPython Developer in Residence

Łukasz Langa looks back on five years and highlights including the transition to GitHub issues from bugs.python.org, the replacement of the mostly manual CLA process with an automated system, the introduction of free threading to Python, and the replacement of the interactive shell in the interpreter. Also while addressing thousands of bugs, he's witnessed the full-time paid developer in residence roster at the Python Software Foundation grow from one person to five.

pyfound.blogspot.com

Updates to Django

Today, "Updates to Django" is presented by Johanan Oppong Amoateng from Djangonaut Space! 🚀

Last week we had 12 pull requests merged into Django by 10 different contributors - including a first-time contributor! Congratulations to Jonathan Wu for having their first commits merged into Django - welcome on board!

This week's Django highlights: 🦄

Django Newsletter

Django Fellow Reports

Fellow Report - Natalia

A good chunk of this week focused on improving contributor workflows and reducing review overhead by introducing automated quality checks for PRs :robot:. This builds on prior experimentation (thanks @frankwiles) and seeks to provide early, actionable feedback for PR authors while helping maintainers focus on substantive review. We also had a flood of overly verbose and low quality reports from the same person, which I closed eagerly making use of the recent new guidelines we published in the security policy.

djangoproject.com

Fellow Report - Jacob

The last report before DjangoCon Europe. Lots of tickets triaged, reviewed, authored, discussed, and the usual kaleidoscope of miscellaneous tasks.

djangoproject.com

Django Fellow Report - Sarah

Django Fellow Sarah Boyce returns from maternity leave with part-time updates, tackling triage, reviews, security work, and GSoC prep while navigating connectivity challenges from Turkey.

djangoproject.com

Sponsored Link 1

You know @login_required. Now meet @app.reasoner(). AgentField turns Python functions into production AI agents, structured output, async execution, agent discovery. Every decorator becomes a REST endpoint. Open source, Apache 2.0. Python, Go & TypeScript SDKs.

agentfield.ai

Articles

Enforce Business Logic in the Database with Django

A practical guide to enforcing business logic at the database layer in Django using transactions, select_for_update locks, and CheckConstraint / UniqueConstraint to prevent race conditions and invalid data rather than relying on application-level validation.

lincolnloop.com

Let's talk about LLMs

James Bennett consolidates his thoughts on AI/LLMs in this wide-ranging piece, ending with a call to invest in software fundamentals instead of racing to adopt the latest AI craze.

b-list.org

Django Table, Filter and Export With Htmx

A reusable pattern for combining django-tables2, django-filter, and HTMX into a single generic view and template. Very cool stuff.

fundor333.com

Decoupling Your Business Logic from the Django ORM

Carlton Gibson's latest The Stack Report is a detailed dive into business logic and how to handle it in Django. This is a perennial topic, but he comes at it with decades of experience and wisdom.

buttondown.com

djust 0.4.0 - The Developer Experience Release

djust 0.4.0 is about developer experience - making everyday tasks faster, safer, and more intuitive. 30+ new features, critical bug fixes, and a security hardening pass that eliminated every known vulnerability.

djust.org

Why aren't we uv yet?

A decent chunk of new Python repos already use uv. Coding agents still overwhelmingly recommend pip and requirements.txt, while many users prefer uv.

aleyan.com

Events

Are You Attending PyCon, or Orbiting It?

PSF Board Member Georgi Ker makes a personal case for booking hotels via the official PyCon US website before April 24th.

georgiker.com

Design Articles

Under the hood of MDN's new frontend

From 2-min dev server starts to 2s. They rewrote MDN's entire frontend, ditching the React SPA for Lit web components, server components, and Rspack. The result: less JS shipped, scoped CSS, and a build pipeline that just works.

mozilla.org

Videos

Debunking Django Myths - Sarah Boyce at PyTV

Django Fellow Sarah Boyce gave a talk recently at PyTV titled, "Django Has a Marketing Problem: Debunking the Myths That Won't Die." It is a fantastic overview of what Django does well and what it can improve.

youtu.be

Incremental Typing in Django - Carlton Gibson

Former Django Fellow and current Django Chat podcast host Carlton Gibson, recently gave a talk titled, "Static Islands, Dynamic Sea: Some Thoughts on Incremental Typing." In it he talks about why Python's dynamic nature is a feature, not a bug, and demonstrates Mantle - a library of utilities for typing around Django's liquid core.

youtu.be

Sponsored Link 2

Annual PyCharm Promo - 30% off, all money goes to Django

The annual PyCharm + Django promotion is live until May 1st. This is the single biggest fundraiser for Django and has raised over $350,000 since 2016.

jetbrains.com

Podcasts

Django Tasks - Jake Howard

Episode 200(!) features Jake Howard, a Senior Systems Engineer at Torchbox and the author of DEP 14, django.tasks, the highlight feature in Django 6.0. We discuss his work on the Django security team, work with Wagtail, AI dabblings, and more.

djangochat.com

Django Job Board

Python Developer at Open Data Services

Remote UK role building Python data systems for social-impact projects, offering ~£48k plus profit share in a collaborative worker co-op.

djangojobboard.com

Projects

yassi/dj-signals-panel

Display registered Django signals and receivers, showing what fires and where.

github.com

dvf/opinionated-django

An opinionated Django project with Repository pattern, Pydantic DTOs, svcs DI, and Stripe-style ULID IDs

github.com


This RSS feed is published on https://django-news.com/. You can also subscribe via email.

17 Apr 2026 3:00pm GMT

feedPlanet Python

Mike Driscoll: Textual – An Intro to DOM Queries (Part I)

In this article, you will learn how to query the DOM in Textual. You will discover that the DOM keeps track of all the widgets in your application. By running queries against the DOM, you can find widgets quickly and update them, too.

You will be learning the following topics related to the DOM:

You will learn more in the second part of this series next week!

You will soon see the value of working with DOM queries and the power that these queries give you. Let's get started!

The Query One Method

You will find the query_one() method throughout the Textual documentation and many Textual applications on GitHub. You may use query_one() to retrieve a single widget that matches a CSS selector or a widget type.

You can pass in up to two parameters to query_one():

If you pass both, pass the CSS selector first, with the widget type as the second parameter.

Try some of this out. Open up your Python editor and create a file named query_input.py. Then enter this code in it:

# query_input.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Input


class QueryInput(App):

    def compose(self) -> ComposeResult:
        yield Input()
        yield Button("Update Input")

    def on_button_pressed(self) -> None:
        input_widget = self.query_one(Input)
        new_string = f"You entered: {input_widget.value}"
        input_widget.value = new_string


if __name__ == "__main__":
    app = QueryInput()
    app.run()

Your code creates an Input and a Button widget. Enter some text in the Input widget and press the button. Your on_button_pressed() method will get called. You call query_one() and pass it an Input widget. Then, you update the returned Input widget's value with a new string.

Here is what the application might look like:

Now, you will try writing a new piece of code where you use query_one() with a CSS selector. Create a new file called query_one_same_ids.py and use this code:

# query_one_same_ids.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -> ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("Test", id="button")

    def on_button_pressed(self) -> None:
        widget = self.query_one("#label")
        widget.update("You pressed the button!")


if __name__ == "__main__":
    app = QueryApp()
    app.run()

In this example, you create two widgets with different IDs. Then you use query_one() to select the Label widget and update its text.

If you call query_one() and there are no matches, you will get a NoMatches exception. On the other hand, if there is more than one match, the method will return the first item that does match.

What will the following code do if you put it in your example above?

self.query_one("#label", Button)

If you guessed that Textual will raise an exception, you should congratulate yourself. You have good intuition! If the widget matches the CSS selector but not the widget type, then you will get a WrongType exception raised.

Textual Queries

Textual has more than one way to query the DOM. You may also use the query() method, which you can use to query or find multiple widgets. When you call query(), it will return a DOMQuery object, which behaves as a list-like container of widgets.

You can see how this works by writing some code. Create a new Python file named query_all.py and add this code to it:

# query_all.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -> ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("Test", id="button")

    def on_button_pressed(self) -> None:
        widgets = self.query()
        s = ""
        for widget in widgets:
            s += f"{widget}\n"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()

The idea is to get all the widgets in your application and print them out. Of course, you can't print out anything when your terminal application is blocking stdout, so instead, you create a string of widgets separated by new lines and update the Label widget.

Here is an example of what you might get if you run the code and press the button on your machine:

You might be surprised by that output. Perhaps you thought you would only see a Label and a Button widget in that list? If so, you forgot that a Screen widget is always lurking in the background. But there are also two more: a ToastRack and a Tooltip widget. These come with all your applications. The ToastRack positions Toast widgets, which you use to display a notification message. A Tooltip is a message that appears when you hover your mouse over a widget.

You do not need to know more about those extra widgets now.

Also note that all query methods can be used on both the App and Widget subclasses, which is very handy.

Using Selectors

You can use CSS selectors with query() in much the same way as you can with query_one(). The difference, of course, is that query() always returns an iterable DOMObject.

Let's pretend you want to get all the Button widgets in your application and iterate over them. Create a new Python script called query_button.py with this code:

# query_buttons.py

from textual.app import App, ComposeResult
from textual.widgets import Button, Label


class QueryApp(App):

    def compose(self) -> ComposeResult:
        yield Label("Press a button", id="label")
        yield Button("One", id="one")
        yield Button("Two", id="two")
        yield Button("Three")

    def on_button_pressed(self) -> None:
        s = ""
        for widget in self.query("Button"):
            s += f"{widget}\n"
        label = self.query_one("#label")
        label.update(s)


if __name__ == "__main__":
    app = QueryApp()
    app.run()

Here you are passing in a string, "Button", to query(). If using query_one, you would use the Button type directly. Regardless, when you run this code and press the button, you will see the following:

That worked great! This time, you queried the DOM and returned all the Button widgets in your application.

What if you wanted to find all the disabled buttons in your code? You can disable widgets using the disabled style flag or the CSS attribute. To find those widgets, you would update the query like this: widgets = self.query("Button.disabled").

Results

The query objects in Textual also provide a results() method that you can use as an alternative way of iterating over the widgets. For example, you can use results() to rewrite the query above that would retrieve all the disabled buttons to be something like this:

widgets = self.query(".disabled").results(Button)
s = ""
for widget in widgets:
    s += f"{widget}\n"

This code combines the last example query with the last full code example. Although this latter version is more verbose, you might find it easier to read than the original query for disabled widgets.

Another benefit of using results() is that Python type checkers, such as Mypy, can use it to determine the widget type in the loop. When you do not use results(), then Mypy will only know that you are looping over a Widget object, rather than a Button object.

Wrapping Up

You learned the basics of using Textual's DOM query methods in this article. You can use these query methods to access one or more widgets in your user interface.

Specifically, you learned about the following:

Textual is a great way to create a user interface with Python. You should check it out today!

Learn More

Want to learn more about Textual? Check out my book, Creating TUI Applications with Textual and Python:

The post Textual - An Intro to DOM Queries (Part I) appeared first on Mouse Vs Python.

17 Apr 2026 12:57pm GMT

Real Python: The Real Python Podcast – Episode #291: Reassessing the LLM Landscape & Summoning Ghosts

What are the current techniques being employed to improve the performance of LLM-based systems? How is the industry shifting from post-training towards context engineering and multi-agent orchestration? This week on the show, Jodie Burchell, data scientist and Python Advocacy Team Lead at JetBrains, returns to discuss the current AI coding landscape.


[ Improve Your Python With 🐍 Python Tricks 💌 - Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

17 Apr 2026 12:00pm GMT

Real Python: Quiz: Working With Python Virtual Environments

Test your understanding of the Working With Python Virtual Environments video course.

You'll revisit why virtual environments matter, how to create and activate them, and how to install and manage packages inside an isolated Python environment.


[ Improve Your Python With 🐍 Python Tricks 💌 - Get a short & sweet Python Trick delivered to your inbox every couple of days. >> Click here to learn more and see examples ]

17 Apr 2026 12:00pm GMT

feedDjango community aggregator: Community blog posts

Djangocon EU: body of knowledge - Daniele Procida

(One of my summaries of the 2026 Djangocon EU in Athens).

Athens! The thinking industry started here. Athens is often the origin if you follow ideas to the source.

Here also Socrates was found guilty (280-221) for "corrupting the youth" on trumped-up charges. Though... he made it is job to be a complete nuisance: exposing everyone's hypocrisy and asking difficult questions. After the 280-221 vote he got to give a speech in reaction. After that, the vote on the actual punishment was 360-141 in favour of the death penalty. The speech must have been particularly irritating.

On to a different subject. He watched the recent launch of the NASA rocket that went to the moon. A marvel of technology. That was measured using body parts, being 322 feet tall. And the distance to the moon in miles. Why not the scientific meter and kilometer?

Plato already mentioned it. "Now take the acquisition of knowledge; is the body a hindrance or not, if one takes it into partnership to share an investigation". And "when the soul tries to investigate anything with the help of the body, it is obviously led astray".

0.098 km = 98 m = 98000 mm, a child can understand it. Pure rationality. But ask a Metric Martyr in the UK how many feet are in a mile and most of them won't know.

The world seems to be divided in two camps:

  • Thinking, rationality, abstraction, unboundedness.
  • Bodies, materiality, tangibility, being rooted.

Wouldn't Plato have loved a computer? Pure rationality, following its logic programming without fail?

What about those body-part-units? They're not that weird actually. They're rational Roman measurements:

  • A mile is 1000 Roman paces.
  • 1 passus = 5 pedes (feet).
  • 1/12 pes (feet) = 1 uncia (thus: inch).

(Note: according to the Greek, Romans are only good for stealing Greek ideas, building roads and killing people.)

On to another aspect. Why is Django's documentation so good? Well, it has been prioritized from the start. It is complete, accurate, consistent, rational and well-structured: all Platonic values.

But Daniele also thinks the documentation is so good because it fits the human body.

The size has to be right. The limitations of our intelligence are the limits of our embodied intelligence. We can only grasp so much, mentally. A list can be too long. A page can be too long. If information is cut in too-small parts, you also can get into problems as you have to context-switch between pages too much. We tire mentally also because we tire physically.

The same applies to our body. Our hands and fingers can grasp objects. But it has to be of a certain size. Too big and we can't grasp it. Too small and our fingers can't pick it up.

We experience documentation in time and space. We move with it. How long have you been reading the Django documentation? "Where are you in the text?" We orient ourselves in text as if in a space or a building. We rely on the humanised rationality of structure. Sometimes you're in a building and it is clear where you have to go and in other buildings you feel lost.

Django's documentation is so good because of the quality of experience that it gives you. It is almost an embodied being that you can experience in space and time. Does it fit you? Do you notice it? The embodied nature of the work and intelligence that the Django community poured into the documentation?

Early Macintosh manuals had to explain new concepts and really tried to explain them in a human way. Scrolling being explained with help of an old book scroll, for instance. A floppy disk for storage as a floor plan of a building with a corridor and rooms.

Aldine Press (started 1494 in Venice) had a vision to print the old classics in a more accessible way. Books in the middle ages used to be big. And sometimes chained to the desk. Not really accessible. By printing them in smaller, lighter, more accessible formats, he wanted to make our "body of knowledge" more fitting to the human body.

You can see the bodily aspects of knowledge in our language:

  • Seizing/taking: grasp, comprehend, apprehend, perceive.
  • Measuring: ponder, weigh up, fathom.
  • Body movement: jumping to conclusions, intuitive leap, stumble/trip
  • Spatiality: understand, position

Mental space. When he asked a question of Russell Keith-Magee at a Django sprint, Russell would close his eyes and turn his eye inwards for a while. He would look at the Django codebase in his head and navigate it. Just like you yourself would navigate a city?

Being a programmer isn't so different from being a human with a body in time and space. Look at questions you might have as a beginning programmer:

  • Which file or directory or window to be in.
  • Where to expect the output.
  • When to expect it.
  • Where to enter a command.
  • When to do something.
  • In what order to do things.

And then look at an experienced programmer. They seem to know where they are. They know their way around. They can move smoothly.

Closing comment: there are some uncanny features in software nowadays. As a human, we are used to having limits. But nowadays we have infinite scrolling, doomscrolling. And edgeless, endless, virtual cloud resources. And LLM indeterminism. Those are not inherintly bad, but it is different from what we're used to. Is this still computing fit for the embodied mind?

https://reinout.vanrees.org/images/2026/kat1.jpeg

Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the neighbourhood behind the hotel.

17 Apr 2026 4:00am GMT

Djangocon EU: auto-prefetching with model field fetch modes in Django 6.1 - Jacob Walls

(One of my summaries of the 2026 Djangocon EU in Athens).

There's an example to experiment with here: https://dryorm.xterm.info/fetch-modes-simple

Timeline: it will be included in Django 6.1 in August.

The reason is the 1+n problem:

books = Book.objects.all()
for book in books:
    print(book.author.name)
    # This does a fresh query for author every time.

You can solve it with select_related(relation_names) or prefetch_related(relation_names). The first does an inner join. The second does two queries.

But: you might miss a relation. You might specify too many relations, getting data you don't need. Or you might not know about the relation as the code is in a totally different part of the code.

Fetch mode is intended to solve it. You can append .fetch_mode(models.FETCH_xyz) to your query:

  • models.FETCH_ONE: the current behaviour, which will be the default.
  • models.FETCH_PEERS: Fetch a deferred field for all instances that came from the same queryset. More or less prefetch_related in an automatic, lazy manner.
  • models.FETCH_RAISE: useful for development, it will raise FieldFetchBlocked. And it will thus tell you that you'll have a performance problem and that you might need FETCH_PEERS

This is what happens:

books = Book.objects.all().fetch_mode(models.FETCH_PEERS)
for book in books:
    # We're iterating over the query, so the query executes and grabs all books.
    print(book.author.name)
    # We accessed a relation, so at this point the prefetch_related-like
    # mechanism ist fired off and all authors linked to by the books are
    # grabbed in one single query.

You can write your own fetch modes, for instance if you only want a warning instead of raising an error.

https://reinout.vanrees.org/images/2026/kat3.jpeg

Unrelated photo explanation: a cat I encountered in Athens on an evening stroll in the neighbourhood behind the hotel.

17 Apr 2026 4:00am GMT

16 Apr 2026

feedDrupal.org aggregator

The Drop Times: Erdfisch Expands nerdfisch DevBits into Public Drupal Code Archive

Reusable fixes often remain confined to individual projects, forcing developers to solve the same problems repeatedly. erdfisch has expanded its internal DevBits system into a publicly accessible archive, exposing working Drupal code snippets drawn directly from project work. The collection prioritises immediate implementation over explanation, making internal solutions available without reshaping them into long-form documentation.

16 Apr 2026 2:41pm GMT

feedDZone Java Zone

Optimizing Java Back-End Performance Profiling and Best Practices

The dashboard turned red at weekday. Our order processing API latency jumped from fifty milliseconds to five seconds. Customer support tickets flooded in. Users reported timeouts during checkout. The infrastructure team scaled up the Kubernetes pods, but the issue persisted. CPU usage sat at 100 percent across all nodes. We were throwing hardware at a software problem. This approach failed miserably.

In this article, I will share how we diagnosed the bottleneck. I will explain the profiling tools we used. I will detail the code changes that restored performance. This is not a theoretical guide. It is a record of a real production incident and the steps we took to resolve it.

16 Apr 2026 12:00pm GMT

feedPlanet Lisp

Tim Bradshaw: Structures of arrays

Or, second system.

A while ago, I decided that I'd like to test my intuition that Lisp (specifically implementations of Common Lisp) was not, in fact, bad at floating-point code and that the ease of designing languages in Lisp could make traditional Fortran-style array-bashing numerical code pretty pleasant to write.

I used an intentionally naïve numerical solution to a gravitating many-body system as a benchmark, so I could easily compare Lisp & C versions. The brief result is that the Lisp code is a little slower than C, but not much: Lisp is not, in fact, slow. Who knew?

The point here though, is that I wanted to dress up the array-bashing code so it looked a lot more structured. To do this I wrote a macro which hid what was in fact an array of (for instance) double floats behind a bunch of syntax which made it look like an array of structures. That macro took a couple of hours.

This was fine and pretty simple, but it only dealt with a single type for each conceptual array of objects, there was no inheritance and it was restricted in various other ways. In particular it really was syntactic sugar on a vector: there was no distinct implementational type at all. So I thought well, I could make it more general and nicer.

Big mistake.

The second system

Here is an example of what I wanted to be able to do (this is in fact the current syntax):

(define-soa-class example ()
  ((x :array t :type double-float)
   (y :array t :type double-float)
   (p :array t :type double-float :group pq)
   (q :array t :type double-float :group pq)
   (r :array t :type fixnum)
   (s)))

This defines a class, instances of which have five array slots and one scalar slot. Of the array slots:

The implementation will tell you this:

> (describe (make-instance 'example :dimensions '(2 2)))
#<example 8010059EEB> is an example
[...]
dimensions      (2 2)
total-size      4
rank            2
tick            1
its class example has a valid layout
it has 3 arrays:
 index 0, element type double-float, 2 slots
 index 1, element type (signed-byte 64), 1 slot
 index 2, element type double-float, 2 slots
it has 5 array slots:
 name x, index 0 offset 0
 name y, index 0 offset 1
 name r, index 1 offset 0
 name p, index 2 offset 0
 name q, index 2 offset 1

This is already too complicated: the ability to control sharing via groups is almost certainly never going to be useful: it's only even there because I thought of it quite early on and never removed it.

The class definition macro then needs to arrange life so that enough information is available so that a macro can be written which turns indexed slot access into indexed array access of the underlying arrays which are secretly stored in instances, inserting declarations to make this as fast as possible: anything slower than explicit array access is not acceptable. This might (and does) look like this, for example:

(with-array-slots (x y) (thing example)
  (for* ((i ...) (j ...))
    (setf (x i j) (- (y i j) (y j i)))))

As you can see from this, the resulting objects should be allowed to have rank other than 1. Inheritance should also work, including for array slots. Redefinition should be supported and obsolete macro expansions and instances at least detected.

In other words there are exactly two things I should have aimed at achieving: the ability to define fields of various types and have them grouped into (generally fewer) underlying arrays, and an implementational type to hold these things. Everything else was just unnecessary baggage which made the implementation much more complicated than it needed to be.

I had not finished making mistakes. The system needs to store some metadata about how slots map onto the underlying arrays, element types and so on, so the macro can use this to compile efficient code. There are two obvious ways to do this: use the property list of the class name, or subclass standard-class and store the metadata in the class. The first approach is simple, portable, has clear semantics, but it's 'hacky'; the second is more complicated, not portable, has unclear semantics1, but it's The Right Thing2. Another wrong decision I made without even trying.

The only thing that saved me was that the nature of software is that you can only make a finite number of bad decisions in a finite time.

More bad decisions

I was not done. Early on, I thought that, well, I could make this whole thing be a shim around defstruct: single inheritance was more than enough, and obviously I could store metadata on the property list of the type name as described above. And there's no nausea with multiple accessors or any of that nonsense.

But, somehow, I found writing a thing which would process the (structure-name ...) case of defstruct too painful, so I decided to go for the shim-around-defclass version instead. I even have a partly-complete version of the defstructy code which I abandoned. Another mistake.

I also decided that The Right Thing was to have the system support objects of rank 0. That constrains the underlying array representation (it needs to use rank \(n+1\) arrays for an object of rank \(n\)) in a way which I thought for a long time might limit performance.

Things I already knew

At any point during the implementation of this I could have told you that it was too general and the implementation was going to be too complicated for no real gain. I don't know why I made so many bad choices.

The whole process took weeks and I nearly just gave up several times.

The light at the end of the tunnel

Or: all-up testing.

Eventually, I had a thing I thought might work. The macro syntax was a bit ugly (that macro still exists, with a different name) but it seemed to work. But since the whole purpose of the thing was performance, that needed to be checked. I wasn't optimistic.

What I did was to write a version of my naïve gravitational many-body system using the new code, based closely on the previous one. The function that updates the state of the particles looks like this:

(defun/quickly step-pvs (source destination from below dt G &aux
                                (n (particle-vector-length source)))
  ;; Step a source particle vector into a destination one.
  ;;
  ;; Operation count:
  ;;  3
  ;;  + (below - from) * (n - 1) * (3 + 8 + 9)
  ;;  + (below - from) * (12 + 6)
  ;;  = (below - from) * (20 * (n - 1) + 18) + 3
  (declare (type particle-vector source destination)
           (type vector-index from)
           (type vector-dimension below)
           (type fpv dt G)
           (type vector-dimension n))
  (when (eq source destination)
    (error "botch"))
  (let*/fpv ((Gdt (* G dt))
             (Gdt^2/2 (/ (* Gdt dt) (fpv 2.0))))
    (binding-array-slots (((source particle-vector :check nil :rank 1 :suffix _s)
                           m x y z vx vy vz)
                          ((destination particle-vector :check nil :rank 1 :suffix _d)
                           m x y z vx vy vz))
      (for ((i1 (in-naturals :initially from :bound below :fixnum t)))
        (let/fpv ((ax/G zero.fpv)
                  (ay/G zero.fpv)
                  (az/G zero.fpv)
                  (x1 (x_s i1))
                  (y1 (y_s i1))
                  (z1 (z_s i1))
                  (vx1 (vx_s i1))
                  (vy1 (vy_s i1))
                  (vz1 (vz_s i1)))
          (for ((i2 (in-naturals n t)))
            (when (= i1 i2) (next))
            (let/fpv ((m2 (m_s i2))
                      (x2 (x_s i2))
                      (y2 (y_s i2))
                      (z2 (z_s i2)))
              (let/fpv ((rx (- x2 x1))
                        (ry (- y2 y1))
                        (rz (- z2 z1)))
                (let/fpv ((r^3 (let* ((r^2 (+ (* rx rx) (* ry ry) (* rz rz)))
                                      (r (sqrt r^2)))
                                 (declare (type nonnegative-fpv r^2 r))
                                 (* r r r))))
                  (incf ax/G (/ (* rx m2) r^3))
                  (incf ay/G (/ (* ry m2) r^3))
                  (incf az/G (/ (* rz m2) r^3))))))
          (setf (x_d i1) (+ x1 (* vx1 dt) (* ax/G Gdt^2/2))
                (y_d i1) (+ y1 (* vy1 dt) (* ay/G Gdt^2/2))
                (z_d i1) (+ z1 (* vz1 dt) (* az/G Gdt^2/2)))
          (setf (vx_d i1) (+ vx1 (* ax/G Gdt))
                (vy_d i1) (+ vy1 (* ay/G Gdt))
                (vz_d i1) (+ vz1 (* az/G Gdt)))))))
  destination)

And it not only worked, the performance was very close to the previous version, straight out of the gate. The syntax is not as nice as that of the initial, quick-and-dirty version, but it is much more general, so I think that's worth it on the whole.

There have been problems since then: in particular the dependency on when classes get defined. It will never be as portable as I'd like because of the unnecessary MOP dependencies3, but it is usable and quick4.

Was it worth it? May be, but it should have been simpler.


  1. When exactly do classes get defined? Right.

  2. Nothing that uses the AMOP MOP is ever The Right Thing, because the whole thing was designed by people who were extremely smart, but still not as smart as they needed to be and thought they were. It's unclear if any MOP for CLOS can ever be satisfactory, in part because CLOS itself suffers from the same smart-but-not-smart-enough problem to a large extent not helped by bring dropped wholesale into CL at the last minute: by the time CL was standardised people had written large systems in it, but almost nobody had written anything significant using CLOS, let alone the AMOP MOP.

  3. A mistake I somehow managed to avoid was using the whole slot-definition mechanism the MOP wants you to use.

  4. I will make it available at some point.

16 Apr 2026 11:01am GMT

feedDrupal.org aggregator

1xINTERNET blog: Drupal Is All In on AI. Now Comes the Hard Part

I co-founded 1xINTERNET on the conviction that Drupal was the right platform for ambitious web applications. AI changed that certainty. Here is what the Drupal AI Initiative is building, what organizations are getting first, and why the direction is clear.

16 Apr 2026 11:00am GMT

Drupal Starshot blog: Differentiating Marketplace Site Templates and Community Site Templates

Site templates are available through two distinct pathways, each serving different needs within the community.

The official Drupal.org Marketplace provides a curated collection of site templates that meet certain quality standards, and are built on top of Drupal CMS as a foundation.

Community templates offer an alternative pathway for innovation and experimentation without the constraints of the curation process, by publishing the template as a general project on Drupal.org.

Official Marketplace Site Templates

The Drupal.org Marketplace are built on top of Drupal CMS, and curated to provide new users with confidence that they're starting with a consistent, solid and professionally built foundation that follows established best practices.

Key characteristics

  • Templates undergo a review processes

  • Must follow Drupal CMS best practices for security, accessibility (WCAG 2.2 AA), performance, and code quality

  • In the beginning, focus is solely on growing Drupal CMS adoption; site templates accelerate adoption of Drupal CMS by providing context relevant demo content and Drupal Canvas-compatible theme

  • Clear documentation, maintenance commitments, and user support expectations

  • Currently open to Drupal Certified Partners (for organizations) and Ripplemakers (for individuals or very small companies). Apply to become a creator here.

Benefits

  • Consistency for users who need reliable, production-ready starting points

  • Quality assurance through professional review processes

  • Support and maintenance commitments for long-term sustainability

  • Revenue opportunities for professional template creators

  • Sustainability for the Drupal Association through revenue sharing

Community Site Templates

Anyone interested in contributing a template can do so now, by publishing it as a general project on Drupal.org. All free site templates, including marketplace templates, are general projects for packaging and distribution purposes. Community site templates will be considered for inclusion in the Drupal.org Marketplace based on their compatibility with the outlined criteria.

Key characteristics

  • Can be published without formal review or approval

  • Not bound by the same standards as Marketplace templates

  • Can be built using Drupal CMS or Drupal Core

  • Available to all community members

  • Can take risks and explore directions that might not fit Marketplace criteria

Benefits:

  • Innovation by removing barriers to experimentation

  • Diversity of approaches and implementations

  • Learning opportunities for the community to explore what's possible

  • Stepping stones that might eventually evolve into Marketplace templates

  • Lower barriers to entry for community contribution

16 Apr 2026 3:10am GMT

14 Apr 2026

feedPlanet Lisp

Robert Smith: Not all elementary functions can be expressed with exp-minus-log

By Robert Smith

All Elementary Functions from a Single Operator is a paper by Andrzej Odrzywołek that has been making rounds on the internet lately, being called everything from a "breakthrough" to "groundbreaking". Some are going as far as to suggest that the entire foundations of computer engineering and machine learning should be re-built as a result of this. The paper says that the function

$$ E(x,y) := \exp x - \log y $$

together with variables and the constant $1$, which we will call EML terms, are sufficient to express all elementary functions, and proceeds to give constructions for many constants and functions, from addition to $\pi$ to hyperbolic trigonometry.

I think the result is neat and thought-provoking. Odrzywołek is explicit about his definition of "elementary function". His Table 1 fixes "elementary" as 36 specific symbols, and under that definition his theorem is correct and clever, so long as we accept some of his modifications to the conventional $\log$ function and do arithmetic with infinities.

My concern is that the word "elementary" in the title carries a much broader meaning in standard mathematical usage. Odrzywołek recognizes this, saying little more than "[t]hat generality is not needed here" and that his work takes "the ordinary scientific-calculator point of view". He does not offer further commentary.

What is this more general setting, and does his claim still hold? In modern pure mathematics, dating back to the 19th century, the definition of "elementary function" has been well established. We'll get to a definition shortly, but to cut to the chase, the titular result does not hold in this setting. As such, in layman's terms, I do not consider the "Exp-Minus-Log" function to be the continuous analog of the Boolean NAND gate or the universal quantum CCNOT/CSWAP gates.

The rough TL;DR is this: Elementary functions typically include arbitrary polynomial root functions, and EML terms cannot express them. Below, I'll give a relatively technical argument that EML terms are not sufficient to express what I consider standard elementary functions.

To avoid any confusion, the purpose of this blog post is manifold:

  1. To elucidate what many mathematicians consider to be an "elementary function", which is the foundation for a variety of rich and interesting math (especially if you like computer science).
  2. To prove a result about EML terms using topological Galois theory.
  3. To demonstrate how this result may be used to show an elementary function not expressible by EML terms.

This blog post is not a refutation of Odrzywołek's work, though the title might be considered just as clickbait (and accurate) as his, depending on where you sit in the hall of mathematics and computation.

Disclaimer: I audited graduate-level mathematics courses almost 20 years ago, and I am not a professional mathematician. Please email me if my statements are clumsy or incorrect.

The 19th century is where all modern understanding of elementary functions was developed, Liouville being one of the big names with countless theorems of analysis and algebra named after him. One such result is about integration: do the outputs of integrals look the same as their inputs? Well, what does "input" and "look the same" mean? Liouville defined a class of functions called elementary functions, and said that the integral of an elementary function will sometimes be elementary, and when it is, it will always resemble the input in a specific way, plus potential extra logarithmic factors.

Since then, elementary functions have been defined by starting with rational functions and closing under arithmetic operations, composition, exponentiation, logarithms, and polynomial roots. While EML terms are quite expressive, they are unable to capture the "polynomial roots" in full generality. We will show this by using Khovanskii's topological Galois theory: the monodromy group of a function built from rational functions by composition with $\exp$ and $\log$ is solvable. For anybody that has studied Galois theory in an algebra course, this will be familiar, as the destination here is effectively the same, but with more powerful intermediate tooling to wrangle exponentials and logarithms.

First, let's be more precise by what we mean by an EML term and by a standard elementary function.

Definition (EML Term): An EML term in the variables $x_1,\dots,x_n$ is any expression obtained recursively, starting from $\{1, x_1,\dots,x_n\}$, by the rule $$ T,S \mapsto \exp T-\log S. $$ Each such term, evaluated at a point where all the $\log$ arguments are nonzero, determines an analytic germ; we take $\mathcal T_n$ to be the class of germs representable this way, together with their maximal analytic continuations.

Definition (Standard Elementary Function): The standard elementary functions $\mathcal{E}_n$ are the smallest class of multivalued analytic functions on domains in $\mathbb{C}^n$ containing the rational functions and closed under

What we will show is that the class of elementary functions defined this way is strictly larger than the class induced by EML terms.

Lemma: Every EML term has solvable monodromy group. In particular, if $f\in\mathcal T_n$ is algebraic over $\mathbb C(x_1,\dots,x_n)$, then its monodromy group is a finite solvable group.

Proof: We prove by induction on EML term construction. Constants and coordinate functions have trivial monodromy.

For the inductive step, suppose $f = \exp A-\log B$ with $A,B\in\mathcal T_n$, and assume that $\mathrm{Mon}(A)$ and $\mathrm{Mon}(B)$ are solvable. We argue in three steps.

Step 1: $\mathrm{Mon}(\exp A)$ is solvable. The germs of $\exp A$ are images under $\exp$ of the germs of $A$, with germs of $A$ differing by $2\pi i\mathbb Z$ collapsing to the same value. So there is a surjection $\mathrm{Mon}(A)\twoheadrightarrow\mathrm{Mon}(\exp A)$, and a quotient of a solvable group is solvable.

Step 2: $\mathrm{Mon}(\log B)$ is solvable. At a generic point $p$, germs of $\log B$ are parameterized by pairs $(b,k)$ where $b$ is a germ of $B$ at $p$ and $k\in\mathbb Z$ selects the branch of $\log$. A loop $\gamma$ acts by $$ (b,k)\mapsto\bigl(\rho_B(\gamma)(b), k+n(\gamma,b)\bigr), $$ where $\rho_B(\gamma)$ is the monodromy action of $\gamma$ on germs of $B$, and $n(\gamma,b)\in\mathbb Z$ is the winding number around $0$ of the analytic continuation of $b$ along $\gamma$. The projection $\mathrm{Mon}(\log B)\to\mathrm{Mon}(B)$ onto the first component is a surjective homomorphism. Its kernel consists of the elements of $\mathrm{Mon}(\log B)$ induced by loops $\gamma$ with $\rho_B(\gamma)=\mathrm{id}$, which then act only by integer shifts on the $k$-coordinate. Let $S_B$ be the set of germs of $B$ at $p$. For each $b\in S_B$, such a loop determines an integer shift $n(\gamma,b)$, so the kernel embeds in the direct product $\mathbb Z^{S_B}$. In particular, the kernel is abelian. Hence $\mathrm{Mon}(\log B)$ is an extension of $\mathrm{Mon}(B)$ by an abelian group, and extensions of solvable groups by abelian groups are solvable.

Step 3: $\mathrm{Mon}(f)$ is solvable. At a generic point, a germ of $f=\exp A-\log B$ is obtained by subtraction from a pair (germ of $\exp A$, germ of $\log B$), and analytic continuation acts componentwise on such pairs. This gives a surjection of $\pi_1$ onto some subgroup $$ H \le \mathrm{Mon}(\exp A)\times\mathrm{Mon}(\log B), $$ and, since $f$ is obtained from the pair by subtraction, this descends to a surjection $H\twoheadrightarrow\mathrm{Mon}(f)$. So $\mathrm{Mon}(f)$ is a quotient of a subgroup of a direct product of solvable groups, hence solvable.

The second statement of the lemma follows: an algebraic function has finitely many branches, so its monodromy group is finite; a solvable group that is finite is, well, finite and solvable. ∎

Remark. This is the core of Khovanskii's topological Galois theory; see Topological Galois Theory: Solvability and Unsolvability of Equations in Finite Terms.

Theorem: $\mathcal T_n \subsetneq \mathcal E_n$.

Proof: $\mathcal E_n$ is closed under algebraic adjunction, so any local branch of an algebraic function is elementary. In particular, a branch of a root of the generic quintic $$ f^5+a_1f^4+a_2f^3+a_3f^2+a_4f+a_5=0 $$ is elementary.

Suppose for contradiction that at some point $p$ a germ of a branch of this root agrees with a germ of an EML term $T$. By uniqueness of analytic continuation, the Riemann surfaces obtained by maximally continuing these two germs coincide, so in particular their monodromy groups coincide. The monodromy group of the generic quintic is $S_5$, which is not solvable. But by the lemma, the monodromy group of any EML term is solvable. Contradiction.

Hence $\mathcal T_n$ is a strict subset of $\mathcal E_n$. ∎

Edit (15 April 2026): This article used to have an example proving that the real and complex absolute value cannot be expressed over their entire domain as EML terms under the conventional definition of $\log$. I wrote it to emphasize that Odrzywołek's approach required mathematical "patching" in order to work as intended. However, it ended up more distracting than illuminating, and was tangential to the point about the definition of "elementary", so it has been removed.

14 Apr 2026 12:00am GMT

13 Apr 2026

feedPlanet Lisp

Scott L. Burson: FSet v2.4.2: CHAMP Bags, and v1.0 of my FSet book!

A couple of weeks ago I released FSet 2.4.0, which brought a CHAMP implementation of bags, filling out the suite of CHAMP types. 🚀 FSet users should have a look at the release page, as it also contained a number of bug fixes and minor changes.

I've since released v2.4.1 and v2.4.2, with some more bug fixes.

But the big news is the book! It brings together all the introductory material I have written, plus a lot more, along with a complete API Reference chapter.

FSet is now in the state I decided last summer I wanted to get it into: faster, better tested and debugged, more feature-complete, and much better documented than it has ever been in its nearly two decades of existence. I am, of course, very much hoping that these months of work have made the library more interesting and accessible to CL programmers who haven't tried it yet. I am even hoping that its existence helps attract newcomers to the CL community. Time will tell!

13 Apr 2026 6:21am GMT

06 Apr 2026

feedKernel Planet

Linux Plumbers Conference: Changes to Registration Availability for 2026

As most of you are painfully aware, Linux Plumbers Conference registrations can run out very fast (yes, we got lots of complaints last year). This year, we're taking a couple of steps to alleviate the issue. Firstly, we're expanding the venue size in Prague to match the number of attendees we got in Vienna (800) which will hopefully mean we have more than enough places to keep registration open all the way up to the beginning of the conference. Secondly, we're going to have an pre-registration period starting two weeks before general registration opens for anyone who submits content. The way this will work is that if you submit anything via indico before general registration opens, you'll receive a voucher and instructions to participate (this applies to every track and MC submission regardless of the accept/reject or pending state). The cost will be the same as general registration (US$600) but you'll be under no obligation to take up the voucher, which will expire when general registration opens. We're aligning the acceptance/rejection notices of the tracks we directly control (Refereed and Kernel Summit) to be complete around the time we open pre-registration. However, for other tracks and MC submissions that aren't aligned, if you take up an early registration voucher but are subsequently offered a free pass, we'll refund it (although if your company pays, we'd appreciate not having to since cvent charges us).

As a reminder of free pass distribution: every accepted Track Speaker (Refereed, Kernel Summit, Net, BPF and Toolchain) gets a free pass. However, Microconferences operate differently and accepted Microconference discussion leads may not receive a free pass (Microconferences have two free passes each and can distribute them arbitrarily to encourage key attendees).

The anticipated date for the opening of early registration is Friday 10 July 2026, but remember this may change due to logistical problems with the cvent website (which we don't control).

06 Apr 2026 4:13pm GMT

04 Apr 2026

feedPlanet Twisted

Donovan Preston: Using osascript with terminal agents on macOS

Here is a useful trick that is unreasonably effective for simple computer use goals using modern terminal agents. On macOS, there has been a terminal osascript command since the original release of Mac OS X. All you have to do is suggest your agent use it and it can perform any application control action available in any AppleScript dictionary for any Mac app. No MCP set up or tools required at all. Agents are much more adapt at using rod terminal commands, especially ones that haven't changed in 30 years. Having a computer control interface that hasn't changed in 30 years and has extensive examples in the Internet corpus makes modern models understand how to use these tools basically Effortlessly. macOS locks down these permissions pretty heavily nowadays though, so you will have to grant the application control permission to terminal. But once you have done that, the range of possibilities for commanding applications using natural language is quite extensive. Also, for both Safari and chrome on Mac, you are going to want to turn on JavaScript over AppleScript permission. This basically allows claude or another agent to debug your web applications live for you as you are using them.In chrome, go to the view menu, developer submenu, and choose "Allow JavaScript from Apple events". In Safari, it's under the safari menu, settings, developer, "Allow JavaScript from Apple events". Then you can do something like "Hey Claude, would you Please use osascript to navigate the front chrome tab to hacker news". Once you suggest using OSA script in a session it will figure out pretty quickly what it can do with it. Of course you can ask it to do casual things like open your mail app or whatever. Then you can figure out what other things will work like please click around my web app or check the JavaScript Console for errors. Another very important tips for using modern agents is to try to practice using speech to text. I think speaking might be something like five times faster than typing. It takes a lot of time to get used to, especially after a lifetime of programming by typing, but it's a very interesting and a different experience and once you have a lot of practice It starts to to feel effortless.

04 Apr 2026 1:31pm GMT

01 Apr 2026

feedKernel Planet

Dave Airlie (blogspot): drm subsystem contributor numbers

I'm doing a podcast recording this week, so I wanted to run some numbers so I could have some facts rather than feels. It turns out my feels were off by a factor of 3 or so.

If asked, I've always said the contributor count to the drm subsystem is probably in the 100 or so developers per release cycle.

Did the simplest:

git log --format='%aN' v6.14..v6.15 drivers/gpu/drm/ include/uapi/drm/ include/drm/ | sort -u | wc -l

Iterated over a few kernel releases

v6.15 326
v6.16 322
v6.17 300
v6.18 334
v6.19 332
v7.0-rc6 346

The number for the complete kernel in those scenarios are ~2000 usually, which means drm subsystem has around 15-16% of the kernel contributors.

I'm a bit spun out, that's quite a lot of people. I think I'll blame Sima for it. This also explains why I'm a bit out of touch with the process problems other maintainers have, and when I say stuff like a lot of workflows don't scale, this is what I mean.

01 Apr 2026 8:59pm GMT

Matthew Garrett: Self hosting as much of my online presence as practical

Because I am bad at giving up on things, I've been running my own email server for over 20 years. Some of that time it's been a PC at the end of a DSL line, some of that time it's been a Mac Mini in a data centre, and some of that time it's been a hosted VM. Last year I decided to bring it in house, and since then I've been gradually consolidating as much of the rest of my online presence as possible on it. I mentioned this on Mastodon and a couple of people asked for more details, so here we are.

First: my ISP doesn't guarantee a static IPv4 unless I'm on a business plan and that seems like it'd cost a bunch more, so I'm doing what I described here: running a Wireguard link between a box that sits in a cupboard in my living room and the smallest OVH instance I can, with an additional IP address allocated to the VM and NATted over the VPN link. The practical outcome of this is that my home IP address is irrelevant and can change as much as it wants - my DNS points at the OVH IP, and traffic to that all ends up hitting my server.

The server itself is pretty uninteresting. It's a refurbished HP EliteDesk which idles at 10W or so, along 2TB of NVMe and 32GB of RAM that I found under a pile of laptops in my office. We're not talking rackmount Xeon levels of performance, but it's entirely adequate for everything I'm doing here.

So. Let's talk about the services I'm hosting.

Web

This one's trivial. I'm not really hosting much of a website right now, but what there is is served via Apache with a Let's Encrypt certificate. Nothing interesting at all here, other than the proxying that's going to be relevant later.

Email

Inbound email is easy enough. I'm running Postfix with a pretty stock configuration, and my MX records point at me. The same Let's Encrypt certificate is there for TLS delivery. I'm using Dovecot as an IMAP server (again with the same cert). You can find plenty of guides on setting this up.

Outbound email? That's harder. I'm on a residential IP address, so if I send email directly nobody's going to deliver it. Going via my OVH address isn't going to be a lot better. I have a Google Workspace, so in the end I just made use of Google's SMTP relay service. There's various commerical alternatives available, I just chose this one because it didn't cost me anything more than I'm already paying.

Blog

My blog is largely static content generated by Hugo. Comments are Remark42 running in a Docker container. If you don't want to handle even that level of dynamic content you can use a third party comment provider like Disqus.

Mastodon

I'm deploying Mastodon pretty much along the lines of the upstream compose file. Apache is proxying /api/v1/streaming to the websocket provided by the streaming container and / to the actual Mastodon service. The only thing I tripped over for a while was the need to set the "X-Forwarded-Proto" header since otherwise you get stuck in a redirect loop of Mastodon receiving a request over http (because TLS termination is being done by the Apache proxy) and redirecting to https, except that's where we just came from.

Mastodon is easily the heaviest part of all of this, using around 5GB of RAM and 60GB of disk for an instance with 3 users. This is more a point of principle than an especially good idea.

Bluesky

I'm arguably cheating here. Bluesky's federation model is quite different to Mastodon - while running a Mastodon service implies running the webview and other infrastructure associated with it, Bluesky has split that into multiple parts. User data is stored on Personal Data Servers, then aggregated from those by Relays, and then displayed on Appviews. Third parties can run any of these, but a user's actual posts are stored on a PDS. There are various reasons to run the others, for instance to implement alternative moderation policies, but if all you want is to ensure that you have control over your data, running a PDS is sufficient. I followed these instructions, other than using Apache as the frontend proxy rather than nginx, and it's all been working fine since then. In terms of ensuring that my data remains under my control, it's sufficient.

Backups

I'm using borgmatic, backing up to a local Synology NAS and also to my parents' home (where I have another HP EliteDesk set up with an equivalent OVH IPv4 fronting setup). At some point I'll check that I'm actually able to restore them.

Conclusion

Most of what I post is now stored on a system that's happily living under a TV, but is available to the rest of the world just as visibly as if I used a hosted provider. Is this necessary? No. Does it improve my life? In no practical way. Does it generate additional complexity? Absolutely. Should you do it? Oh good heavens no. But you can, and once it's working it largely just keeps working, and there's a certain sense of comfort in knowing that my online presence is carefully contained in a small box making a gentle whirring noise.

01 Apr 2026 2:35am GMT

16 Mar 2026

feedPlanet Twisted

Donovan Preston: "Start Drag" and "Drop" to select text with macOS Voice Control

I have been using macOS voice control for about three years. First it was a way to reduce pain from excessive computer use. It has been a real struggle. Decades of computer use habits with typing and the mouse are hard to overcome! Text selection manipulation commands work quite well on macOS native apps like apps written in swift or safari with an accessibly tagged webpage. However, many webpages and electron apps (Visual Studio Code) have serious problems manipulating the selection, not working at all when using "select foo" where foo is a word in the text box to select, or off by one errors when manipulating the cursor position or extending the selection. I only recently expanded my repertoire with the "start drag" and "drop" commands, previously having used "Click and hold mouse", "move cursor to x", and "release mouse". Well, now I have discovered that using "start drag x" and "drop x" makes a fantastic text selection method! This is really going to improve my speed. In the long run, I believe computer voice control in general is going to end up being faster than WIMP, but for now the awkwardly rigid command phrasing and the amount of times it misses commands or misunderstands commands still really holds it back. I've been learning the macOS Voice Control specific command set for years now and I still reach for the keyboard and mouse way too often.

16 Mar 2026 11:04am GMT

04 Mar 2026

feedPlanet Twisted

Glyph Lefkowitz: What Is Code Review For?

Humans Are Bad At Perceiving

Humans are not particularly good at catching bugs. For one thing, we get tired easily. There is some science on this, indicating that humans can't even maintain enough concentration to review more than about 400 lines of code at a time..

We have existing terms of art, in various fields, for the ways in which the human perceptual system fails to register stimuli. Perception fails when humans are distracted, tired, overloaded, or merely improperly engaged.

Each of these has implications for the fundamental limitations of code review as an engineering practice:

Never Send A Human To Do A Machine's Job

When you need to catch a category of error in your code reliably, you will need a deterministic tool to evaluate - and, thanks to our old friend "alert fatigue" above - ideally, to also remedy that type of error. These tools will relieve the need for a human to make the same repetitive checks over and over. None of them are perfect, but:

Don't blame reviewers for missing these things.

Code review should not be how you catch bugs.

What Is Code Review For, Then?

Code review is for three things.

First, code review is for catching process failures. If a reviewer has noticed a few bugs of the same type in code review, that's a sign that that type of bug is probably getting through review more often than it's getting caught. Which means it's time to figure out a way to deploy a tool or a test into CI that will reliably prevent that class of error, without requiring reviewers to be vigilant to it any more.

Second - and this is actually its more important purpose - code review is a tool for acculturation. Even if you already have good tools, good processes, and good documentation, new members of the team won't necessarily know about those things. Code review is an opportunity for older members of the team to introduce newer ones to existing tools, patterns, or areas of responsibility. If you're building an observer pattern, you might not realize that the codebase you're working in already has an existing idiom for doing that, so you wouldn't even think to search for it, but someone else who has worked more with the code might know about it and help you avoid repetition.

You will notice that I carefully avoided saying "junior" or "senior" in that paragraph. Sometimes the newer team member is actually more senior. But also, the acculturation goes both ways. This is the third thing that code review is for: disrupting your team's culture and avoiding stagnation. If you have new talent, a fresh perspective can also be an extremely valuable tool for building a healthy culture. If you're new to a team and trying to build something with an observer pattern, and this codebase has no tools for that, but your last job did, and it used one from an open source library, that is a good thing to point out in a review as well. It's an opportunity to spot areas for improvement to culture, as much as it is to spot areas for improvement to process.

Thus, code review should be as hierarchically flat as possible. If the goal of code review were to spot bugs, it would make sense to reserve the ability to review code to only the most senior, detail-oriented, rigorous engineers in the organization. But most teams already know that that's a recipe for brittleness, stagnation and bottlenecks. Thus, even though we know that not everyone on the team will be equally good at spotting bugs, it is very common in most teams to allow anyone past some fairly low minimum seniority bar to do reviews, often as low as "everyone on the team who has finished onboarding".

Oops, Surprise, This Post Is Actually About LLMs Again

Sigh. I'm as disappointed as you are, but there are no two ways about it: LLM code generators are everywhere now, and we need to talk about how to deal with them. Thus, an important corollary of this understanding that code review is a social activity, is that LLMs are not social actors, thus you cannot rely on code review to inspect their output.

My own personal preference would be to eschew their use entirely, but in the spirit of harm reduction, if you're going to use LLMs to generate code, you need to remember the ways in which LLMs are not like human beings.

When you relate to a human colleague, you will expect that:

  1. you can make decisions about what to focus on based on their level of experience and areas of expertise to know what problems to focus on; from a late-career colleague you might be looking for bad habits held over from legacy programming languages; from an earlier-career colleague you might be focused more on logical test-coverage gaps,
  2. and, they will learn from repeated interactions so that you can gradually focus less on a specific type of problem once you have seen that they've learned how to address it,

With an LLM, by contrast, while errors can certainly be biased a bit by the prompt from the engineer and pre-prompts that might exist in the repository, the types of errors that the LLM will make are somewhat more uniformly distributed across the experience range.

You will still find supposedly extremely sophisticated LLMs making extremely common mistakes, specifically because they are common, and thus appear frequently in the training data.

The LLM also can't really learn. An intuitive response to this problem is to simply continue adding more and more instructions to its pre-prompt, treating that text file as its "memory", but that just doesn't work, and probably never will. The problem - "context rot" is somewhat fundamental to the nature of the technology.

Thus, code-generators must be treated more adversarially than you would a human code review partner. When you notice it making errors, you always have to add tests to a mechanical, deterministic harness that will evaluates the code, because the LLM cannot meaningfully learn from its mistakes outside a very small context window in the way that a human would, so giving it feedback is unhelpful. Asking it to just generate the code again still requires you to review it all again, and as we have previously learned, you, a human, cannot review more than 400 lines at once.

To Sum Up

Code review is a social process, and you should treat it as such. When you're reviewing code from humans, share knowledge and encouragement as much as you share bugs or unmet technical requirements.

If you must reviewing code from an LLM, strengthen your automated code-quality verification tooling and make sure that its agentic loop will fail on its own when those quality checks fail immediately next time. Do not fall into the trap of appealing to its feelings, knowledge, or experience, because it doesn't have any of those things.

But for both humans and LLMs, do not fall into the trap of thinking that your code review process is catching your bugs. That's not its job.

Acknowledgments

Thank you to my patrons who are supporting my writing on this blog. If you like what you've read here and you'd like to read more of it, or you'd like to support my various open-source endeavors, you can support my work as a sponsor!

04 Mar 2026 5:24am GMT

22 Jan 2026

feedPlanet Plone - Where Developers And Integrators Write

Maurits van Rees: Mikel Larreategi: How we deploy cookieplone based projects.

We saw that cookieplone was coming up, and Docker, and as game changer uv making the installation of Python packages much faster.

With cookieplone you get a monorepo, with folders for backend, frontend, and devops. devops contains scripts to setup the server and deploy to it. Our sysadmins already had some other scripts. So we needed to integrate that.

First idea: let's fork it. Create our own copy of cookieplone. I explained this in my World Plone Day talk earlier this year. But cookieplone was changing a lot, so it was hard to keep our copy updated.

Maik Derstappen showed me copier, yet another templating language. Our idea: create a cookieplone project, and then use copier to modify it.

What about the deployment? We are on GitLab. We host our runners. We use the docker-in-docker service. We develop on a branch and create a merge request (pull request in GitHub terms). This activates a piple to check-test-and-build. When it is merged, bump the version, use release-it.

Then we create deploy keys and tokens. We give these access to private GitLab repositories. We need some changes to SSH key management in pipelines, according to our sysadmins.

For deployment on the server: we do not yet have automatic deployments. We did not want to go too fast. We are testing the current pipelines and process, see if they work properly. In the future we can think about automating deployment. We just ssh to the server, and perform some commands there with docker.

Future improvements:

  • Start the docker containers and curl/wget the /ok endpoint.
  • lock files for the backend, with pip/uv.

22 Jan 2026 9:43am GMT

Maurits van Rees: Jakob Kahl and Erico Andrei: Flying from one Plone version to another

This is a talk about migrating from Plone 4 to 6 with the newest toolset.

There are several challenges when doing Plone migrations:

  • Highly customized source instances: custom workflow, add-ons, not all of them with versions that worked on Plone 6.
  • Complex data structures. For example a Folder with a Link as default page, with pointed to some other content which meanwhile had been moved.
  • Migrating Classic UI to Volto
  • Also, you might be migrating from a completely different CMS to Plone.

How do we do migrations in Plone in general?

  • In place migrations. Run migration steps on the source instance itself. Use the standard upgrade steps from Plone. Suitable for smaller sites with not so much complexity. Especially suitable if you do only a small Plone version update.
  • Export - import migrations. You extract data from the source, transform it, and load the structure in the new site. You transform the data outside of the source instance. Suitable for all kinds of migrations. Very safe approach: only once you are sure everything is fine, do you switch over to the newly migrated site. Can be more time consuming.

Let's look at export/import, which has three parts:

  • Extraction: you had collective.jsonify, transmogrifier, and now collective.exportimport and plone.exportimport.
  • Transformation: transmogrifier, collective.exportimport, and new: collective.transmute.
  • Load: Transmogrifier, collective.exportimport, plone.exportimport.

Transmogrifier is old, we won't talk about it now. collective.exportimport: written by Philip Bauer mostly. There is an @@export_all view, and then @@import_all to import it.

collective.transmute is a new tool. This is made to transform data from collective.exportimport to the plone.exportimport format. Potentially it can be used for other migrations as well. Highly customizable and extensible. Tested by pytest. It is standalone software with a nice CLI. No dependency on Plone packages.

Another tool: collective.html2blocks. This is a lightweight Python replacement for the JavaScript Blocks conversion tool. This is extensible and tested.

Lastly plone.exportimport. This is a stripped down version of collective.exportimport. This focuses on extract and load. No transforms. So this is best suited for importing to a Plone site with the same version.

collective.transmute is in alpha, probably a 1.0.0 release in the next weeks. Still missing quite some documentation. Test coverage needs some improvements. You can contribute with PRs, issues, docs.

22 Jan 2026 9:43am GMT

Maurits van Rees: Fred van Dijk: Behind the screens: the state and direction of Plone community IT

This is a talk I did not want to give.

I am team lead of the Plone Admin team, and work at kitconcept.

The current state: see the keynotes, lots happening on the frontend. Good.

The current state of our IT: very troubling and daunting.

This is not a 'blame game'. But focussing on resources and people this conference should be a first priority. We are a real volunteer organisation, nobody is pushing anybody around. That is a strength, but also a weakness. We also see that in the Admin team.

The Admin team is 4 senior Plonistas as allround admin, 2 release managers, 2 CI/CD experts. 3 former board members, everyone overburdened with work. We had all kinds of plans for this year, but we have mostly been putting out fires.

We are a volunteer organisation, and don't have a big company behind us that can throw money at the problems. Strength and weakness. In all society it is a problem that volunteers are decreasing.

Root causes:

  • We failed to scale down in time in our IT landscape and usage.
  • We have no clean role descriptions, team descriptions, we can't ask a minimum effort per week or month.
  • The trend is more communication channels, platforms to join and promote yourself, apps to use.

Overview of what have have to keep running as admin team:

  • Support main development process: github, CI/CD, Jenkins main and runners, dist.plone.org.
  • Main communication, documentation: pone.org, docs.plone.org, training.plone.org, conf and country sites, Matomo.
  • Community office automation: Google docds, workspacae, Quaive, Signal, Slack
  • Broader: Discourse and Discord

The first two are really needed, the second we already have some problems with.

Some services are self hosted, but also a lot of SAAS services/platforms. In all, it is quite a bit.

The Admin team does not officially support all of these, but it does provide fallback support. It is too much for the current team.

There are plans for what we can improve in the short term. Thank you to a lot of people that I have already talked to about this. 3 areas: GitHub setup and config, Google Workspace, user management.

On GitHub we have a sponsored OSS plan. So we have extra features for free, but it not enough by far. User management: hard to get people out. You can't contact your members directly. E-mail has been removed, for privacy. Features get added on GitHub, and no complete changelog.

Challenge on GitHub: we have public repositories, but we also have our deployments in there. Only really secure would be private repositories, otherwise the danger is that credentials or secret could get stolen. Every developer with access becomes an attack vector. Auditing is available for only 6 months. A simple question like: who has been active for the last 2 years? No, can't do.

Some actionable items on GitHub:

  • We will separate the contributor agreement check from the organisation membership. We create a hidden team for those who signed, and use that in the check.
  • Cleanup users, use Contributors team, Developers
  • Active members: check who has contributed the last years.
  • There have been security incidents. Someone accidentally removed a few repositories. Someone's account got hacked, luckily discovered within a few hours, and some actions had already been taken.
  • More fine grained teams to control repository access.
  • Use of GitHub Discussions for some central communication of changes.
  • Use project management better.
  • The elephant in the room that we have practice on this year, and ongoing: the Collective organisation. This was free for all, very nice, but the development world is not a nice and safe place anymore. So we already needed to lock down some things there.
  • Keep deployments and the secrets all out of GitHub, so no secrets can be stolen.

Google Workspace:

  • We are dependent on this.
  • No user management. Admins have had access because they were on the board, but they kept access after leaving the board. So remove most inactive users.
  • Spam and moderation issues
  • We could move to Google docs for all kinds of things. Use Google workspace drives for all things. But the Drive UI is a mess, so docs can be in your personal account without you realizing it.

User management:

  • We need separate standalone user management, but implementation is not clear.
  • We cannot contact our members one on one.

Oh yes, Plone websites:

  • upgrade plone.org
  • self preservation: I know what needs to be done, and can do it, but have no time, focusing on the previous points instead.

22 Jan 2026 9:43am GMT