#Field Notes

Replacing a Transient With Post Meta

One cached value per post, already loaded with the post, and one less query.
author
Ilya Kessler
category
"Field Notes"
reading
"3 min"
replacing-a-transient-with-post-meta.md

The number looked great on the dashboard, which should have been the first warning. It turned out we were averaging across every request, including the health checks, which are very fast and very frequent. Once they were excluded the graph told a much less flattering and far more useful story.

Write the failing case down before you change anything. It can be a test, a curl command or a sentence in the ticket. The point is that you have something outside your own head that says what wrong looks like, because your memory of the bug starts drifting the moment you begin working on it.

Naming turned out to matter more than the structure did. Once the function stopped being called process and started saying what it did, two of the three bugs in the surrounding code became obvious to everybody reading it, including the person who had written them, who was me.

Things that did not help

<?php
namespace App\Cache;

final class Related {
	public function ids( int $post_id ): array {
		$cached = get_post_meta( $post_id, '_related', true );

		return is_array( $cached ) ? $cached : [];
	}
}

Test it with the keyboard before you call it finished. Tab through the page, press Enter on things and see where focus lands afterwards. It takes less than a minute, it needs no tooling at all, and it finds a remarkable number of problems that an automated checker will never report.

Do the rename in its own commit. It feels like extra ceremony right up until someone tries to find out when the behavior changed and the history shows a single enormous diff. Separating the mechanical change from the interesting one costs thirty seconds and saves a very long afternoon.

The review comment that stuck with me was a question: what happens if this runs twice? It had not occurred to me, and the answer was unpleasant. Since then I ask it of every job, webhook and migration I touch, and it has been worth more than any tool I have installed.

The actual fix

--- a/inc/cache.php
+++ b/inc/cache.php
@@ -12,3 +12,3 @@
-$key = 'related_' . $id . '_' . $generation;
+$key = 'related_' . $id;
 return get_transient( $key );

After that, the only thing left was to delete the workaround. This is the step that tends to get skipped, which is how a codebase ends up with fixes for bugs that stopped existing years ago. Put the removal in the same ticket, or accept that it will outlive everyone on the team.

Old code deserves a little respect, even when it is bad. It has been running in production for years and has already met all the strange inputs you have not thought of yet. Read it carefully before rewriting it, because the odd conditional in the middle is usually a scar from a real incident.

The fix itself took four lines. Finding out where those four lines belonged took most of a week, including one afternoon spent proving that the bug was not where everyone, including me, had been absolutely certain it was. That is a normal ratio, and it is worth telling new people about.

What to watch for

We now write a short note after every incident, and the rule is that it has to be useful to somebody who was asleep at the time. What happened, how we noticed, what we did and what we changed. Blame is not one of the headings, and it has never needed to be.

1 comment on “Replacing a Transient With Post Meta”

Leave a Reply

Your email address will not be published. Required fields are marked *