Evaluating Qt

Published at 13:18 on 14 May 2026

I had high hopes for PySide6, the dominant Python binding to the Qt framework. From what I had read, it was clearly the most popular way to do GUI programming in Python, well-supported by an active dev team, and a little checking up revealed this impression was accurate.

All went well until it came time to do some I/O-and-compute-intensive background work. For some reason, the Qt dev team didn’t stop at GUI development; they went on to develop a whole universe that encompasses more than just graphical user interfaces. As an example, instead of using standard Python thread pools, all documentation on worker threads in Qt focuses on using QThreadPool. I was hoping to find some explanation on why things are done this way, and why doing it the standard Python way was or was not a good idea, but I had no luck. Not willing to run risks, QThreadPool it was.

Thankfully, it wasn’t bad. It’s just a typical thread pool that one submits jobs too. The problem happened when my jobs were done and wanted to return some results for the UI thread to display.

In normal UI frameworks, that is simplicity itself. There is a call to submit code for execution in the front-end event loop, you submit a closure containing the result and how to process it, exit the background job, and you are done.

Qt is not normal. It has these quirky relics of the early/mid 90’s attempts to manage concurrency called signals and slots and it expects you to use them everywhere. That includes worker jobs that need to notify the UI event loop of something.

It turns out that signals and slots are seriously broken by design. They don’t work unless both sender and receiver objects remain active until the signal is received by the slot. Any departure from this requirement and things will randomly fail. Sometimes they fail visibly with an exception (often an oddball one out of left field that doesn’t directly signify the cause of the issue). Sometimes signals will just get silently dropped on the floor.

So, no problem, just keep the sender active. But I’m sending the signal to indicate that I am done! Why should I remain active? My work is done, it is time to exit. It is an onerous requirement, one that I don’t have to bother with in any other framework of which I am aware. In wxPython, Swing, and Avalonia, I just submit the code to do the front-end processing to the event loop and exit. In Qt, I have to wait around for the front end to message me back that it has received my results and I may now exit.

And signals themselves are bizarre and un-Pythonic. You create them by defining class variables of type Signal, but you never use them as such. Instead you create instances of that class and Qt does some backhanded, behind-the-scenes hackery to transmute those class variables into instance variables of type SignalInstance. Just one more sharp edge for the programmer to cut themselves on.

It gets worse. It turns out that this is but a particular instance of Qt in Python being for the most part a thin veneer over a library written in C++. There is little or no communication between how C++ manages memory and how Python manages it. Create a control, add it to a parent control, but don’t store it in a global variable or as an instance variable in a long-lived Python object, and Python might garbage-collect it even though Qt still has a C++ pointer to the same memory region. Qt programmers have to follow all sorts of arbitrary rules to avoid memory management issues.

Use Qt and you will be painfully aware of memory management all the time. This cuts to the prime reason I want to use a language like Python, which features automatic memory management, in the first place! Forcing this sort of experience onto the programmer is positively asinine.

This is not a minor nitpick, as it gets in the way of how I like to program. I like to be dynamic and reactive. Just create UI elements on the fly and display them. The logic that manages and displays them will (or, rather, should) hold references to them, keeping them from being garbage collected, until they get closed or removed. Then they vanish, in memory as well as on screen. The number of elements naturally expands and contracts according to the needs of the user. The only constraint is the amount of hardware memory, and possibly quotas on same imposed by the system. Larger systems with more memory can naturally be taken advantage of, as needed. Simple, logical, effective. And needlessly painful to accomplish in Qt.

And this seems to be an only-in-Qt thing. In wxPython, for example, the dev team thought about this, and explicitly coded their glue to the C++ world to avoid these issues. (It’s not very hard to do.)

Qt seems to be the answer to the question: “I want the worst of both worlds. Is there any way I can program in a language as slow as Python and still have the memory-management headaches of C++ to deal with?”

To hell with Qt.

Evaluating Avalonia

Published at 09:49 on 13 May 2026

The suckiness that is the Java build environment finally got to me. That corrupt JAR file (the second time this has happened to me) was the final straw. I should not have to debug common build tools and the files they make. They should just work. The only bugs I should routinely battle with are the ones I just wrote in my own code. That is quite enough work, thank you very much.

So, despite Java in many ways being the best way to write a portable graphical application for the desktop, those JCA’s just are too much. The final straw had broken the camel’s back.

