20 Jul 2026
Django community aggregator: Community blog posts
Best Django Redis configuration for speed and size
`lzma` compresses the most and `zlib` is about as fast as `zstd` in `django_redis` as compressor.
20 Jul 2026 1:01am GMT
19 Jul 2026
Django community aggregator: Community blog posts
How I got Claude certified (and how you can too)

I'm Claude Certified Architect - Foundations now. Look at me.
A friend from work, Daniel, pinged me right after he started studying because he was confused and a little frustrated. He'd gone through the official docs, done a couple of practice exams, and felt like there was no connection between what he was studying and what the exam was actually testing. Fair. I felt the same way when I started. So we hopped on a call, I dumped everything I knew, and this post is basically that call cleaned up so more people can use it.
Fair warning up front: this is part study guide, part honest review. I'll tell you how to pass, but I'm also going to be honest about the parts that felt like studying for the exam rather than becoming a better engineer. Both things are true at once.
What I studied with
The exam I took is the Claude Certified Architect - Foundations. The single most useful resource for me was the Claude Certification Guide. The lessons are very good, and everything that showed up on my exam was covered by the syllabus there. I took notes like a maniac: more than half of a physical notebook, front and back, is just Claude notes from that site. I still recommend it without hesitation, because I genuinely learned a lot from it, not just enough to pass.
The thing I liked most about it is that it teaches you to recognize the pitfalls the exam throws at you, without spoon-feeding you exactly what they are. So instead of memorizing "the answer is X," you build the instinct to spot what's going on. I prefer that. It stuck better.
One warning: don't over-index on practice exams. I did five or ten before the real thing, and the free mock exams floating around out there are way too basic compared to what you'll actually face. The guide's questions are the closest I found, and even those I wouldn't call identical to the real exam. Daniel was studying with a different mock exam site and the questions were so convoluted and samey that he said he fell asleep halfway through. So calibrate your expectations: use the mocks to get familiar with the format, not to predict the exact questions.
Study Claude CLI. Seriously. Study it.
This was the hardest part of the exam by far, and the part I was least prepared for.
I genuinely did not think they were going to ask about specific flags. The Claude CLI has something like a thousand of them, and my attitude going in was "I'm not learning all of this, if I need a flag I'll just run claude --help." In the real world, that's exactly what you do. Why would you memorize flags you can look up in two seconds? Daniel made this same point on our call, and honestly I agree with him.
But the exam doesn't care about that argument. They started asking about specific flags and what they do, and I sat there thinking I messed up.
So here's my advice: study the flags. You don't need to memorize all thousand of them, but go through the ones on the certification course and at least read each one and understand what it means. Not just claude -p. All of them. If you do one thing differently from how I studied, make it this.
Read the questions. Then read them again.
The exam is 60 questions, and you get 2 and a half hours. That's more than enough time. I finished the whole thing in about an hour and a half, went back through everything, and still submitted with time to spare. So don't rush.
But do read carefully, because the questions are convoluted. Sometimes it feels like they're testing whether you can read a wall of text, parse it, and pick the best answer, more than whether you know the material. English isn't my first language, and after about 45 minutes of dense, twisty questions I was toast. Budget your brain, not just your time.
A few tactics that worked for me:
- Read the full question before you even look at the answers. Give it 30 to 45 seconds. If it's obviously straightforward, great, move on. If it's not, you'll be glad you understood the setup before the answers tried to trick you.
- Don't get stuck. If you've spent two or three minutes on one question, pick something, flag it, and move on. You can always come back.
- Use the "flag for review" feature. Before you submit, the exam shows you a list of everything you answered plus everything you flagged. I went back through all my flagged ones, re-read them, and changed a couple of answers. Then, with 30 minutes left, I went through the entire exam one more time reading each prompt and my answer. Was that overkill? Probably. Did it work? Also yes. I'm thorough like that.
The "pick the most correct answer" trap
This is the thing that trips everyone up, so it gets its own section.
A lot of questions have two answers that both technically work. The exam wants you to pick the one that's most correct, and "most correct" almost always means "the most Claude-native way of doing it."
Concrete example from the kind of question you'll see: you've got a process that runs sequentially and takes too long, and you need to cut the latency. One option is to implement a message queue to parallelize the slow tasks. Another is to spawn additional agents so the work gets split up. Both reduce latency. Both are reasonable. But Claude doesn't have a message queue, and the exam heavily favors two ideas: use Claude for everything, and let the agent take control. The parallel-agents answer wins because Claude decides how many agents to spawn, and if one fails it can respawn it. The queue answer works in real life, but it's not the answer the exam is looking for.
So when two answers both work, ask yourself: which one leans hardest into letting Claude do the thing? That's usually the "most correct" one.
The exam-day logistics nobody warns you about
The exam costs 125 USD, it's fully proctored, and the proctoring is strict. Here's what actually happens so you're not caught off guard:
- You install an app called OnVue on your machine. (I already uninstalled it. No shade, I just didn't need it hanging around.)
- Before the exam, you submit photos of your environment. Then a proctor called me to take additional photos. I waited around 10 minutes for the proctor to show up, so be patient.
- Follow the instructions to the letter. They are extremely strict. I had a random piece of paper sitting on my desk and they made me throw it away. Clear your whole desk before you check in.
- Be ready to join right at your "check-in" time so the proctor can get to you on schedule.
None of this is hard, it's just a lot of process, and it's easier when you know it's coming.
So… is it actually worth it?
Here's the honest part, because Daniel asked me this directly and I don't want to pretend.
Did I learn to take an exam, or did I learn something real? My honest answer is about 50/50. A chunk of the studying was pure exam-prep: memorizing flags I'd normally just look up, learning to spot which answer the test wants. That part I won't oversell.
But the other half genuinely changed how I work. I picked up new things while studying and actually changed parts of my workflow afterward. So it's not just a piece of paper. There's real signal in there if you go in wanting to learn and not just wanting to pass.
Would I tell you to get certified? If your company is paying, or you've got the time and 125 bucks to spare, yeah, go for it. You'll come out a bit sharper and you get a shiny badge for LinkedIn. Just go in with clear eyes about which parts are learning and which parts are hoop-jumping.
Good luck. If you're studying for this and you get stuck, my inbox is open.
See you in the next one!
19 Jul 2026 5:00am GMT
18 Jul 2026
Planet Python
PyPy: Moving linux builds to GLIBC==2.28
A short note for visibility.
PyPy builds tarballs of the python interpreter ready for download. These include the latest releases and also nightly builds, fresh from our fleet of buildbots. Over the next couple of days, the nightly builds on linux will transition from manylinux2014 based docker images to manylinux2_28 images. The practical implication is that nightly images, and the next releases, will require a minimum of GLIBC>=2.28, i.e. AlmaLinux8, amanzonlinux 2023, debian 10, ubuntu 20.04. For a good overview of how this glibc/distro/manylinux all works, see the PEP 600 compliance page.
The next release will indicate this change by a new PyPy major version, 8.0.0. It should include a Python3.12 interpreter, in which case it will be the last release of the Python 3.11 interpreter.
18 Jul 2026 6:09pm GMT
Django community aggregator: Community blog posts
Migrating from FeinCMS to feincms3
Migrating from FeinCMS to feincms3
FeinCMS is still actively maintained, but development, bugfixes and new features mostly land on feincms3 and django-content-editor these days, not on FeinCMS itself. That's reason enough to eventually move a project over.
Someone asked on the feincms3 issue tracker whether there's a guide for making that move. There isn't one yet, so I thought I'd expand on my comment in the issue tracker and post it here too in the hope that it's useful to others. The post is based on a gradual migration we did in a large, long-lived Django project - a textbook publishing platform with years of content. Unfortunately I can't show more details since it's a commercial, closed source project. During the migration, the platform stayed in production the whole time, aside from the inevitable bug here and there.
The most important insight is that FeinCMS 1 content types and feincms3 plugins are close enough that we could keep using the same underlying database tables. An export/import step isn't required at all.
The overall shape of the migration
- FeinCMS keeps managing the plugin tables as usual, for now.
- New feincms3 plugin models get added alongside the old ones, with
managed = Falsein theirMetaanddb_tablepointing at the exact table FeinCMS already created. Django doesn't think these are new or different tables, it just uses the ones that already exist. - The admin gets migrated from
ItemEditortodjango-content-editor'sContentEditor. If your project has more than one independent hierarchy (in our case, one per book series instead of a single global page tree) you can migrate them independently instead of having to do it all in one step. - Rendering moves over from FeinCMS's rendering machinery to feincms3's, again piece by piece: the HTML generation for the frontend, any full text search indexers, anything else that touches the content.
The examples below use Chapter and ChapterPlugin because that's the hierarchy in this particular project, one tree per book series rather than one global page tree. Most feincms3 projects center on a single Page model instead, so read Page wherever you see Chapter; the rest still applies.
Getting the migration state right
Here's a concrete proxy model from that migration, simplified. RichTextContent is our own abstract mixin, holding nothing but the actual text field, shared between the FeinCMS and feincms3 sides of the code base. The same role is played by feincms3.plugins.richtext.RichText if you use feincms3's bundled plugin instead of your own. ChapterPlugin in turn only wires up the boilerplate that's specific to being a plugin content type of our Chapter model:
class RichTextContent(models.Model):
text = RichTextField()
class Meta:
abstract = True
class RichText(ChapterPlugin, RichTextContent):
class Meta:
db_table = "textbooks_chapter_richtextcontent"
managed = False # Proxy for the real model still owned by FeinCMS
The exact table name depends on how FeinCMS auto-generated it for your project (a mix of the app label, the base model and the content type name), so look it up rather than guessing. ./manage.py sqlmigrate or just inspecting the database will tell you.
For the very last step, once a plugin has been converted and managed = False can go away, use Django's migrations.SeparateDatabaseAndState. It lets you change the migration state without touching the database at all. That's exactly what you need here since the table already exists and already has the right shape; only Django's idea of what the schema is needs to catch up.
You don't even have to write these migrations by hand. Remove managed = False, run ./manage.py makemigrations as usual, and Django writes out the normal operations it would generate for any new model. The only manual step is moving that generated list of operations into the state_operations argument of a single SeparateDatabaseAndState operation, redacted and shortened here:
class Migration(migrations.Migration):
dependencies = [
("textbooks", "0096_remove_richtextcontent_managed"),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name="RichText",
fields=[
("id", models.AutoField(primary_key=True, serialize=False)),
("text", models.TextField()),
("chapter", models.ForeignKey(to="textbooks.Chapter", ...)),
],
options={"db_table": "textbooks_chapter_richtextcontent"},
),
],
# No database_operations -- the table is already exactly like this.
),
]
As always, test this on a copy of the database first and make sure you have backups. Hand-editing migrations isn't generally recommended, but it's safe if you know exactly what you're doing. Treat it with the same care as any other schema change.
The page tree: mptt vs. tree-queries
FeinCMS 1's bundled Page model uses django-mptt for the page hierarchy, while feincms3's page comes with django-tree-queries instead. If you're using FeinCMS's own Page model and migrating to feincms3's, this can be a little bit painful since the database schema is different. There's a dedicated guide for migrating from django-mptt in the django-tree-queries documentation.
Once the last plugin has switched over, FeinCMS can be dropped from the project entirely, including from the requirements. The models end up exactly where they would have been if you'd started the project with feincms3 in the first place - and you never had to export or import a single row to get there.
18 Jul 2026 5:00pm GMT
Planet Python
Python Insider: Python 3.15.0 beta 4 is here!
The final 3.15 beta is out!
18 Jul 2026 12:00am GMT
Core Dispatch: Core Dispatch #8
Welcome back to Core Dispatch! This edition covers July 5 through July 18, 2026. Python 3.15.0 beta 4 landed today, July 18 (we just released it at the EuroPython sprints!), with 3.13.15, 3.14.7 and the first 3.15 release candidate following on August 4.
It's EuroPython week! Much of the core team has been gathered in one place for our annual Language Summit (blog posts to come!) and the conference. Recordings aren't up just yet, but as promised with the PyCon US talks, once they are, we'll pull talks and Language Summit coverage from the team into a future edition.
On the PEP front, discussion is lively: PEP 835 (shorthand syntax for Annotated metadata) and PEP 836 ("JIT Go Brrr") are both drawing dozens of new replies, and a few fresh PEPs - including PEP 840 on name resolution in class namespaces - have joined the queue.
Don't miss the "One More Thing" at the bottom of this edition. This one's a little sillier (correction: more unhinged) than usual, but we think you'll enjoy it.
As always, if you maintain a package or just like living on the edge, give the final 3.15 beta a spin and file any issues you find.
Upcoming Releases
- Python 3.15.0 beta 4 - Jul 18
- Python 3.13.15 - Aug 04
- Python 3.14.7 - Aug 04
PEP Updates
- PEP 797: Shared Object Proxies
- PEP 838: Adding
python-versiontopyvenv.cfg - PEP 840: Name Resolution in Class Namespaces
Steering Council Updates
Merged PRs
- Add
next_network()to theipaddressmodule - Normalize symlink targets in
tarfile.TarFile.gettarinfo() - Add
tkinter.systray- system tray icon and notifications - Add
tkinter.fontchooser- a font selection dialog - Reject CR and LF in
email.utils.formataddr() - Add a dataclass-like decorator for
ctypesstructures - Stop exposing the internal mapping when comparing
MappingProxyTypeobjects - Fix a data race compiling
string.Templatepatterns in free-threaded builds - Make
tempfile.TemporaryFileWrapperpublic
Discussion
- PEP 835: Shorthand Syntax for
AnnotatedType Metadata - 🔥 36 new replies · 4.6k views - PEP 836: JIT Go Brrr: The Path to a Supported JIT Compiler for CPython - 🔥 28 new replies · 3.9k views
- PEP 840: Name Resolution in Class Namespaces - 🆕 🔥 10 new replies · 496 views
- PEP 822: Dedented Multiline String (d-string) - 7 new replies · 7.6k views
- PEP 832: Virtual Environment Discovery - 6 new replies · 8.8k views
- PEP 827: Type Manipulation - 3 new replies · 8.4k views
- PEP 718: Subscriptable Functions - 3 new replies · 14.3k views
Core Dev Musings
- How to publish to PyPI using GitHub Actions securely - By Brett Cannon
- Security: line goes up - By Hugo van Kemenade
- Fixing the dictionary with Python 3.14 - By Hugo van Kemenade
- EuroPython 2026: Learning from the "not-so-secret" Python security cabal - By Seth Larson
Upcoming CFPs & Conferences
- EuroSciPy 2026 - Jul 18
- PyData PyCon Armenia 2026 - Jul 24
- PyOhio 2026 - Jul 25
- 📋 Plone Conference 2026 Deadline - Aug 01
- 📋 PyCon France 2026 Deadline - Aug 01
- 📋 PyCon Ireland 2026 Deadline - Aug 01
- 📋 PyBay 2026 Deadline - Aug 01
- 📋 PyData Global 2026 Workshop Deadline - Aug 04
One More Thing
"The Meowl is a part of life."
- Ken Jin
"We're just normal men"
- Łukasz Langa
Pablo during one of his "EuroPython 2026" talks, as hallucinated by AI.
Credits
18 Jul 2026 12: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