24 Jul 2026
Django community aggregator: Community blog posts
Issue 347: Django 6.1 release candidate 1 released
News
Django 6.1 release candidate 1 released
This is the final opportunity to try out the new version before Django 6.1 is released. Try it, run your test suite, and report anything that breaks!
The DjangoCon US 2026 schedule has been released!
The talk lineup is out, covering Django 6.0 and 6.1 features, modern deployment patterns, GeoDjango at scale, and lightning talks across all three days.
PyPI Releases now reject new files after 14 days
PyPI will reject new files uploaded to releases older than 14 days to limit the impact of compromised publishing tokens or workflows.
Planned Updates to the PyPI User Interface
PyPI's first UI refresh since 2018 will roll out in phases over the coming months, surfacing more security signals on package pages. The first phase is staged on TestPyPI now and ready for your feedback.
Wagtail CMS News
What our AI guiding principles actually mean
Wagtail unpacks its refreshed AI guiding principles and how they steer adoption in practice, starting with a firm commitment: no AI dependency in Wagtail core, with AI features staying opt-in through packages like Wagtail AI.
Django Software Foundation
DSF Board monthly meeting, July 09, 2026
Minutes from this month's DSF Board meeting: a host for DjangoCon Europe 2027 was approved, a new Google Summer of Code Working Group was chartered, Executive Director hiring continues with guidance from the PSF, and grants went to PyCon Cameroon and PyCon Africa.
Updates to Django
Today, "Updates to Django" is presented by Raffaella from Djangonaut Space! 🚀
Last week we had 17 pull requests merged into Django by 11 different contributors - including 6 first-time contributors! Congratulations to Tom Most, CharulL00, Sina Chaichi Maleki, Harvey Bellini, Stephanie and Vismay for having their first commits merged into Django - welcome on board!
News in Django 6.2:
- The
MiddlewareMixinclass moved fromdjango.utils.deprecationtodjango.middleware. The old import path is deprecated in Django 6.2. - Whether to suppress an
ImportErrorescaping from a settings module is configurable by the newBaseCommand.requires_settingsattribute (defaultTrue). In previous versions, such errors were always suppressed. - The minimum supported version of
asgirefis increased from 3.9.1 to 3.12.1.
Thanks to the continuous efforts of the contributors, a SQLite regression test has also been added to inspectdb when a table has a foreign key that references sqlite_master. (#25243)
Support for prefers-color-scheme was also implemented, adding dark mode CSS overrides for the technical 500 (traceback) and 404 debug views. (#35875)
Django Fellow Reports
Django Fellow Report - Natalia
A security-heavy week: two patches for confirmed vulnerabilities, deep-dive reviews of two more, and prep for the August release with CVE metadata and prenotifications, plus continued iteration on EmailValidator improvements.
Django Fellow Report - Sarah
Reviews across Django and djangoproject.com, including the Selenium to Playwright migration and admin widget fixes, plus a new GitHub Action to test djangoproject.com against Django main and engagement on six security issues across Django and djangoproject.com.
Django Fellow Report - Jacob
Sustained attention on a couple of security reports, alongside triage and a long review list covering dark mode error pages, Oracle Test Pilot in CI, and the asgiref 3.12 update that enables free-threading tests.
Python Software Foundation
Get Ready: PSF Board Nominations Opening Soon!
PSF Board nominations open July 28, with voting September 1-15. If you're a voting member, affirm your intent to vote by August 25.
Get Ready: 2026 Python Packaging Council Nominations Opening Soon!
Nominations for the first-ever Python Packaging Council open July 28 and close August 11.
Events
Django Girls Chicago - August 22, 2026
Django Girls returns the Saturday before DjangoCon US in Chicago: build your first website, eat free food, and meet fellow aspiring Django developers. The free workshop is limited to 45 people and applications close August 12, so apply early.
Preparing for sprints as a project leader (at DjangoCon US)
As DjangoCon US approaches in just a few weeks time, here are some good tips on how to make the most out of the sprints following the tutorial talks.
A First-Timer's Guide to Navigating America
If you are attending DjangoCon US, please do follow the news section of the website, as it has helpful articles like this one, as well as info on childcare at the conference, and more.
Sponsored Link
When is it worth paying for a mentor?
Thinking about hiring a mentor to grow as a Django developer? Here are a few honest questions to help you get clarity on whether now's the right time.
Articles
Django: introducing django-crawl
Adam Johnson introduces django-crawl, a new package that crawls your whole site with Django's test client (via links, sitemaps, or a Python API) to surface broken pages before your users do.
Nifty Django Feature: Form Templates
Django's form templates separate a form's HTML from the view that processes it: set template_name on the form for one reusable layout, or on an individual field when a single input needs custom markup.
Some more things about Django I've been enjoying
Building a "2010 style" backend-heavy web app, this writeup highlights Django's readable query builders, handy template filters like querystring and json_script, and the comfort of automatic migrations. It also covers the author's practical performance questions, including a misconfigured cached template loader and why it mattered.
Browser Push Notifications for a Django Website
A step-by-step tutorial on adding browser Web Push notifications to a Django site using VAPID keys, a service worker, and a Huey background task, so you get OS-level notifications even when the admin tab is closed.
Is it time to go back to Django?
Some arguments for Django's opinionated, batteries-included approach in the AI-coding era, since it limits the decisions an AI agent has to make and reduces the chance of it going astray.
Deploying Web Apps in 2026: My EuroPython Conference Talk
The written version of Will Vincent's EuroPython talk, which maps today's hosting landscape and builds a ten-step mental model of everything your dev server quietly handles for you, from WSGI servers and static files to running migrations at release time.
My EuroPython 2026 - Paolo Melchiorre
A day-by-day recap of Paolo Melchiorre's EuroPython 2026 in Kraków, compiled from Mastodon posts and photos, from the Python Steering Council update to rethinking asyncio for free-threaded Python and time with the Django community at the booth.
EuroPython 2026 Recap - Will Vincent
Highlights from a packed week in Kraków, where the standout theme was agentic AI workflows, with teams split between off-the-shelf tools and heavy internal tooling.
PyCon US 2026 Recap - Katherine Michel
Katherine's famous PyCon recap is here! Security and AI front and center, PSF and PyPI updates, steering council priorities for free-threading, and lots of great pictures.
Events
Django on the Med
Three days of Django development sprints, September 23-25 in Pescara, Italy. The second edition is free to attend and gathers Fellows, board and Steering Council members, and contributors new and experienced to push Django forward.
Django Day Copenhagen 2026
October 2 in Copenhagen. The first three talks are by Marijke Luttekes, Efe Öge, and Denny Biasiolli.
Django Job Board
Three new remote openings join the board this week, from AI-native full-stack work at Hive Collective to Django backend engineering for genetic testing at MyOme and Python + TypeScript roles at Fusionbox.
Senior Full Stack Engineer at Hive Collective 🆕
Senior Backend Engineer at MyOme 🆕
Python + TypeScript Engineers at Fusionbox 🆕
Freelance Full-Stack Web App Developer at Mindrift
Projects
adamchainz/django-crawl
An in-process site crawler using Django's test client.
FROWNINGdev/django-orm-lens
See your entire Django schema (every model, field, and relationship) in your editor, terminal, or AI agent, one keystroke away from a live ER diagram.
24 Jul 2026 3:00pm GMT
Planet Python
Django Weblog: See You in Chicago in One Month!
In just one month, developers, maintainers, educators, and Django enthusiasts from around the world will gather in Chicago for DjangoCon US 2026.
DjangoCon US is more than a conference, it's a place to learn from the community, share ideas, contribute to Django, and make connections that last long after the event ends. Whether you're attending your first DjangoCon US or you've been coming for years, we're excited to welcome you.
Over five days, attendees will have the opportunity to:
- Learn from expert speakers and community members
- Participate in hands-on tutorials
- Collaborate during Django sprints
- Join Open Spaces and informal discussions
- Meet fellow Django developers from across the globe
If you're planning to join us, tickets are still available, and there's still time to reserve your stay at the conference hotel. The hotel room block deadline is August 3, so be sure to book your room before then.
August 24-28, 2026
Chicago, Illinois
We can't wait to see you in Chicago!
24 Jul 2026 11:00am GMT
Armin Ronacher: Codeberg Divides
Codeberg recently changed its terms to exclude projects that are largely written with generative AI. Since I want GitHub to face competition I have thoughts.
Codeberg is entirely within its rights to do this. It is an association with members and a democratic process, and that process produced a result. But democracy is a way of making a decision, not a guarantee that the decision is inclusive, wise, or even good for the people already depending on it. A majority can still decide that certain projects and people no longer belong.
GitHub's governance has never been democratic and there is plenty about the platform that I dislike. Yet democracy is not the main property I need from infrastructure. I need it to be predictable, dependable, and reasonably neutral towards the legal Open Source software hosted on it. A democratic provider without a clear constitution can be worse at those things than a corporation.
The actual wording makes this more difficult. The terms prohibit projects that mostly consist of code written by generative AI tools. In an actively developed codebase, what does "mostly" mean, and who can still tell? I could not reliably assign authorship percentages to many of my own recent projects. The line is open to interpretation precisely where it needs to be enforceable. In practice the center will probably lose out, as it has a bias.
A harsher line would probably be preferable. If Codeberg wants no LLM involvement, it should say so. If it wants to prevent autonomous repository spam and abusive resource consumption, it should write rules for those instead. The current middle ground delegates too much of the policy to moderators and community norms. I'm currently assuming the community around it draws a much harsher social boundary, making projects and maintainers unwelcome even when they technically comply.
It is a real shame that the Open Source and Free Software communities are splitting this deeply over LLMs and agents. There are serious questions about copyright, labor, energy use, slop, and maintainers drowning in generated contributions. But these tools are also becoming part of how software is made. The Open Source world needs to figure out how to engage with that future, not just divide into camps. More importantly, LLMs if done and used well, should be welcome to all of us. They could be used to reclaim control and power, away from large corporations and institutions.
As I mentioned before, I want GitHub to face true competition in the Open Source space. I would particularly like some of it to come from associations rather than another large corporation. As a European project, Codeberg naturally matters to me even more. It can choose to be a smaller community with a stronger political identity, but that is a different ambition from being a broad and dependable European alternative to GitHub.
I wish Codeberg were more forward-looking here: willing to host the Open Source software of tomorrow, not only software made in the ways its community approves of today. It has every right to make the choice it made, but I just do not think it is a good one.
24 Jul 2026 12:00am GMT
23 Jul 2026
Planet Python
Python Software Foundation: Get Ready: PSF Board Nominations Opening Soon!
Who runs for the PSF Board? People who care about the Python community, who want to see it flourish and grow, and also have a few hours a month to attend regular meetings, serve on committees, participate in conversations, and promote the Python community. We're looking for candidates with a diverse range of skills and backgrounds, including leadership experience, fundraising knowledge, non-profit familiarity, and event organizing. Technical expertise, a record of collaboration, and experience speaking or teaching in the Python community are also all qualities we hope to see in Board members.
Want to learn more about being on the PSF Board? Check out the following resources to learn more about the PSF, as well as what being a part of the PSF Board entails:
- FAQs About the PSF Board video on YouTube
- Our past few Annual Impact Reports:
Board Election Timeline
- Nominations open: Tuesday, July 28th, 2:00 pm UTC
- Nomination cut-off: Tuesday, August 11th, 2:00 pm UTC
- Announce candidates: Thursday, August 13th
- Voter affirmation cut-off: Tuesday, August 25th, 2:00 pm UTC
- Voting start date: Tuesday, September 1st, 2:00 pm UTC
- Voting end date: Tuesday, September 15th, 2:00 pm UTC
Not sure what UTC is for you locally? Check this UTC time converter!
Nominations
You can nominate yourself or someone else. If you're nominating someone else, we'd encourage you to reach out to them first to make sure they're excited about the opportunity and give them a heads up that they'll need to submit their own nomination statement via the nomination form. Take a look at last year's nomination statements for reference.
To submit a nomination for yourself or someone else, use the 2026 PSF Board Election Nomination Form on our website. The form will open on Tuesday, July 28th, 2:00 pm UTC and close on Tuesday, August 11th, 2:00 pm UTC.
To support potential candidates and nominators, the PSF has created a nomination resource (embedded below). It includes tips, formatting instructions, and guidance on what to include in a nomination. The goal is to help nominees understand what to expect and ensure that all candidates are provided the same clear and consistent standards.
Nominee Election Participation
PSF Board nominees will be invited to participate in the PSF Board Office Hour on the PSF Discord on September 8th at 1PM UTC. PSF Board Office Hours are a chance for the Python community to ask questions, share perspectives, and in this case, connect with PSF Board nominees. If you are unable to attend the sessions for whatever reason, that's totally fine, though we'd love to have each of you participate!
PSF Board nominees will also be invited to participate in text-based interviews that will result in content published on the PSF Blog. The interview questions will be similar to those used in the video interviews that have been produced in years past:
A current PSF Board member will reach out to you with instructions and field any questions you may have about the interviews. We ask that nominees keep an eye on their email inboxes during the nomination period and right after so that we can ensure your interview responses get published for the Python community's consideration.
Voting Affirmation Reminder
Every PSF Voting Member (Supporting, Contributing, and Fellow) must affirm their intention to vote no later than Tuesday, August 25th, 2:00 pm UTC, to participate in this year's election. You should have received an email from "psf@psfmember.org <Python Software Foundation>" with the subject "[Action Required] Affirm your PSF Membership voting intention for 2026 PSF Board Election" that contains information on how to affirm your voting status.
You can see your membership record and status on your PSF Member User Information page. If you are a voting-eligible member and do not already have a login, please create an account on psfmember.org first and then email psf-elections@pyfound.org so we can link your membership to your account.
23 Jul 2026 5:16pm GMT
22 Jul 2026
Django community aggregator: Community blog posts
Tracking Blips
bliptracker was a side project that I happened to produce during June and last week realised I hadn't written about it here, so here goes!
One annoyance I have with Claude.ai (or other web based LLM interfaces), is that I would start multiple conversations across multiple topics such as client work, organising my Todoist, an idea to explore, gifts to research, the list goes on, but I was keeping open tabs for each conversation to not lose track of the active conversations, but this didn't work as I still had those open loops in my head to follow up to move each conversation forwards.
I didn't want a full blown task manager (I pay for Todoist which fits perfectly), but I did want to track the state of each conversation in Claude from both the web app and the mobile. The result is a two fold solution, first there is a system prompt telling Claude to end each respond with either a 🔴, along with the next action required from me, or a ✅ which tells me the conversation is resolved. The second part of the solution is a Chrome extension which then automatically updates the title of any conversation with the red dot or green check mark, so I can tell at a glance which chats need work and which are done.
I do have a couple more features planned such as supporting other LLMs and a possible snooze feature. But for now it's a small working project that keeps my chats organised. It's available at bliptracker.xyz.
One final point on this project, I hope that eventually it gets replace by Anthropic building a better native product for tracking the status of chats, it's very limited right now. More widely this highlights that while new models are powerful and can do more, it still requires us as engineers to build products that solve actual problems in novel, tasteful and well designed solutions. That is what we pay for when buy a tool and what our users expect from us and something that no model as far as I can see will ever replace.
22 Jul 2026 5:00am GMT
Django: introducing django-crawl
I recently migrated one of my client projects from the legacy django-csp package to Django 6.0's built-in Content Security Policy (CSP) support (release note). This security header is a powerful tool for preventing unwanted content from being loaded on your site, so configuration correctness is paramount. The migration was fairly straightforward, but a few pages had complicated overrides, so I wanted to be sure that no CSP headers had been changed by my swapping of CSP implementations.
I had the idea to verify no page had changed its content-security-policy header by crawling the site with Django's test client, outputting URL and header contents during the process. By diffing the output from crawls before and after the migration, I could check for changes and track down which pages had been affected.
The core loop of that script looked something like this:
from collections import deque
from django.test import Client
client = Client()
client.force_login(superuser)
queue: deque[str] = deque(["/", "/admin/"])
...
while queue:
url = queue.popleft()
...
response = client.get(url, follow=False)
...
print(f"{url}\t{response.headers.get('content-security-policy')}")
...
for anchor in BeautifulSoup(response.content, "html.parser").find_all(
"a", href=True
):
# Enqueue these found links
...
This simple crawl of the site ended up flushing out seven non-CSP bugs, despite the project having 100% test coverage and a full suite of integration tests. Those bugs were due to incorrect link generation, admin features not being disabled, and regular old broken code.
I was pretty impressed with the power of this technique for finding broken stuff! Given this experience, I wanted to expand the script into a reusable tool, which I have now done with django-crawl.
To use django-crawl, install it, add it to INSTALLED_APPS, and you can run the crawl management command:
$ ./manage.py crawl -v 2
🐛 Crawling up to 1000 URLs, logged in as Ad Min
/
/about/
/blog/
/contact/
/dev/
/blog/2026/
/blog/2025/
/blog/2026/07/22/introducing-django-crawl/
...
🦋 Crawled 1000 URLs, encountered 0 errors, stopped due to reaching max URL limit of 1000.
The command reports any errors it encounters, from broken links to exceptions. The output uses Rich for pretty formatting and a live spinner while it runs. Adding -v 2 prints the URLs as they are crawled.
The crawler discovers links in various forms in HTML responses (<a href>, <link href>, <script src>, <img src>, etc.), sitemaps, and feeds. If you have a sitemap, you can start your crawl with a simple:
$ ./manage.py crawl /sitemap.xml
The HTML parsing is done with a custom Rust extension built with html5ever, the HTML parser from the Servo project, so it's very fast. And with no overhead from real HTTP requests or inter-process communication, the crawl is limited only by how fast your application code can run.
django-crawl also provides a Python API that you can use to make a mega-test that crawls your whole site with example data, raising an ExceptionGroup if any errors are encountered:
from django.contrib.auth.models import User
from django.test import TestCase
import django_crawl
class CrawlTests(TestCase):
@classmethod
def setUpTestData(cls):
cls.admin = User.objects.create_superuser(username="admin")
def test_crawl(self):
client = django_crawl.CrawlClient()
client.force_login(self.admin)
django_crawl.crawl("/", "/admin/", client=client)
I am not sure if this is a good fit for most projects, since it will leave you with one long test that exercises many views. But it might be good for building a "safety net" on untested projects, something I know Jeff Triplett likes to do (ref Django Chat #24).
Fin
Please try out django-crawl today and let me know how it goes.
May your site not slow to a crawl,
-Adam
22 Jul 2026 4:00am GMT
23 Jun 2026
Planet Twisted
Glyph Lefkowitz: Adversarial Communication
As I have discussed in previous posts, "AIs" can make mistakes. In fact, they do make mistakes, and their mistake-making patterns are such that where and how they will make mistakes is both uncertain and constantly changing.
Thus, in any scenario where you want to attempt to make "productive" use of "AI", you must have a system in place for checking every result. Not checking some results; checking every result. If each result might have a consequence for you (and if it didn't have a consequence, why bother automating it?) and you cannot predict in advance which kinds of results will need verification, then verification is always required.
The verification often ends up being just as expensive as doing the work in the first place, which means that if you want your usage of "AI" to be personally profitable, you have to find someone else to externalize the cost of verification onto. This person becomes your adversary, and, if you are successful, your "AI's" victim.
The Ladder-Climber And Their Reverse-Centaur Rungs
One way that this constellation of facts can straightforwardly assemble themselves into a dystopian nightmare is the phenomenon, described by Cory Doctorow, of the reverse centaur. This is when your employer non-consensually turns you into the verification system. The "AI" does the fun part of initially performing the work, and then you do the boring part where you check if the robot is right and clean up its messes, even if everyone already knows that it would, in aggregate, be cheaper for you to do the work in the first place.
Reverse centaurs can be made from any automation, not only "AI" automation. I think that there is a reason that this term happens to have emerged in the "age of AI", though, and not with earlier automation technologies (even those which were considerably more viscerally horrific). That reason is: the wrongness of "AI" output is not merely a technical feature that must be compensated for, it is a generalized externality.
As I mentioned above, if you are responsible for the entirety of the work, both extruding the "AI" output and checking it, it's usually cheaper to have humans do the entirety of the work to begin with. When humans do the writing directly, we can check as we go, and thus verification doesn't need to be as comprehensive.
When "AI" coding advocates say "code review is the bottleneck", what they are observing is that the LLM is still rolling the dice for each PR, and a human is still necessary to verify that each of those rolls is a winner. But calling this process "code review" is a bit of a misnomer; it's not really "code review" in the traditional sense, it's human understanding.
Before the advent of "AI", the human understanding was implicit in the process of writing the code in the first place1, and the code review was a way of diffusing and extending that understanding. Now that the code can be authored with no initial understanding taking place, that cost has not gone away, it has moved.
Human understanding was always the bottleneck.
However, this is taking a collaborative view of a software project, where satisfying the needs and solving the problems of your customers are the goals. We can see that "AI" is a bad tool to satisfy those goals, because all it's doing is converting the first half of the work, that of understanding the code as you write it, to understanding the agent's output as you read it.
What if, instead, we were to take the view that every software company is a Hobbesian nightmare, red in tooth and claw? In this view, the only goal of a software project is for the individual developers to make their promo cycles and get their bonuses. Given that there is only a certain amount of money to go around, this is a zero-sum game where each programmer wants to look more productive than their colleagues.
Pretty much every organization finds it easy to reward "productivity" as expressed by lines of code emitted, but the benefits of doing thorough and thoughtful design, analysis, and code review very difficult to reward. In this world, an LLM is an invaluable tool for the sociopathic ladder-climber, particularly if your legacy organization is still structuring their workflows as if the person prompting the bot is "writing" the code, and then they get to foist off the act of "reviewing" the code onto someone else.
Here, the prompter effectively externalizes the cost of the LLM's failures but internalizes any benefits. The prompter will vibe-code a big feature, so large that the assigned reviewer can't possibly comprehend it all effectively. When this happens, the reviewer will, eventually, be pressured to approve it, even if they can try to spot a few problems along the way. The reviewer has their own work to get back to, after all, the obligation to review the prompter's (read: the bot's) code is a drain on their time that they are not going to get rewarded for.
If this feature is a big success, the prompter gets a promotion. If it causes a big issue, well, the reviewer must not have been careful enough.
This is why LLMs are "good for coding", and also why their biggest promoters keep having outages.
The Generative Gish Galloper
Coding is the biggest "success story" of this type of adversarial communication, but it is by far not the only instance of such a thing. LLMs create a new form of leverage that can turn Brandolini's law from a linear advantage into an exponential one. If you are engaged in a political debate where you want to overwhelm the other side in nonsense, an LLM can generate bullshit faster than it is physically possible for a human being to type, let alone respond thoughtfully. There is an asymmetry to the utility of this weapon as well: only one side of the political spectrum wants to flood the zone and destroy trust in institutions and the concept of truth. There's a good reason that the fascists love it.
Straightforward Spam and Fraud
This is kind of obvious, but LLMs can generate lightly-customized, plausible-looking text much more quickly than any human being. This facilitates their use in fraud, spam, and scams. In a spamming or fraudulent interaction, once again, the costs are externalized onto the victim: the recipient of a spam message has to do all the work of "checking" the LLM's output. Spammers already expect very low hit rates from boilerplate, and if the LLM can increase those percentages from 1% to 5% the technology will pay for itself; they don't need anything like reliable accuracy.
Customer "Support"
If you have any kind of commercial relationship with a company, I probably don't even need to mention this: customer "support" bots are a misery. Everybody knows it at this point. But customer support is usually conceptualized by businesses as an adversarial interaction, because it is a cost center. They maintain internal metrics on time-to-resolution and try to optimize them. Implicitly, this creates a dynamic where the goal of the customer service agent's job is not to solve your problem, but to emit noise that will cause you to think your problem is resolved, or to give up, as fast as possible. Unsurprisingly, LLMs can emit this noise faster than humans can, getting those customers off the phone. But those customers will remember those interactions, and the story outside the TTR metrics is horrible.
Similarly to the situation in software development, LLMs can look very good on paper for customer support, but mostly what they are doing is illuminating the problems with the industry's existing metrics, by turning "winning the metrics battle against the customer" into a more obvious and immediate defeat for the company's long term reputation.
"Education"
In 2026 it is sadly a fact of life that students cheat all the time using "AI", and that this cheating is very successful, in that the teachers find it very hard to detect.
LLMs are great for cheating on schoolwork because the student is externalizing the work of the checking onto the teachers, who are often starting at a disadvantage to begin with, at least in the US.
My view is that this is happening because of a divergence in the way that students vs. teachers (or, more accurately, "the broader educational system") view grading.
When a student is asked to write an essay, the teachers see the effort as both intrinsically worthwhile for the student, as well as useful as a pedagogical tool to evaluate and react to the student's progress. The student, by contrast, sees a stumbling block designed to knock them off the path to success and into a permanent underclass. It is no wonder that the student sees "AI" as useful to their own goals and has no compunction about deploying it.
There is a bitter irony that the ability to understand the inherent value of actually writing the essay on their own is the sort of thing that students can really only learn by writing a bunch of essays. There's no way that I can think of which makes the benefit legible as long as a shortcut is available.
The net effect here is a downward spiral, where the already-wobbling educational system is sustaining an attack that it doesn't have the resources to recover from. The individual students' attacks against their teachers and their schools' grading systems might appear to momentarily succeed, but they will win the battle and lose the war.
Spamming "For Good"?
Usually when we talk about someone unilaterally choosing to enter into an adversarial relationship, that's an "attack" and for good reasons we have a negative impression of the attacker. However, I would be remiss if I did not point out that there are some cases where the relationship was already adversarial; just because you're the attacker doesn't mean that you are evil.
For example we might imagine use-cases like automatically filing appeals for prior authorizations against health insurance. It's relatively well-known at this point that the main way for-profit insurers maintain their margins is by denying claims right up to the line of the policies themselves being fraud, so using a spamming tool to fight them might be entirely justifiable2 in that case.
Similarly, using an LLM could be justified in a fight against a company refusing to honor a warranty. One could imagine using an LLM to immediately generate replies and escalations.
However, even in imagined cases like these, the underlying problem is that the insurers and the vendors already have a tremendous amount of structural power, so it is more likely that they will have the advantage in deploying a communications weapon like an LLM, as well as enacting policies to simply ignore any LLM-based communication that you might submit. Worse, if these strategies were to become widespread, they might provide an excuse to reject any communications by feeding them into an unreliable "LLM detector" and issuing an automated "computer says no" even to hand-written correspondence.
It is also worth stressing that these cases are imagined, as compared to the very real coworker-abuse, spam, scam, fraud, and disinformation campaigns being waged in real life today.
Therefore, while legitimate uses might exist, it's hard to imagine that there's anywhere they would be genuinely valuable and sustainable. In the best case "AI" will provide a temporary advantage for underdogs that will provoke an arms race which the resource-advantaged adversaries will win in the long run, in the worst case the arms race itself will cement permanent structural change that will make things worse.
"Search" By Stealing
Most of the adversarial utility of "AI" is on the "write" side, since write-amplification is more obviously aggressive than reading. But the "read" side of LLMs - summarization and question-answering - can be a form of attack as well.
To begin with, the act of reading itself is currently enormously destructive, but that's arguably not a fundamental aspect of this technology. They could set reasonable rate-limits and respect things like robots.txt, as search engines have for decades now. They could also refrain from committing criminal levels of copyright infringement. But, today, using "AI" tools does suborn this sort of out-of-control crawling.
More insidiously, consider the scenario described in this YouTube video. The LTT Bros decided to try Linux again, and in the course of so doing, they had problems. When trying to solve these problems, they were faced with a choice: they could consult Reddit, or they could ask an LLM. Asking an LLM would "gaslight the heck out of" them, but they still found it preferable, because they would at least get an answer without getting yelled at.
Initially this sounds great. But it also means that you want to extract knowledge from a community, while mechanically eliding any values or norms that the community may want to impart as part of offering that knowledge. As someone who spent many years in a community tech support role, this is worrying. Many requests for support are people asking how to do things that will momentarily solve a superficial problem but create a long-term reliability problem or even an immediate security risk, that the question-asker doesn't want to hear about. Consider the question "I'm tired of entering my password so much, how do I make it so my laptop unlocks automatically". An obsequious chatbot will helpfully tell you how to do this without pushback.
But, this is also a sort of ethically murky area. The Linux community is somewhat famously, for many years now, a toxic cesspool of general hostility, misogyny, etc. It is certainly a good thing that people can get access to this knowledge without subjecting themselves to abuse. But it also means that the people with the power and the privilege to change the community for the better can just quietly withdraw, rather than fixing the problems. It also means that the positive elements of culture cannot be transmitted, and people will have no opportunity to learn about unknown unknowns.
In this case, the "adversarial" communication is with society. The thing that using an LLM for search lets you do is withdraw from society and avoid forming any personal connections. There are some personal connections which are painful and annoying, and so that can feel like a momentary balm. But the need to make connections in general is, like, the concept of society itself.
Who Am I Hurting?
LLMs are good at adversarial communication. They are so good at it, relative to their other benefits, that they will tend to make communications adversarial if you are not remaining vigilant about the possibility that it might do so. My request to you, dear reader, if you are going to use such tools, is to always ask yourself, "who might I be hurting, if I use an LLM for this?"
If you're using an "AI", who is its adversary? If you haven't given it one yet, who might the "AI" turn into an adversary? Who might you overwhelm with an asymmetric amount of output, or, if you're receiving information and not sending it, who are you taking that information from without consulting?
Figure out the answers to these questions and conduct yourself accordingly; the answer might be "yourself".
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!
-
One of the reasons that software developers tend to prefer greenfield development is that when you are given a blank page, you can project your own specific understanding onto it. You can structure the codebase in a way that works for your brain, down to the variable naming conventions and the module layouts. LLM-assisted development makes everything into instant brownfield work, which makes developers instantly miserable; even those who are excited about the technology will frequently complain about how it feels like their agency has been stolen and their joy in the work has been diminished. But I digress. ↩
-
Modulo the massive amount of other externalities involved in using LLMs, of course, but I don't have the time or energy to get into those here. ↩
23 Jun 2026 8:06pm GMT
09 Jun 2026
Planet Twisted
Hynek Schlawack: How to Ditch Codecov for Python Projects
Codecov's unreliability breaking CI on my open source projects has been a constant source of frustration for me for years. I have found a way to enforce coverage over a whole GitHub Actions build matrix that doesn't rely on third-party services.
09 Jun 2026 12:00am GMT
22 May 2026
Planet Twisted
Glyph Lefkowitz: Opaque Types in Python
Let's say you're writing a Python library.
In this library, you have some collection of state that represents "options" or "configuration" for a bunch of operations. Such a set of options is a bundle of potentially ever-increasing complexity. Thus, you will want it to have an extremely minimal compatibility surface, with a very carefully chosen public interface, that is either small, or perhaps nothing at all. Such an object conveys state and might have some private behavior, but all you want consumers to be able to do is build it in very constrained, specific ways, and then pass it along as a parameter to your own APIs.
By way of example, imagine that you're wrapping a library that handles shipping physical packages.
There are a zillion ways to do it ship a package. There are different carriers who can ship it for you. There's air freight, and ground freight, and sea freight. There's overnight shipping. There's the option to require a signature. There's package tracking and certified mail. Suffice it to say, lots of stuff.
If you are starting out to implement such a library, you might need an object called something like ShippingOptions that encapsulates some of this. At the core of your library you might have a function like this:
1 2 3 4 5 |
|
If you are starting out implementing such a library, you know that you're going to get the initial implementation of ShippingOptions wrong; or, at the very least, if not "wrong", then "incomplete". You should not want to commit to an expansive public API with a ton of different attributes until you really understand the problem domain pretty well.
Yet, ShippingOptions is absolutely vital to the rest of your library. You'll need to construct it and pass it to various methods like estimateShippingCost and shipPackage. So you're not going to want a ton of complexity and churn as you evolve it to be more complex.
Worse yet, this object has to hold a ton of state. It's got attributes, maybe even quite complex internal attributes that relate to different shipping services.
Right now, today, you need to add something so you can have "no rush", "standard" and "expedited" options. You can't just put off implementing that indefinitely until you can come up with the perfect shape. What to do?
The tool you want here is the opaque data type design pattern. C is lousy with such things (FILE, pthread_*_t, fd_set, etc). A typedef in a header file can easily achieve this.
But in Python, if you expose a dataclass - or any class, really - even if you keep all your fields private, the constructor is still, inherently, public. You can make it raise an exception or something, but your type checker still won't help your users; it'll still look like it's a normal class.
Luckily, Python typing provides a tool for this: typing.NewType.
Let's review our requirements:
- We need a type that our client code can use in its type annotations; it needs to be public.
- They need to be able to consruct it somehow, even if they shouldn't be able to see its attributes or its internal constructor arguments.
- To express high-level things (like "ship fast") that should stay supported as we add more nuanced and complex configurations in the future (like "ship with the fastest possible option provided by the lowest-cost carrier that supports signature verification").
In order to solve these problems respectively, we will use:
- a public
NewType, which gives us our public name... - which wraps a private class with entirely private attributes, to give us an actual data structure, while not exposing the constructor,
- a set of public constructor functions, which returns our
NewType.
When we put that all together, it looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
As a snapshot in time, this is not all that interesting; we could have just exposed _RealShipOpts as a public class and saved ourselves some time. The fact that this exposes a constructor that takes a string is not a big deal for the present moment. For an initial quick and dirty implementation, we can just do checks like if options._speed == "fast" in our shipping and estimation code.
However, the main thing we are doing here is preserving our flexibility to evolve the related APIs into the future, so let's see how we might do that. For example, let's allow the shipping options to contain a concrete and specific carrier and freight method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
As a NewType, our public ShippingOptions type doesn't have a constructor. Since _RealShipOpts is private, and all its attributes are private, we can completely remove the old versions.
Anything within our shipping library can still access the private variables on ShippingOptions; as a NewType, it's the same type as its base at runtime, so it presents minimal1 overhead.
Clients outside our shipping library can still call all of our public constructors: shipFast, shipNormal, and shipSlow all still work with the same (as far as calling code knows) signature and behavior.
If you need to build and convey some state within your public API, while avoiding breakages associated with compatibility churn, hopefully this technique can help you do that!
Acknowledgments
Thanks for reading, and 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.
-
The overhead is minimal, but it is not completely zero. The suggested idiom for converting to a
NewTypeis to call it like a function, as I've done in these examples, but if you are wanting to use this pattern inside of a hot loop, you can use# type: ignore[return-value]comments to avoid that small cost. ↩
22 May 2026 12:33am GMT