I had sort of been biased against Avalonia since learning that it focuses on pixel-for-pixel consistency between platforms (something I believe is a big mistake for desktop application programming). Then again, ASP.NET Core is so much better than any Python alternative web framework and Avalonia uses a similar code-behind model. Maybe the pixel-for-pixel compatibility doesn’t result in something that bad after all?

Executive summary: The rendering is better than I thought, but it doesn’t matter. There are enough other reasons not to use Avalonia.

File dialogs. In Swing (Java), Qt, and wxWidgets, one can customize these by adding extra controls. In Avalonia, you get the standard file dialog and that is it, no provision for customization. Want to ask for more parameters? Pop up a second dialog. The problem is, that is often a losing user experience; many load and save operations just naturally lend themselves to a need for a little extra information. Better to handle it all in one step, unless one is asking for a lot of extra information (and one usually is not).

File filters in dialogs. In Java and wxWidgets, you filter files by extension. In Qt, you filter by extension or MIME type. In Avalonia, you filter by extension on Windows and Linux. Linux also offers the option of filtering by MIME type. On the Mac, neither of the above work; you must filter on this wonky thing called an “Apple uniform identifier type.” Hello? The whole point of a cross-platform framework is to smooth this sort of stuff out! I shouldn’t have to deal with any weird Apple-specific stuff like this at all; it should just be MIME types and/or extensions everywhere. So stupid and annoying.

Data types in the clipboard. On most frameworks (Java Swing and AWT, Qt, wxWidgets) these are normalized from system-to-system. On Avalonia, the story is similar to file frameworks: one way for Linux and Windows, another way (those “Apple uniform identifier types” again) on Macs.

