One: a database row for every occurrence

This is the big one, and the list above makes it concrete. Set the limit to a hundred and give it your Tuesday class. Every date you see is a full WordPress post in a plugin that materialises occurrences — with its own row in the posts table, its own set of meta rows, its own permalink, its own entry in your sitemap, and its own presence in every query any other plugin runs.

Two years of one weekly class is around 104 posts. Three classes is three hundred. That weight does not sit in the plugin's file size; it sits in your database forever, and it grows.

The alternative is to store the rule and expand it when a page is drawn. The expansion above took a fraction of a millisecond. There is no scheduled job to run, nothing to reconcile when you edit the series, and nothing to clean up when you delete it.

Two: work that happens on every page of the site

Several popular calendar plugins hook their own routines into WordPress globally — running on the front page, on blog posts, on your contact form, on pages that have no calendar anywhere near them. Sometimes it is a query parser, sometimes a rewrite handler, sometimes a settings object being rebuilt from the database.

You can see this for yourself. Install Query Monitor, open a page with no calendar on it, and look at the query count and the autoloaded option size before and after activating a calendar plugin. A calendar has no business costing anything on a page it does not appear on.

Three: autoloaded options and expiry-less transients

WordPress loads every option marked autoload on every single request. A plugin that stores a cache entry with no expiry ends up putting it in the options table with autoload switched on — and then every page load on your site, forever, reads a cache for a page most visitors never see. This is a documented, live problem in the largest plugin in this category.

The fix is unglamorous: never store a cache without an expiry, keep the autoloaded set to a couple of small values, and invalidate the cache by bumping a counter rather than by deleting entries. This plugin autoloads exactly two options — a small settings array and one integer.