XAML. This is the declarative, layout-describing language that one codes behind. Unfortunately, unlike Razor templates in ASP.NET (which are powerful and have access to basically all C# control structures), XAML is pretty weak-sauce. It’s a garden-variety XML schema, with little or no provision for imperative coding. This is a pretty big problem, as cross-platform GUI programming often necessitates imperative coding. Consider pull-down menus; the Mac organizes them quite differently than do Windows or Linux. So a user-friendly app will include conditional logic and adapt itself accordingly. This is not exactly easy in XAML. You have to use some brand-new (and poorly-documented) features, some of which have some truly obnoxious misfeatures. The end result ends up being a lot more verbose and repetitive than it needs to be.

MVVM. Avalonia rams the model-view-view-model paradigm down your throat. It’s possible to resist this, but you have to work at it. MVVM was an effort to manage complexity in very large and complex programs. Not all programs are very large and complex (and fewer programs should be), so not all programs need MVVM. Even the inventor of MVVM openly admits this. Worse, the flavour of MVVM that Avalonia pushes is cartoonishly rigid, formulaic, and doctrinaire. Every view gets its own separate view model, even though it often makes more sense to share a view model amongst cooperating views. Even worse yet, by pushing something that makes sense for the largest programs, one pushes the mindset that computer programs should by default be large and complex. This mindset is positively harmful, as it leads to feature bloat.

The documentation. XAML is all standard controls classes under the hood, so one doesn’t have to use it. One can just use those controls classes directly, in C#. Then one gets the full suite of comprehensive imperative programming features C# has. Unfortunately, the documentation on how to do this is extremely incomplete. It’s mostly focused on how to do things in XAML. What documentation there is tends to be something of a mess. Complex classes, with many dozens of properties and methods, and nothing in alphabetical order. Why? Just why? There is simply no excuse for this lack of organization.

The choice of what programming language to use is often dictated by frameworks and libraries, and not the core features of the language itself. So it is here. Everything other than Avalonia in C# is generally worse than Avalonia. So C# simply doesn’t make sense for this project.

It is the exact converse of the situation with back-end Web programming. This time, it is Python that has clearly the best framework.

Why? Culture. Group identification. Avalonia was written by people that came out of the .NET world and wanted to make .NET applications more portable. It was strongly patterned after Windows frameworks like WPF. User-friendly GUI programs have never precisely been Microsoft’s strongest suit; Windows is rightly regarded as inferior to the Mac when it comes to user friendliness. It just turns out that that externally-visible awkwardness is there in no small part due to internal awkwardness bubbling up to the surface. And because the Windows universe is really big, most of those in it are astoundingly ignorant of what other universes have done.

When it came to Web servers, Microsoft had been left behind. They had to do a good job on ASP.NET, else nobody would switch over from the various open source frameworks that had already dominated the landscape. When it comes to the desktop, the exact opposite was the case. Complacency tends to produce inferior outcomes.

That said, I still might use Avalonia someday. If, that is, I ever have occasion to develop another smartphone app. I would want it to be portable, and the main alternatives to Avalonia for such things are Electron and Qt. The former is Javascript-based, and the way Javascript so badly botched modules and imports is reason enough to eschew the language. Qt is, well, Qt, and I will get into the specifics of that particular flavour of awfulness soon.

Portable GUI Frameworks

Published at 22:01 on 11 May 2026

I’ve been experimenting with them, because I have a need to rewrite a tool I use from Kotlin into some other programming language. Not because of any real defects in Kotlin (despite its proximity to the Java world and its associated JCA’s, this is not a show-stopper).

No, the problem is that I need to read HEIC files, and there is no good, easy way to do this in Kotlin, because there are simply no good, open-source, comprehensive, general-purpose imaging processing libraries available for the JVM. There are limited libraries that do not support HEIC nor the large size of the HEIC images I wish to read (such as the built-in stuff in the standard Java class library). There are esoteric libraries like ImageJ that support all sorts of oddball image formats used in the microscopy and health care fields, but not HEIC. There is no shortage of libraries that are abandonware and haven’t had an update in a decade or more.

By contrast, C# has Magick.NET and Python has Pillow, both of which do what I want. So I have been looking into GUI frameworks for those languages. Lets just say there are a lot of bad ones and leave it at that for now. I will post some details on the badness later (some of it is pretty mind-blowingly bad).

Suffice it to say that at this point I am reasonably sure wxPython will suit my needs, and it took a disgustingly long period of trial-and-error to get to that conclusion.

Bypassing Cloudflare to Scrape a Web Site

Published at 21:49 on 15 April 2026

It’s supposed to be really hard, and Cloudflare does indeed to a very good of detecting (and banning) web scrapers. But, it turns out that this is one of those things that, while indeed very difficult to do in the general case, is actually quite simple in a lot of specific cases.

The main trick is to use Playwright (with the playwright_stealth addon) to control a browser, and to use that browser to scrape the web. You could theoretically use some other browser automating tool like Selenium to do this, but the problem with Selenium is that it basically advertises itself with every request (and there is no way to turn this off), and that triggers Cloudflare in short order.

The second trick is to mimic a human user as faithfully as possible. Generally, that means going slow, i.e. no faster than a human would navigate a web page, and inserting randomness into the process so it looks like a human and not an automaton is controlling the browser. This is where things break down for abusive scrapers like AI companies; they can’t take it slow, because if they do, it will literally take millennia to get as much data as they want. But I am not an AI company, and only need to scrape a modest amount of data, so taking it slow is good enough (I just let it run, and the results get collected eventually, not as fast as they might otherwise have been, but it still beats manual cutting and pasting).

The reason I mention this is that, if you try searching on how to do this, the results tend to be pretty useless. They are dominated by either a) techniques that no longer work and which will trigger Cloudflare almost instantly, or b) commercial scraping services interested in taking your money. The latter may actually be a useful service if you want to scrape a lot, but I am not in that category.

That all of this is even necessary to write about is yet another data point in the overall enshittification of the Internet, which by my reckoning peaked in its usefulness circa 2010 and has been heading downhill ever since. The services I now have to use devious techniques to scrape used to be easily scrapable; actually, in many cases, they had free API’s designed to work well with another computer and didn’t need to be scraped at all.

And yes, the abusive scrapers are as much (or more) to blame here as are the sites removing functionality. I have had to implement anti-scraping measures on a site I host, because AI companies were ignoring robots.txt and scraping the daylights out of it. It was so bad that I was getting server crashes from the abuse.

Another Javascript Problem: Libraries

Published at 08:26 on 5 April 2026

One of the nice things about C# and Python is that both come with a robust, comprehensive standard library. Java, too: although its standard library tends to be awkward and clunky to use, it is still comprehensive and well documented.

Javascript? Not so much. As part of its heritage of originally being a relatively small, client-side language for embedded scripting, its built-in library is not nearly so comprehensive. One must instead go to npm for many things that are supported out of the box in many other programming languages. Thankfully, as a result of Javascript’s popularity, npm is very comprehensive.

Unfortunately, the quality of the libraries published there (and the quality of their documentation) is, let me just say, erratic. Plus, there’s just so many of them, and it is not always obvious which libraries are better-supported and higher-quality, often resulting a fair amount of trial and error before one finds a suitable solution. Then there is the module and import hell I have written of before; sometimes a given module is not available in the module and import configuration you are currently using, causing you to have to deal with all sorts of uniquely Javascript headaches that no decent programming language has.

In short, if you write an application in Javascript, you’re not going to be able to find reasonable, well-documented default library solutions as often as you would were you using a better-designed, better-planned programming language. Finding and using the right libraries is going to be a source of recurring headaches.

What I Hate about Java

Published at 12:32 on 12 February 2026

Consider a common programming task: open a text file for reading with buffering. Let’s go through some of the programming languages I have used, in rough order of my learning them. (Disclaimer: my memory is a little rusty on some of these; they may not all be 100% correct. But they are not that far off the mark.)

First, the non-Java languages.

BASIC-PLUS:
OPEN "FILE.TXT" FOR INPUT AS FILE #1%

FORTRAN:
OPEN(UNIT=1,FILE='FILE.TXT',STATUS='OLD')

Pascal:
assign(file1, 'file.txt');

C:
FILE *file1 = fopen("file.txt", "r");

Perl:
open(FILE1, '<file.txt');

Python:
file1 = open("file.txt", "r")

C#:
var file1 = new StreamReader("file.txt");

And then we have Java:
var file1 = new BufferedReader(new FileReader("file.txt"));

LOL, what? Why should those internals be exposed? Why should I have to explicitly wrap an unbuffered reader in a buffering one? Why the extra step to do something so common and routine? Why did I just have to spend a half hour studying the documentation, chasing from class to class to class, to figure out how to do something that was almost self-evident in every other language I was learning?

Why can’t Java do out-of-the-box today in one simple step what FORTRAN could do in 1966?

And don’t say “object orientation.” Python and C# are object-oriented, and don’t have this programmer-hostile silliness.

Sure, this seems to be a little thing, and it is just one thing. But it’s not really just one thing: this sort of crap is all over the map in the Java world. Everything is clunkier and more awkward than it should be, everywhere. It’s relentless. It’s wearing.

Re-Discovering the Advantages of .NET

Published at 09:06 on 28 January 2026

Fifteen or so years ago, as an exercise in curiosity, prompted by how often I saw the technology mentioned in job listings, I decided to check out Microsoft’s .NET framework. I was expecting to come away feeling smug about how much better competing technologies more popular in the Linux world were.

Surprise No. 1: I didn’t have to just read about it. .NET is an implementation of an open standard called the Common Language Runtime (CLR), and there was what turned out to be a very nice open source implementation of the CLR called Mono. Which I proceeded to install on my Mac and play with.

Surprise No. 2: It (both the C# programming language and the .NET framework) was well designed! This one floored me, given how sucky I generally find things that Microsoft has been heavily involved in. C#’s designers obviously learned from Java’s mistakes, particularly when it came to designing a standard library. And, frankly, they had to do a good job. Unlike its operating systems and desktop environments, which have long been market leaders, and could get away with coasting on their well-established momentum, Java was the clear market leader in virtual machines that ran byte-compiled code. If Microsoft didn’t do a good job, people would just stick with Java, which runs just fine on Windows.

I ended up writing a bunch of command-line utilities in C# and a web site using ASP.NET. It even led to a job where my history as an individual who knew both .NET and Linux servers was the special sauce that got me hired.

But that job didn’t last forever, and there was still a lot of anti-Microsoft tradition that caused most of the open source world to dismiss .NET and Mono out of hand. I could tell I was probably not going to luck out like that again, so I shelved .NET in favour of technologies more common in the open source universe.

Fast forward 15 years and Microsoft has now open-sourced .NET and merged its codebase with that of Mono, meaning the two formerly separate projects are now effectively one.

I have been struggling in the past few days with how to integrate authentication into a web app I am writing. Rolling your own is generally frowned upon (it’s surprisingly complicated; you have to deal with sign-ups, account deletions, forgotten password resets, perhaps two-factor authentication, etc.) But the off-the-shelf solutions available for Python or Node.js just plain suck.

Mainly, they don’t have the flexibility I need. You see, I need access to the actual password used to log in, because I am using it to derive an encryption (and decryption) key used to protect sensitive per-user data in my database. One of my web app’s selling points will be that even I won’t be able to know your secret data. Most authentication services and libraries simply don’t support this: you never see the user’s password, because you don’t prompt for it yourself.

So I check out what sort of authentication systems the .NET world has to offer, and immediately find one that doesn’t suck: one of its key design principles is in fact to let their clients do the prompting for authentication credentials, because, guess what? They just might want access to them, themselves. Cluefulness, what a concept.

Then I find out that I don’t need that product at all, because ASP.NET comes with a surprisingly capable identity management system built in. Which, while it doesn’t let you do your own prompting for credentials by default, does offer it as an option.

Database access is better, too. Most open source object-relational managers (ORM’s) are flat-out terrible. They force you to code all sorts of repetitive boilerplate to mirror what’s already in your database schema*. Instead of simple, logical, expressive SQL, you have to use awkward and clunky chains of method invocations. It’s bad enough that I’ve written my own ORM for Python. It wasn’t that hard, and it’s a whole lot nicer to use.

* How utterly asinine this is becomes clear when one realizes that one of the key characteristics of a relational database is the ability to use queries to programmatically deduce the schema of an existing database. Most ORM’s are, in other words, forcing the programmer to do manually what they could do automatically themselves.

Well, the two most popular ORM’s in the .NET world, Dapper and Entity Framework, are both best of breed. They don’t suck. Entity Framework even has, with C#, query expressions as first-class language constructs.

Then we have file-based routing, where you create a new file and get a new route automatically, something that Apache did 30 years ago (and still does today) but many modern open-source frameworks (particularly in the Python universe) still can’t do. Another win.

Documentation is another big win. .NET has some of the best in the business. Nearly everything is covered by both tutorials and comprehensive API documentation, the latter of which is liberally supplied with examples. It’s not just documentation, either; there is all sorts of help for the programmer in the form of what the .NET world calls “scaffolding,” in which example code can be created for you on request. It’s almost always easier to do something by modifying existing code that comes close to what you want, rather than to start from a completely blank slate.

It’s just generally a better developer experience all around. Normally, you pay for convenience like this, typically in the form of poorer performance. Not this time: ASP.NET sits at the very top of web framework performance benchmarks.

It’s not all roses. .NET is arguably overengineered (just look at function parameters: you have normal parameters, out parameters, keyword parameters, ref parameters, and readonly ref parameters). And there’s at least four different ways to template and generate web pages in ASP.NET.

But while the overengineering is tiring at times, there’s still nothing as bad as the hideous shambolic mess that is the Javascript module and import system. And, arguably, it does make for a lot of choices, choices that I will be taking advantage of to develop exactly the sort of web application that I want.

Evaluating Node.js as a Client-Side Technology

Published at 14:49 on 24 January 2026

Executive Summary

  1. I can see it perhaps making sense in some corporate situations,
  2. I am not personally in such a situation, therefore, it does not make much sense for me, and
  3. The Javascript module system is (or should I say systems are) absolute garbage.

How I Got Here

Prompted by data like this, I decided Node.js and frameworks based upon it were worth a closer look, since I am starting a web-based software project for a nonprofit I am associated with.

First, I want to choose something that someone else can easily step in and maintain, and that means choosing something in common use. Second, at least some of what is in common use is likely so for a reason.

A word or two is first necessary on that chart. It’s what pops up highest when I ask Duck Duck Go about the most popular web frameworks. I did once find out the source; it was a poll taken at a major conference of web developers in 2024. So it is actually based on actual real data, which is more than one can say about most lists of top web frameworks. But Node.js is not a web framework; it is an implementation of a programming language, Javascript. Why this is so becomes clear if you scroll down the right sidebar and look at the question asked, which was about “web frameworks and web technologies [emphasis added],” and not strictly frameworks.

But I digress. There are frameworks based on that language, however, which come up repeatedly (and are the top rankers) in that chart.

I’ve dabbled in client-side Javascript over the years, simply because one cannot avoid doing so. It is just not possible to do all that needs to be done purely in declarative HTML. But the key term here is “dabbled.” I have deliberately avoided the sort of Javascript overuse that harms the user experience for so many on the Web, but it is still relatively easy to run into situations for which the easiest and simplest solution is a little bit of client-side scripting.

So I install the latest version of Node.js, and because I have personal experience with how Javascript’s lack of static typing makes it easy to write and difficult to find coding errors, I install Typescript as well.

Module and Import Hell

Right off the bat, I run into obstacles of the sort I seldom do with a new programming language. Typescript blows up, big time, spewing error after error for a small snippet of code that has absolutely nothing wrong with it so far as I can see. After some time, and some sleuthing on the Web, I pinpoint the cause as having my import resolution options incorrectly configured.

Which right off the bat shows that said subsystem is hot garbage in the Javascript world. Just for openers, there’s at least three kinds of modules. Most all programming languages get by fine with just one, but not Javascript. Modules and imports are super-simple in most programming languages. There should be no need to configure things. Imports should just work out of the box, like they have for the vast majority of the world’s programming languages at least for the past fifty years or so.

If you have to write a lengthy web page to explain how your programming language’s imports and modules work, long enough to require a special summary section, and that summary itself takes up three screenfuls, you have a problem.

I finally figured it out and got my test script to compile, but I basically blew a day doing so, and it’s still largely a crap shoot for me how to properly import a given module, because the rules are so complex and I have yet to fully internalize them.

To reiterate: this aspect of Javascript is hot garbage. No other honest assessment is possible. Javascript does modules and imports worse than any other programming language I know of.

Random “WTF Javascript” Stuff

Javascript has more than its share of strange quirks. Most of these are related to it being a weakly-typed language. Probably the worst thing is that Javascript has two sets of equality and inequality comparison operators, which differ in the details of the type coercion they apply to their operands (most languages get along just fine with one). More than one web site is dedicated to pointing this all out.

While this does make for a quirky and sometimes annoying experience, it doesn’t rise to the level of malicious complexity, bordering on total unpredictability, that the module and import system does. Moreover, a lot of it simply comes with the territory when one has a dynamically-typed language. For most of the “WTF Javascript” examples I have seen, if I feed the analogous expression to Python (which is famed for being a “clean” and logical language), I get a similar result. So I could have coped with this if it was all there was.

As such, I almost didn’t include this aspect of the programming language in this article. But I figured I’d mention it here because if I didn’t, people would keep commenting and telling me about it.

Generating HTML with JSX

Why did I persevere? In part because of JSX (and its Typescript analogue TSX). It’s really quite the clever innovation for generating HTML. Not quite so clever as it first appears, but it’s still pretty nice. It’s definitely the easiest and most logical way (and one of the most powerful) ways of letting the user generate custom markup tags that I have run across.

Eventually, however, the conclusion was inescapable: there is just too much mismatch between Javascript and what I want to do for a 100% Javascript framework to make much sense for me.

Event-Based Programming

Javascript gives the programmer a single thread and an event-based programming model with a dispatch loop. It’s evidently very efficient; benchmarks give Node.js a decided edge over alternatives such as Python.

I think, however, that in most cases this is a “so what?” moment. Benchmarks give C and C++ a decided performance edge over languages that do automatic memory management. Yet most programs are no longer written in C/C++, because the mental overhead of dealing with manual memory management is a drag on programmer productivity, and the overhead of finding and dealing with memory mismanagement bugs is an even bigger drag yet.

My contention is forcing the programmer to manually manage threading and context switching is approximately as much a drag on programmer productivity as is forcing him or her to manually manage memory allocation. And just like the latter has the automated solution of garbage collection to relieve the programmer of cognitive load, the former has the solution of preemptive multithreading.

Yes, it’s not as fast. Again, so what? Processors are faster than they have ever been. Raw speed is not nearly so important as it once was.

And how much faster is it? The figure thrown around on the Net is that Javascript comes out 40–70% faster than Python for back-end web software. Frankly, that is a very modest improvement, considering that Python is one of the slowest languages out there. What happens when you compare Node.js platforms to those coded in compiled languages? Let’s just stay it starts looking a lot less impressive for Node.js.

It also must be mentioned that while Python’s ability to multithread has historically sucked, it is about to get a whole lot better.

Culture, or Tradition Being Mightier than Innovation

One of the most overlooked aspects of any framework is the culture and traditions that have evolved around it.

Java really isn’t that bad a language, when you consider its core features. Sure, it seems dated now, but that design represented state-of-the-art consensus in the 1990s. That Java is still around after all those years shows its design got at least some things right. The problem with Java is all the dysfunctional culture and traditions that are present in that language’s community.

Well, part of the traditions of Javascript is where it started: in the browser, as a relatively simple scripting language that was thrown together in a hurry. That use case grew, eventually growing to encompass server-side code, and the language acquired more features as a result. There was never a great deal of careful planning in the process; features were just added in a mostly ad hoc manner, as needed. Sometimes this resulted in features that didn’t grow well (this is the source of much of the module and imports mess).

Because of this history, there is this hidden implicit assumption in the Javascript world that web pages will have a lot of Javascript in them. Javascript frameworks, even server-side ones, tend to make this the path of least resistance for doing anything. There is, to put it mildly, insufficient appreciation of the pitfalls of doing this in the Javascript community, both client-side and server-side.

Javascript frameworks make it easy to write web pages with lots of Javascript in them, pages that perform poorly on mobile devices and when networks are slow. They make it difficult to write anything else.

The Real Argument for Javascript

It’s that there is currently, and has historically been, no alternative for client-side scripting. Web Assembly is in the process of ending that, but it has not yet done so. Plus sheer momentum means that Javascript on the client side won’t be going away any time soon.

So there are a lot of workplaces that have teams coding in Javascript for the front end. Probably too many of them, given the current overuse of client-side scripting, but I digress. Javascript is already in use, and wouldn’t it be nice to be able to move people between the front-end and back-end teams as demand for labour requires? Or maybe get rid of the front-end and back-end distinction and just hire for full-stack positions? If all the code was in the same programming language, that would be a whole lot easier.

And, fortunately enough, it just so happens that there are server-side implementations of Javascript, and while they can’t match the performance of compiled languages, they actually perform very respectably for an interpreter. Major win!

And that explains the current popularity of server-side Javascript.

But That Is Not My Situation

I am developing a service that I want to work as well as possible on mobile devices with subpar Internet connections. That means, wherever possible, using plain HTML, eschewing the Ajax pattern (which I consider mostly an antipattern). I don’t need deep Javascript knowledge to do that.

I am not trying to bully people into installing a custom app (which then proceeds to make me money for me by spying on the user) by having my web site be slow and unusable on mobile devices, because I am not a slimy capitalist. I don’t personally have the time to write both and app and a website, so just writing a mobile-friendly website serves my best interest as much it does the user’s. Win/win!

I already know Python well, and Python is squarely within a virtuous cycle of programmers with healthy design patterns attracting other programmers with similarly healthy patterns. The Python standard library is more comprehensive than the one for Node.js. The overall quality of open-source, third-party Python libraries in my experience exceeds that in the Node.js world. Python’s modules and import logic are sane, not a shambolic mess. Python allows event-based programming but doesn’t force it on me.

While Python isn’t the No. 1 back-end web language, it is No. 2 or 3, and over all, for all uses (not just the web), ranks No. 1. As such, there are plenty of Python web frameworks out there. While they are not as performant as Node.js frameworks, they aren’t that far behind, and should do a fair bit of catching up soon.

And so far as recoding if I want better performance, I have written web sites using C# and ASP.NET (currently sitting at the very top of the benchmarks, and no, you don’t need Windows to run it), and I found it an easier technology to adapt to than Javascript (just for openers, there is but a single module and import system, and it behaves predictably). Even old-school Java Server Pages, when built with a simple and predictable build tool like Ant, was less painful. Why should I choose an option that has more pain and less performance?

Node.js just doesn’t seem to make much sense for me.

The Problems with React

Published at 10:30 on 15 January 2026

If you look at most lists of the most popular web frameworks,
React ranks at the top. (Node.js is not a framework; it is an implementation of the JavaScript programming language. It turns out that the question which prompted that survey response was about “web frameworks and web technologies,” not strictly frameworks, which explains some of the answers listed.)

It explains why so many web sites suck, particularly when used on smartphones and in other situations with slow network connections. The entire premise of React seems to be that everyone has a speedy connection to the Internet.

React is sold as a framework that does server-side rendering. This is highly misleading. Yes, it does do some rendering on the server, but then it repeats the entire exercise on the client. It is part of how its process of “hydration” works, by creating a second entire document object model called the VDOM (virtual DOM), then reconciling the differences between the VDOM and the DOM. It does server-side rendering merely to help conceal what a slow, booger-eating fat pig it is under the hood.

And it can download a lot of code to do this. I created, as a test, a simple “hello, world” page using React and Next.js. When I say “hello, world” I mean it literally; that two word sentence was its entire content. It was about half a megabyte in size. I am not making this up: half a megabyte. For two words.

Now, Next.js has something of a reputation for page bloat, but still. It is also sold as doing server-side rendering, which misled me into thinking maybe it could deliver reasonably-sized content.

The root of the problem is a React design goal, that of so-called isomorphic code, wherein the exact same code runs on both client and server. This inevitably leads to an unnecessarily excessive amount of JavaScript being sent to the client. Forcing the programmer to be aware of the distinction between client and server is actually a good thing, as it leads to more performant code.

There are far better ways to use JavaScript. I just wrote a small web application using Express JS and the rendering engine from an open-source static site generator (because the latter allowed me to use JSX).

It is not a minimalist “hello, world” page; it has interactive content. It does hydration, too, by hand. Since I hydrate the page by hand, I have complete control over how much client-side rendering I do, and how much of a JavaScript payload burden the page has.

Total size of all assets sent to the browser: under 10 kbytes. Yes, it’s that simple. No, it is not isomorphic. So what? Again, the entire size of the page, both procedural JavaScript and declarative HTML and CSS, is under 10 k. Hardly a great burden of additional complexity. So much of the bloat you see on modern web sites is the result of pure laziness and dubious design decisions. It doesn’t have to be this way.

React, I think, is rotten down to its very roots. It was developed by Meta, the Facebook people. They don’t care how much their site sucks on mobile devices. In fact, they actively want it to suck, because they would prefer people install their app, which spies on their users. React is a framework that reflects the moral bankruptcy of the organization who sponsored its creation.

Re-Evaluating Kotlin

Published at 15:40 on 15 December 2025

Back in 2019, I wrote:

The pity is that once one does things other than Android software development in Kotlin, the rough edges in its ecosystem quickly become all too apparent. Just out of curiosity I’ve been playing with the Ktor server-side framework. The documentation ranges from flat-out obsolete (and thus incorrect) to simply nonexistent. The result is that even simple things take hours of tedious experimentation to determine how to do.

I’m hoping that Android development goes better, but unless those rough edges get smoothed out, and soon, Kotlin may well end up being stereotyped as an Android-only thing.

Only last month, I wrote:

The big problem with Java is not the language, it is the culture around the language. I have a friend with a number of pet sayings, one of which is “tradition is mightier than innovation,” and that certainly applies in spades to Java. There’s just so much bad tradition enshrined as respected convention in the Java world. I call the result Java Community Antipatterns or JCA’s for short.

      ⋮

If it weren’t for the JCA’s, Kotlin would be a near-ideal programming language. If it weren’t for the heat and dryness, Phoenix would have a near-ideal climate. (And aside from that, Mrs. Lincoln, how did you enjoy the play?)

But, I ended up concluding:

The Java community’s faults may be legion, but Java set out to be a “write once, run anywhere” language, and its virtual machine has to this date succeeded at that goal better than any other such environment of which I am aware.

So I’m back to coding in Kotlin, and once again Kotlin’s nemesis becomes clear. It is its proximity to the Java universe, and by implication the universe of JCA’s.

The Java virtual machine’s advantages make the concept of a language like Kotlin tempting. It is why JetBrains decided to create Kotlin. It is why I have been coding in Kotlin.

Unfortunately, realizing that concept is a very tall order. Quite simply, it is not exactly easy to take a complicated, badly-designed, antiquated ecosystem and attempt to layer a more rational, more modern, more well-designed one on top of it. This problem becomes all the more acute if said ecosystem is associated with a culture that has enshrined harmful antipatterns as part of its respected traditions. The Kotlin development team is obviously trying as hard as they can, but it doesn’t matter: their effort still falls short.

I am picking up a project I put aside about five months ago, due to a need then to focus on other more pressing goals. Then, I had foundered for some time on serialization. Kotlin has what is in theory a great serialization subsystem. Of course, given the Java world it was layered on, it took no small effort to implement it. And the latest version of Kotlin at the time hadn’t quite got its implementation right; I was getting bitten by those bugs, which were causing my code to throw exceptions and die. JetBrains was aware of those issues, and planning to fix them in the future, but that didn’t help me in the here and now. Eventually, after blowing several days on the issue, I found the magic combination of an older Kotlin compiler and serialization library that did not make these bugs manifest.

That resolved my issue, but enough time had transpired that when I recently resumed my efforts, one of the things at the top of my agenda was to upgrade to the current versions of things and see if JetBrains had fixed the bugs. I did, and they had. So far, so good.

The problem is, the build system (based on the Java universe’s Gradle, which coming from the Java world is the standard shambolic mess you find in that world) has now for some reason started producing a corrupt Jar file. The jar command-line utility can list and process the Jar file. It can locate the class I am trying to invoke. Yet when I attempt to invoke it, the JVM claims it cannot find the class.

So now I have to troubleshoot that build system and figure out why it is (once again) failing me. And this is the sort of shit that keeps coming up in the Kotlin world, simply due to its proximity to the Java one.

Where this goes, I am not sure. Maybe I will find a resolution or a workaround, like I did the last time the build system started spitting out Jar files that were corrupted in a slightly different way. Maybe not. Maybe I will just give up on Kotlin, despite its advantages, because the disadvantages simply outweigh them.