<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Joost.blog</title><description>Joost de Valk - internet entrepreneur, founder of Yoast, investor at Emilia Capital</description><link>https://joost.blog/</link><language>en-us</language><atom:link href="https://joost.blog/feed.xml" rel="self" type="application/rss+xml"/><managingEditor>joost@joost.blog (Joost de Valk)</managingEditor><webMaster>joost@joost.blog (Joost de Valk)</webMaster><item><title>How I made my skills update themselves</title><link>https://joost.blog/self-updating-agent-skills/</link><guid isPermaLink="true">https://joost.blog/self-updating-agent-skills/</guid><description>Agent Skills install as loose folders. There&apos;s no npm, no registry, no daemon checking for updates. I shipped a new version of a skill and realized I had no way of telling the users still running the old one. So I taught the skills to notice — and then to update themselves.</description><pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I updated one of my &lt;a href=&quot;https://github.com/jdevalk/skills&quot;&gt;Agent Skills&lt;/a&gt; today and realized I had no way to tell my other machines they were running a stale copy. Skills install as loose folders in &lt;code&gt;~/.claude/skills/&lt;/code&gt; (or wherever your agent puts them). There&apos;s no &lt;code&gt;npm outdated&lt;/code&gt;, no &lt;code&gt;brew upgrade&lt;/code&gt;, no update daemon. The skill just runs whatever&apos;s on disk.&lt;/p&gt;
&lt;p&gt;So I added a small pattern that makes each skill check itself on invocation. If it&apos;s out of date, the skill offers to install the update before continuing. I couldn&apos;t find anyone doing quite this. The whole thing is a few lines of configuration per skill.&lt;/p&gt;
&lt;h2&gt;Why versioning matters when posts and skills ship together&lt;/h2&gt;
&lt;p&gt;Most of &lt;a href=&quot;https://github.com/jdevalk/skills&quot;&gt;my skills&lt;/a&gt; are the executable companion to a post:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-astro-seo&quot;&gt;astro-seo skill&lt;/a&gt; implements &lt;a href=&quot;/astro-seo-complete-guide/&quot;&gt;Astro SEO: the definitive guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-github-repo-optimizer&quot;&gt;github-repo skill&lt;/a&gt; implements &lt;a href=&quot;/healthy-github-repository/&quot;&gt;How to create a healthy GitHub repository&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-github-profile-optimizer&quot;&gt;github-profile skill&lt;/a&gt; implements &lt;a href=&quot;/good-looking-github-profile-pages/&quot;&gt;Good-looking GitHub profile pages&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-wordpress-github-actions&quot;&gt;wp-github-actions skill&lt;/a&gt; implements &lt;a href=&quot;/github-actions-wordpress/&quot;&gt;GitHub Actions to keep your WordPress plugin healthy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each pairing turns an opinionated blog post into a drop-in workflow.&lt;/p&gt;
&lt;p&gt;This is a shipping pattern I like a lot: the post argues for a set of choices, the skill makes those choices the default. Want to understand why? Read the post. Want to apply it? Run the skill.&lt;/p&gt;
&lt;p&gt;But opinions evolve. When I update the Astro SEO guide to cover a new &lt;a href=&quot;/astro-seo-complete-guide/#build-time-validation&quot;&gt;build-time validator&lt;/a&gt;, I update the skill in the same pass. If users don&apos;t know their installed skill is behind the post, they&apos;ll follow instructions that no longer match what I&apos;d write today. An Agent Skill without a version check is cached documentation. Useful until it&apos;s wrong. And you don&apos;t find out until it is.&lt;/p&gt;
&lt;p&gt;That&apos;s the specific problem I&apos;m solving. The pattern below is general, but this is why it matters to me.&lt;/p&gt;
&lt;h2&gt;The pattern&lt;/h2&gt;
&lt;p&gt;Four pieces.&lt;/p&gt;
&lt;p&gt;First, every SKILL.md carries a &lt;code&gt;version:&lt;/code&gt; field in its frontmatter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
name: astro-seo
version: &quot;0.4&quot;
description: &amp;gt;
  Audits and improves SEO for Astro sites...
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Second, a single &lt;code&gt;versions.json&lt;/code&gt; at the repo root maps each skill name to its current version:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;astro-seo&quot;: &quot;0.4&quot;,
    &quot;readability-check&quot;: &quot;0.4&quot;,
    &quot;github-repo&quot;: &quot;0.3&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Third, every SKILL.md includes a short paragraph that tells the skill to self-check when it runs — and, if the user approves, to install the update inline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Before running, fetch https://raw.githubusercontent.com/jdevalk/skills/main/versions.json
and compare the `astro-seo` entry to the `version:` in this file&apos;s frontmatter.
If the manifest version is higher, tell the user the skill is out of date and
offer to update it now. If they agree, run:

    curl -fsSL https://github.com/jdevalk/skills/releases/latest/download/astro-seo.skill \
      -o /tmp/astro-seo.skill \
      &amp;amp;&amp;amp; unzip -oq /tmp/astro-seo.skill -d &amp;lt;parent of this skill&apos;s directory&amp;gt; \
      &amp;amp;&amp;amp; rm /tmp/astro-seo.skill

After the unzip, ask the user to re-invoke the skill so the new version loads
into context. The check is informational and never blocks: if the user
declines, continue with the rest of the workflow on the current version.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Fourth, a CI job that fails any PR where a skill&apos;s frontmatter version doesn&apos;t match its &lt;code&gt;versions.json&lt;/code&gt; entry. The manifest and the shipped skills can&apos;t drift.&lt;/p&gt;
&lt;p&gt;That&apos;s it. No runtime. No service. The skill checks on each invocation using the agent&apos;s own &lt;code&gt;WebFetch&lt;/code&gt; tool. If the user approves the update, the skill re-uses the &lt;code&gt;Bash&lt;/code&gt; tool that&apos;s already there to pull the latest release and unpack it in place. Users find out they&apos;re behind — and can be caught up — at exactly the moment it matters: when they&apos;re about to run the skill.&lt;/p&gt;
&lt;h2&gt;Why no cache&lt;/h2&gt;
&lt;p&gt;The obvious concern is that every skill invocation now costs a network round-trip. Three reasons I left it alone:&lt;/p&gt;
&lt;p&gt;The WebFetch tool already has a built-in 15-minute cache. Multiple skill invocations in one work session share the response, which covers the realistic hot path. I&apos;m not going to beat that with hand-rolled caching.&lt;/p&gt;
&lt;p&gt;A longer cache fights the purpose. The whole point of the check is to alert me about updates. A 24-hour cache means I could miss a release for a day. The check is already non-blocking. A cache miss costs less than running a stale skill for another day.&lt;/p&gt;
&lt;p&gt;The fetch is around a hundred milliseconds against a static GitHub raw URL. In a human-paced workflow where I invoke a skill once or twice per session, it&apos;s noise. If I ever ship fifty skills and it matters, the better fix is to centralize. One shared manifest fetch per session, not one per skill. But that needs harness support Claude Code doesn&apos;t expose to skill authors today. So the skill is the smallest unit with an execution context, and that&apos;s where the check lives.&lt;/p&gt;
&lt;h2&gt;What I couldn&apos;t find&lt;/h2&gt;
&lt;p&gt;Before writing this up I went looking for prior art. The closest patterns in the Agent Skills ecosystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Anthropic&apos;s &lt;code&gt;marketplace.json&lt;/code&gt; plus &lt;code&gt;/install&lt;/code&gt;&lt;/strong&gt;: version tracked in a marketplace manifest, updates happen via an external CLI command or an &lt;code&gt;update_marketplace.py&lt;/code&gt; script. The user has to step out of whatever they were doing to learn they&apos;re behind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/yizhiyanhua-ai/skills-updater&quot;&gt;skills-updater&lt;/a&gt;&lt;/strong&gt; (community skill): scans your installed skills, checks each against its remote repo, and offers to update them. The closest prior art I found. But it&apos;s a separate skill you remember to run, not a check embedded in the skill you&apos;re actually trying to use.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smithery, OpenCode, and other skill hosts&lt;/strong&gt;: track versions for distribution but don&apos;t ship a self-check.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of them put the check — and the install — inside the skill itself, at invocation time, against a repo-local manifest. Which is fair: it&apos;s not a big idea. It&apos;s just a pattern that package managers have had for decades, applied to a new distribution format that doesn&apos;t have one yet.&lt;/p&gt;
&lt;p&gt;If you maintain skills and your users install them as loose files, steal this. It works for any agent whose skill format is &quot;a folder with a markdown file and some frontmatter.&quot;&lt;/p&gt;
&lt;p&gt;Have you seen a better workflow for keeping agent skills in sync with their source? I&apos;d genuinely like to know. This is the best I could come up with, but that&apos;s a low bar when the format is new enough that conventions haven&apos;t settled.&lt;/p&gt;
</content:encoded><category>Development</category><category>AI</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Standards don&apos;t prove themselves</title><link>https://joost.blog/standards-dont-prove-themselves/</link><guid isPermaLink="true">https://joost.blog/standards-dont-prove-themselves/</guid><description>The SEO Framework analyzed 180,000 AI bot requests and found zero llms.txt lookups. Their conclusion: not worth implementing. That conclusion reveals a misunderstanding of how web standards work, and a willingness to only pick sides after the debate is settled.</description><pubDate>Mon, 13 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Measuring whether a standard works by checking if anyone uses it before it exists is backward. And yet that&apos;s exactly what The SEO Framework just did.&lt;/p&gt;
&lt;p&gt;They &lt;a href=&quot;https://github.com/sybrew/the-seo-framework/issues/732#issuecomment-4226935622&quot;&gt;published data&lt;/a&gt; showing that across six months, 57 AI bots, and 180,000 AI-related requests to their site, not a single bot requested &lt;code&gt;llms.txt&lt;/code&gt;. &lt;a href=&quot;https://x.com/TheSEOFramework/status/2042723331049136341&quot;&gt;Their conclusion&lt;/a&gt;: implementing it would be &quot;a waste of resources.&quot; Yoast SEO and Rank Math, they imply, are selling a feature that doesn&apos;t do what it promises.&lt;/p&gt;
&lt;p&gt;The data is solid. I have no reason to doubt the methodology. The conclusion is where it breaks.&lt;/p&gt;
&lt;h2&gt;How standards actually get adopted&lt;/h2&gt;
&lt;p&gt;Web standards don&apos;t get adopted because bots start requesting something that doesn&apos;t exist yet. They get adopted because publishers start serving something, which gives crawlers a reason to look for it, which gives more publishers a reason to serve it. That&apos;s the cycle. Someone has to go first.&lt;/p&gt;
&lt;p&gt;XML sitemaps followed exactly this path. Google launched the protocol in June 2005. For over a year, Google was the only search engine using it. Yahoo and Microsoft didn&apos;t join until November 2006. Ask.com waited until April 2007. If you&apos;d analyzed server logs in early 2006 and concluded &quot;only one search engine uses sitemaps, not worth implementing,&quot; you&apos;d have been right about the data and completely wrong about the trajectory.&lt;/p&gt;
&lt;p&gt;IndexNow is an even more recent example. Bing and Yandex launched it in October 2021. By August 2022, &lt;a href=&quot;https://blogs.bing.com/webmaster/august-2022/IndexNow-adoption-gains-momentum&quot;&gt;over 16 million websites&lt;/a&gt; were publishing 1.2 billion URLs per day through it. By September 2023, that grew to 60 million websites and 1.4 billion URLs daily. Google still hasn&apos;t adopted it. If you&apos;d waited for Google to adopt IndexNow before implementing it, you&apos;d still be waiting. Meanwhile, Bing serves a significant and growing share of AI-powered search through Copilot and ChatGPT.&lt;/p&gt;
&lt;p&gt;The pattern is always the same: someone builds it, someone else adopts it, adoption creates incentive, incentive creates more adoption. At no point does a standard prove itself to an empty room.&lt;/p&gt;
&lt;h2&gt;The multiplier effect&lt;/h2&gt;
&lt;p&gt;Here&apos;s what makes this particularly relevant for SEO plugins. Yoast SEO runs on &lt;a href=&quot;https://wordpress.org/plugins/wordpress-seo/&quot;&gt;over 10 million WordPress sites&lt;/a&gt;. Rank Math runs on &lt;a href=&quot;https://wordpress.org/plugins/seo-by-rank-math/&quot;&gt;over 3 million&lt;/a&gt;. When those plugins ship a feature, it doesn&apos;t create one data point. It creates millions of endpoints overnight.&lt;/p&gt;
&lt;p&gt;That&apos;s the multiplier effect. SEO plugins don&apos;t just implement standards. They &lt;em&gt;create the supply&lt;/em&gt; that gives crawlers a reason to consume them. When Yoast shipped XML sitemaps, it didn&apos;t help a few individual sites get indexed. It made sitemaps ubiquitous enough that every search engine had to support them. When Yoast added IndexNow, it turned a niche protocol into infrastructure overnight.&lt;/p&gt;
&lt;p&gt;The SEO Framework has around 200,000 active installs. That&apos;s a solid user base, and the plugin has earned its reputation for being lightweight and well-built. But 200,000 sites choosing not to serve &lt;code&gt;llms.txt&lt;/code&gt; has negligible impact on whether AI bots will ever look for it. The adoption question will be decided by the plugins with 10 million installs, not 200,000.&lt;/p&gt;
&lt;p&gt;Which makes the framing of their tweet odd. They&apos;re not saying &quot;we&apos;re choosing not to implement this yet.&quot; They&apos;re implying Yoast and Rank Math are misleading their users by shipping a feature no AI bot actually uses. And honestly, there&apos;s something to that: both Yoast and Rank Math market &lt;code&gt;llms.txt&lt;/code&gt; with stronger claims about its current usefulness than the evidence supports.&lt;/p&gt;
&lt;p&gt;Shipping the feature is the right call. Overselling it isn&apos;t. But The SEO Framework&apos;s response overcorrects in the other direction, dismissing the concept entirely based on the absence of adoption that hasn&apos;t had time to materialize. And it&apos;s a strong implication to make from a position that has essentially no influence on the outcome they&apos;re measuring.&lt;/p&gt;
&lt;h2&gt;The cost of waiting vs. the cost of trying&lt;/h2&gt;
&lt;p&gt;Implementing &lt;code&gt;llms.txt&lt;/code&gt; in a WordPress plugin is trivial. You&apos;re generating a markdown file from data the plugin already has: titles, URLs, descriptions. The cost is measured in hours, not weeks. The ongoing maintenance cost is near zero. Arguably, the time spent analyzing six months of server logs to prove &lt;code&gt;llms.txt&lt;/code&gt; is a &quot;waste of resources&quot; could have been spent implementing it.&lt;/p&gt;
&lt;p&gt;The potential upside? If AI systems start consuming &lt;code&gt;llms.txt&lt;/code&gt; (and they might, precisely because millions of sites now serve one), your users benefit immediately. If they don&apos;t, you&apos;ve lost almost nothing.&lt;/p&gt;
&lt;p&gt;Compare that to the cost of waiting. If you wait until &lt;code&gt;llms.txt&lt;/code&gt; is proven, your users are late. They don&apos;t get the early-mover advantage. And you&apos;ve contributed nothing to the ecosystem that might have made it succeed.&lt;/p&gt;
&lt;p&gt;This isn&apos;t unique to &lt;code&gt;llms.txt&lt;/code&gt;. The same logic applies to every emerging standard in this space.&lt;/p&gt;
&lt;h2&gt;It&apos;s not just llms.txt&lt;/h2&gt;
&lt;p&gt;The broader question isn&apos;t whether &lt;code&gt;llms.txt&lt;/code&gt; specifically will win. It&apos;s whether we&apos;re building toward machine-readable architecture at all. And there are multiple complementary approaches, each solving a different piece of the puzzle.&lt;/p&gt;
&lt;h3&gt;Content negotiation&lt;/h3&gt;
&lt;p&gt;Cloudflare launched &lt;a href=&quot;https://blog.cloudflare.com/markdown-for-agents/&quot;&gt;Markdown for Agents&lt;/a&gt; in February 2026, serving markdown instead of HTML when an agent sends an &lt;code&gt;Accept: text/markdown&lt;/code&gt; header. I &lt;a href=&quot;/markdown-alternate/&quot;&gt;built the same thing independently&lt;/a&gt; as a WordPress plugin, with richer metadata and dedicated &lt;code&gt;.md&lt;/code&gt; URLs. Claude Code and other AI coding tools already send this header. The infrastructure is ready. The 80% token reduction over HTML is real and measurable.&lt;/p&gt;
&lt;h3&gt;Schema endpoints and schema maps&lt;/h3&gt;
&lt;p&gt;Instead of requiring agents to crawl every page to understand your site, you can serve your entire structured data graph through a single endpoint. Yoast launched &lt;a href=&quot;https://yoast.com/yoast-seo-march-3-2026/&quot;&gt;Schema Aggregation&lt;/a&gt; in March 2026, in collaboration with Microsoft&apos;s NLWeb project, bringing this to millions of WordPress sites. I&apos;m proud that the company I co-founded is doing this, though I wish they&apos;d moved faster. I&apos;ve &lt;a href=&quot;/seo-graph/&quot;&gt;built the same concept&lt;/a&gt; into my Astro SEO library and have it running on &lt;a href=&quot;/schemamap.xml&quot;&gt;this site&lt;/a&gt;. The approach is sound: give agents a complete, deduplicated map of your content in one request instead of making them reverse-engineer it from thousands of HTML pages.&lt;/p&gt;
&lt;h3&gt;NLWeb&lt;/h3&gt;
&lt;p&gt;Microsoft&apos;s open protocol for connecting websites with AI systems. It builds on schema.org, which LLMs already understand well. Still early, but the pieces are falling into place. A &lt;code&gt;&amp;lt;link rel=&quot;nlweb&quot;&amp;gt;&lt;/code&gt; tag in your HTML head, a conversational endpoint, and your existing structured data. One line of HTML and you&apos;re discoverable.&lt;/p&gt;
&lt;h3&gt;llms.txt itself&lt;/h3&gt;
&lt;p&gt;A curated, markdown-formatted index of your most important content, placed at a well-known URL. Simple, low-cost, and easy to generate automatically.&lt;/p&gt;
&lt;p&gt;These aren&apos;t competing standards. They&apos;re layers. &lt;code&gt;llms.txt&lt;/code&gt; helps agents find your content. Content negotiation helps them consume it efficiently. Schema endpoints help them understand its structure and relationships. NLWeb lets them query it conversationally. You don&apos;t pick one. You build toward all of them.&lt;/p&gt;
&lt;h2&gt;I&apos;ve been writing about this for a while&lt;/h2&gt;
&lt;p&gt;Back in 2022, &lt;a href=&quot;/optimize-crawling-for-the-environment/&quot;&gt;optimizing crawling for the environment&lt;/a&gt; showed how WordPress creates seven or more URLs for every single post, all of which get crawled, none of which add information. The &lt;a href=&quot;/optimize-crawling-lets-turn-things-around/&quot;&gt;follow-up proposed inverting the crawling model entirely&lt;/a&gt;: what if search engines only crawled URLs that sites explicitly listed in their sitemaps?&lt;/p&gt;
&lt;p&gt;By 2025, &lt;a href=&quot;/build-websites-like-2005/&quot;&gt;AI systems were struggling to understand the modern web&lt;/a&gt; because so much of it is buried behind JavaScript rendering and div soup. The irony: &lt;code&gt;llmstxt.org&lt;/code&gt; was already pushing for exactly the kind of machine-readable content that post argued for.&lt;/p&gt;
&lt;p&gt;This year, the pieces are getting built. &lt;a href=&quot;/markdown-alternate/&quot;&gt;Markdown Alternate&lt;/a&gt; for serving markdown to agents. &lt;a href=&quot;/seo-graph/&quot;&gt;seo-graph&lt;/a&gt; for linked JSON-LD knowledge graphs. Schema endpoints and schema maps for agent discovery. NLWeb integration. Each piece is small. None of them have won yet. But they&apos;re all attempts to answer the same question: how do we make the web readable by machines, not just browsers?&lt;/p&gt;
&lt;p&gt;The thread connecting all of this is simple: everyone who understands crawling can see that there&apos;s an opportunity to optimize how search engines and LLMs retrieve data from websites. That optimization is in the interest of the crawlers, those being crawled, and everyone in between (hosts, CDNs, the environment). The question has never been &lt;em&gt;whether&lt;/em&gt; to build machine-readable architecture. It&apos;s &lt;em&gt;how&lt;/em&gt;, and &lt;em&gt;how fast&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Picking sides after the debate is settled&lt;/h2&gt;
&lt;p&gt;What bothers me about The SEO Framework&apos;s position isn&apos;t the decision not to implement &lt;code&gt;llms.txt&lt;/code&gt;. Plenty of reasonable people could look at the current state and decide to wait. That&apos;s a legitimate technical judgment.&lt;/p&gt;
&lt;p&gt;What bothers me is the framing. They&apos;re not saying &quot;we&apos;re watching this space.&quot; They&apos;re implying the plugins that &lt;em&gt;did&lt;/em&gt; implement it are misleading their users, positioning &quot;we only pick sides when the data is clear&quot; as the principled stance.&lt;/p&gt;
&lt;p&gt;But there&apos;s nothing principled about only joining a debate after it&apos;s been settled. That&apos;s just showing up to the victory party. Standards need early adopters who are willing to invest before the returns are obvious. That&apos;s what moves the ecosystem forward. Measuring traffic to a file that barely exists yet and concluding the concept is flawed is like checking how many people show up to a restaurant before you&apos;ve opened it. And concluding nobody wants to eat there.&lt;/p&gt;
&lt;p&gt;I&apos;ve been &lt;a href=&quot;/ai-optimization-is-replaying-early-seo-just-faster/&quot;&gt;arguing&lt;/a&gt; that the SEO community should be playing a role in building better standards for AI systems, not waiting for the platforms to figure it out. We should be giving them clear feedback on how this &lt;em&gt;could&lt;/em&gt; all work better. SEOs have always played a role in bettering (and worsening) the web. We should keep playing that role, preferably the first one.&lt;/p&gt;
&lt;p&gt;The SEO Framework makes a good plugin. Their data analysis was thorough. But their conclusion says more about their approach to the industry than it does about &lt;code&gt;llms.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Standards don&apos;t prove themselves. People prove them by building.&lt;/p&gt;
</content:encoded><category>SEO</category><category>AI</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Astro SEO: the definitive guide</title><link>https://joost.blog/astro-seo-complete-guide/</link><guid isPermaLink="true">https://joost.blog/astro-seo-complete-guide/</guid><description>In 2008 I wrote &quot;WordPress SEO: the definitive guide.&quot; Eighteen years later, this is the Astro version. The full SEO stack: linked JSON-LD graphs, per-collection sitemaps, auto-generated OG images, IndexNow, schema endpoints for agent discovery, and why keyphrase optimization matters less than you think.</description><pubDate>Sun, 12 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In 2008, I wrote &lt;a href=&quot;https://yoast.com/wordpress-seo/&quot;&gt;WordPress SEO: the definitive guide&lt;/a&gt;. It became one of the most-linked SEO articles on the internet and laid the groundwork for what eventually became Yoast SEO. The tools have changed, the web has changed, and my thinking on several fundamentals has evolved. This is the Astro version.&lt;/p&gt;
&lt;p&gt;When I &lt;a href=&quot;/do-you-need-a-cms/&quot;&gt;moved this blog to Astro&lt;/a&gt;, people asked: what about SEO? The answer is that everything I did on WordPress, I do on Astro. And because I control the entire HTML output, most of it is easier to get right. No theme conflicts, no plugin fights over head tags, no render-blocking resources injected by something I forgot I installed.&lt;/p&gt;
&lt;p&gt;No server to compromise, no database to inject into, no login to brute force. From an SEO perspective, static HTML on a CDN is a better starting point than most CMSes will ever give you.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;not-prose rounded-lg border-2 border-primary/20 bg-tertiary px-6 py-5 dark:border-accent/20 dark:bg-stone-900&quot;&amp;gt;
&amp;lt;p class=&quot;mb-2 text-lg font-semibold text-primary dark:text-accent&quot;&amp;gt;Want your AI agent to do this for you?&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;text-base leading-relaxed text-stone-700 dark:text-stone-300&quot;&amp;gt;Install my &amp;lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-astro-seo&quot; class=&quot;text-primary underline decoration-1 underline-offset-2 hover:no-underline dark:text-accent&quot;&amp;gt;Astro SEO skill&amp;lt;/a&amp;gt;, or point your AI coding agent at this article. Everything below is written so an agent can read it and implement it directly.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the full stack.&lt;/p&gt;
&lt;h2&gt;1. Technical foundation&lt;/h2&gt;
&lt;h3&gt;One component for all head metadata&lt;/h3&gt;
&lt;p&gt;The starting point is &lt;a href=&quot;https://github.com/jdevalk/seo-graph&quot;&gt;&lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt;&lt;/a&gt;, a package I &lt;a href=&quot;/seo-graph/&quot;&gt;built for exactly this purpose&lt;/a&gt;. The &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component handles everything you&apos;d normally scatter across your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;: title, description, canonical, Open Graph, Twitter cards, hreflang, and the JSON-LD graph.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
import Seo from &apos;@jdevalk/astro-seo-graph/Seo.astro&apos;;
---

&amp;lt;Seo
    title=&quot;My Post | My Site&quot;
    description=&quot;A concise description for search engines.&quot;
    canonical=&quot;https://example.com/my-post/&quot;
    ogType=&quot;article&quot;
    ogImage=&quot;https://example.com/og/my-post.jpg&quot;
    ogImageAlt=&quot;My Post&quot;
    ogImageWidth={1200}
    ogImageHeight={675}
    siteName=&quot;My Site&quot;
    twitter={{ card: &apos;summary_large_image&apos;, site: &apos;@handle&apos; }}
    article={{ publishedTime: publishDate, tags: [&apos;Astro&apos;, &apos;SEO&apos;] }}
    graph={graph}
    extraLinks={[
        { rel: &apos;icon&apos;, type: &apos;image/svg+xml&apos;, href: &apos;/favicon.svg&apos; },
        { rel: &apos;sitemap&apos;, href: &apos;/sitemap-index.xml&apos; },
        { rel: &apos;alternate&apos;, type: &apos;application/rss+xml&apos;, href: &apos;/feed.xml&apos;, title: &apos;RSS&apos; },
    ]}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s the entire &lt;code&gt;BaseHead.astro&lt;/code&gt; here (minus font preloads and analytics). It used to be 130 lines.&lt;/p&gt;
&lt;p&gt;The component handles several SEO details automatically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Canonical URLs&lt;/strong&gt; are derived from Astro&apos;s &lt;code&gt;site&lt;/code&gt; config with query parameters stripped by default (UTM tags and other tracking params don&apos;t create duplicate canonicals)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Robots meta&lt;/strong&gt; always includes &lt;code&gt;max-snippet:-1&lt;/code&gt;, &lt;code&gt;max-image-preview:large&lt;/code&gt;, &lt;code&gt;max-video-preview:-1&lt;/code&gt; for maximum snippet sizes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Canonical is omitted when &lt;code&gt;noindex&lt;/code&gt; is true&lt;/strong&gt;, per Google&apos;s recommendation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Twitter tags&lt;/strong&gt; that duplicate their Open Graph equivalents are suppressed (Twitter falls back to OG automatically)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hreflang alternates&lt;/strong&gt; for multilingual sites. This blog is English-only so it doesn&apos;t need them, but &lt;a href=&quot;https://limonaia.house&quot;&gt;limonaia.house&lt;/a&gt; uses the same &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component with &lt;code&gt;alternates&lt;/code&gt; to tell search engines which page is the Italian version and which is the English version. Without hreflang, Google may show the wrong language version in search results, or treat the translations as duplicate content.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Auto-generated OG images&lt;/h3&gt;
&lt;p&gt;Every page gets a 1200×675 Open Graph image generated at build time. That size matters: Google Discover requires images at least 1200px wide, and the 16:9 ratio works well across social platforms. The route at &lt;code&gt;/og/[...slug].jpg&lt;/code&gt; uses &lt;a href=&quot;https://github.com/vercel/satori&quot;&gt;satori&lt;/a&gt; to render a template to SVG, then &lt;a href=&quot;https://sharp.pixelplumbing.com/&quot;&gt;sharp&lt;/a&gt; to convert it to JPEG. If the post has a featured image, it&apos;s composited into the design. If not, the template fills with the title and site branding.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/use-avif-webp-share-images/&quot;&gt;Why JPEG and not WebP or AVIF?&lt;/a&gt; Because social platforms don&apos;t reliably support modern formats yet.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component derives the OG image URL from the page slug automatically:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const slug = Astro.url.pathname.replace(/^\/|\/$/g, &apos;&apos;);
const ogImage = new URL(`/og/${slug || &apos;index&apos;}.jpg`, SITE_URL).toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No manual image creation, no missing OG images, no forgetting to update them when you change the title.&lt;/p&gt;
&lt;h3&gt;Build-time validation&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;seoGraph()&lt;/code&gt; integration that handles IndexNow also validates your built HTML on every build. All six checks are on by default:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;H1 validation:&lt;/strong&gt; Warns about pages with zero or more than one &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; element, a common SEO and accessibility issue that&apos;s easy to miss in templates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Duplicate title/description detection:&lt;/strong&gt; Checks across all built pages for duplicate &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; or meta description values. Here, it caught paginated blog pages all sharing the same title, a corpus-level SEO problem that per-page checks can&apos;t find.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema validation:&lt;/strong&gt; Validates the JSON-LD structured data on every page, which brings us to the next layer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Image alt validation:&lt;/strong&gt; Warns about &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags without an &lt;code&gt;alt&lt;/code&gt; attribute. Ran this once on my own site and got a list of 24 images across 14 posts with missing alt text that I&apos;d missed over the years. Fixed in an afternoon.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Metadata length validation:&lt;/strong&gt; Flags titles or meta descriptions outside SERP-safe bounds. Defaults are title 30–65 and description 70–200. Configurable per site — a personal blog with listing pages like &lt;code&gt;/ask/&lt;/code&gt; can loosen the title min, which is what I did here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal link validation:&lt;/strong&gt; Scans every page&apos;s &lt;code&gt;&amp;lt;a href&amp;gt;&lt;/code&gt; values and checks them against the set of paths the build actually produced. Catches the common &quot;I linked to &lt;code&gt;/about-me&lt;/code&gt; but the page is &lt;code&gt;/about-me/&lt;/code&gt;&quot; bug, where the site &quot;works&quot; via a 301 redirect but wastes a round-trip on every visit. Catches typo-broken links too. Supports a &lt;code&gt;skip&lt;/code&gt; callback for paths handled at the CDN layer (like generated sitemaps).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What the link validator found on this site the first time I ran it: a broken &lt;code&gt;/about-me&lt;/code&gt; (no trailing slash) in one post, every pagination link in my blog archive missing its trailing slash, and five internal links to &lt;code&gt;/plugins/*&lt;/code&gt; that were being served via a redirect because the pages had moved to a different domain. The first two took five minutes to fix, the third was a real content issue I replaced with direct links.&lt;/p&gt;
&lt;p&gt;Beyond build-time checks, add a &lt;strong&gt;broken link checker&lt;/strong&gt; to your CI pipeline. A &lt;a href=&quot;https://github.com/lycheeverse/lychee-action&quot;&gt;lychee&lt;/a&gt; GitHub Action that runs on every push to your content files catches dead links to external URLs — which &lt;code&gt;validateInternalLinks&lt;/code&gt; doesn&apos;t cover. A weekly scheduled run catches link rot as external sites move or disappear. Broken outbound links are a bad user experience and a negative trust signal.&lt;/p&gt;
&lt;h2&gt;2. Structured data&lt;/h2&gt;
&lt;p&gt;Most sites that have structured data at all output a flat snippet: a single &lt;code&gt;Article&lt;/code&gt; or &lt;code&gt;WebPage&lt;/code&gt; object. That&apos;s better than nothing, but it doesn&apos;t tell search engines or AI agents how things connect. Who wrote this article? What site is it on? What organization does the author work for?&lt;/p&gt;
&lt;p&gt;I &lt;a href=&quot;/seo-graph/&quot;&gt;wrote about this in detail&lt;/a&gt; when I shipped &lt;code&gt;@jdevalk/seo-graph-core&lt;/code&gt;, the graph engine that powers this site&apos;s structured data. The short version: every page outputs a linked &lt;code&gt;@graph&lt;/code&gt; with &lt;code&gt;WebSite&lt;/code&gt;, &lt;code&gt;Blog&lt;/code&gt;, &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;WebPage&lt;/code&gt;, &lt;code&gt;BlogPosting&lt;/code&gt;, &lt;code&gt;BreadcrumbList&lt;/code&gt;, and &lt;code&gt;ImageObject&lt;/code&gt; entities. They&apos;re wired together with &lt;code&gt;@id&lt;/code&gt; references so an agent can walk the relationships.&lt;/p&gt;
&lt;p&gt;Trust signals in your graph matter more than they used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;publishingPrinciples&lt;/code&gt; tells agents where your editorial policy lives&lt;/li&gt;
&lt;li&gt;&lt;code&gt;copyrightHolder&lt;/code&gt; and &lt;code&gt;copyrightYear&lt;/code&gt; communicate rights&lt;/li&gt;
&lt;li&gt;&lt;code&gt;knowsAbout&lt;/code&gt; helps topical authority&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SearchAction&lt;/code&gt; on the &lt;code&gt;WebSite&lt;/code&gt; tells agents how to search your content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can &lt;a href=&quot;https://classyschema.org/Visualisation?url=https%3A%2F%2Fjoost.blog%2F&quot;&gt;visualize this blog&apos;s graph&lt;/a&gt; to see how the entities connect.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component takes the assembled graph as a prop and renders it as &lt;code&gt;&amp;lt;script type=&quot;application/ld+json&quot;&amp;gt;&lt;/code&gt; alongside all the meta tags. Everything in one place.&lt;/p&gt;
&lt;p&gt;The seo-graph repo includes a &lt;a href=&quot;https://github.com/jdevalk/seo-graph/blob/main/AGENTS.md&quot;&gt;3,000-line AGENTS.md&lt;/a&gt; with recipes for fourteen site types (blog, e-commerce, vacation rental, podcast, documentation, and more), so your AI coding agent knows which entities to pick for your specific case.&lt;/p&gt;
&lt;h2&gt;3. Content optimization&lt;/h2&gt;
&lt;p&gt;This is where my thinking has changed the most since 2008.&lt;/p&gt;
&lt;h3&gt;Topics over keyphrases&lt;/h3&gt;
&lt;p&gt;The original WordPress SEO guide spent a lot of time on keyphrase optimization: pick a focus keyword, use it in your title, heading, first paragraph, meta description. That advice was correct for a world where search engines matched strings. It&apos;s much less relevant in 2026.&lt;/p&gt;
&lt;p&gt;Modern search is vectorized. Engines and AI models convert your content into mathematical representations that capture &lt;em&gt;meaning&lt;/em&gt;, not exact word matches. Writing about &quot;building websites with Astro&quot; has a much better chance of surfacing for related queries like &quot;static site generators&quot; than it used to. You don&apos;t need those exact phrases in your text. Exact keyword placement still has some effect, but it&apos;s far less important than covering the topic thoroughly and clearly.&lt;/p&gt;
&lt;p&gt;That doesn&apos;t mean titles and descriptions don&apos;t matter. They do, because humans read them in search results and social shares. But optimizing them for exact keyword placement is solving a problem from 2015.&lt;/p&gt;
&lt;h3&gt;Write for humans and extraction&lt;/h3&gt;
&lt;p&gt;Readability has always mattered, but the reason has expanded. It&apos;s no longer just about keeping humans engaged. AI systems pull answers from your content, and the unit of extraction is the paragraph. A self-contained paragraph that makes its point without requiring context from surrounding paragraphs is the one that gets surfaced in AI-generated answers. If your paragraph can&apos;t stand on its own, an AI agent can&apos;t use it.&lt;/p&gt;
&lt;p&gt;Several specific things matter more than they used to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lead with the point.&lt;/strong&gt; Every paragraph should open with its most important sentence. That&apos;s what AI systems quote, and it&apos;s what L2 English readers use to decide whether to keep reading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Keep sentences short.&lt;/strong&gt; Sentences over 20 words are harder to parse, especially for readers who aren&apos;t native English speakers. Most of your audience reads English as a second language. Write for them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One idea per paragraph.&lt;/strong&gt; When a paragraph does two things, neither is extractable. Break it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use transitions.&lt;/strong&gt; Words like &quot;because&quot;, &quot;however&quot;, &quot;for example&quot; tell readers (and machines) how paragraphs connect. Without them, your post reads like a list of unrelated statements.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Avoid filler.&lt;/strong&gt; Words like &quot;basically&quot;, &quot;simply&quot;, &quot;really&quot;, &quot;just&quot; add length without meaning. Cut them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The same writing discipline that makes content readable to humans makes it useful to machines. If you write with an AI agent, the &lt;a href=&quot;https://github.com/jdevalk/skills#-readability-check&quot;&gt;readability-check skill&lt;/a&gt; can audit your drafts against these criteria automatically.&lt;/p&gt;
&lt;h3&gt;Enforce structure at the content level&lt;/h3&gt;
&lt;p&gt;Astro&apos;s content collections let you enforce SEO fields at the schema level using Zod validation. &lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt; provides &lt;code&gt;seoSchema&lt;/code&gt; which enforces title length (5-120 characters) and description length (15-160 characters):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { seoSchema } from &apos;@jdevalk/astro-seo-graph&apos;;

const blog = defineCollection({
    schema: ({ image }) =&amp;gt; z.object({
        title: z.string(),
        publishDate: z.coerce.date(),
        seo: seoSchema(image).optional(),
    }),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If someone adds a 200-character SEO title, the build fails. That&apos;s more reliable than a runtime warning in a CMS sidebar.&lt;/p&gt;
&lt;p&gt;The schema endpoints also include &lt;code&gt;articleBody&lt;/code&gt; with the full text of each post (markdown-stripped, up to 10K characters), giving AI agents access to your content through structured data, not just by scraping rendered HTML.&lt;/p&gt;
&lt;h2&gt;4. Site structure&lt;/h2&gt;
&lt;h3&gt;Content collections as typed architecture&lt;/h3&gt;
&lt;p&gt;On WordPress, your site structure lives in the database: posts, pages, categories, tags, custom post types. On Astro, it lives in the file system as content collections with typed schemas. Each collection is a directory of Markdown files with frontmatter validated against a Zod schema at build time.&lt;/p&gt;
&lt;p&gt;This is stricter than WordPress taxonomies. A blog post that&apos;s missing a required &lt;code&gt;publishDate&lt;/code&gt; or has a malformed &lt;code&gt;category&lt;/code&gt; array won&apos;t build. The site structure is enforced by the type system, not by conventions that plugins may or may not follow.&lt;/p&gt;
&lt;h3&gt;One taxonomy, not two&lt;/h3&gt;
&lt;p&gt;Most sites should not use both categories and tags. Pick one taxonomy and commit to it. If you have both, one of them is almost certainly adding clutter without adding navigational value. I &lt;a href=&quot;/research-wordpress-publications-misuse-tags/&quot;&gt;researched this&lt;/a&gt; and wrote &lt;a href=&quot;https://progressplanner.com/plugins/fewer-tags/&quot;&gt;a plugin&lt;/a&gt; about it for WordPress. On Astro, I simply didn&apos;t build tags. This site uses categories only.&lt;/p&gt;
&lt;h3&gt;Breadcrumbs linked to the graph&lt;/h3&gt;
&lt;p&gt;Breadcrumbs aren&apos;t just a visual navigation aid. In the JSON-LD graph, each breadcrumb item can reference a schema entity via &lt;code&gt;@id&lt;/code&gt;. Here, the &quot;Blog&quot; crumb in every blog post&apos;s breadcrumb trail links directly to the &lt;code&gt;Blog&lt;/code&gt; entity in the graph, not just to the &lt;code&gt;/blog/&lt;/code&gt; URL. That tells agents the structural relationship between the breadcrumb and the blog as a publication.&lt;/p&gt;
&lt;h3&gt;Internal linking&lt;/h3&gt;
&lt;p&gt;Internal linking doesn&apos;t need to be a manual process. Tools like &lt;a href=&quot;https://github.com/safishamsi/graphify&quot;&gt;Graphify&lt;/a&gt; can analyze your content as a knowledge graph and surface linking opportunities you&apos;d never find by scanning posts manually. The structure of your internal links is one of the strongest signals you control. Automate the discovery; be intentional about the execution.&lt;/p&gt;
&lt;h2&gt;5. Performance&lt;/h2&gt;
&lt;p&gt;Performance is where Astro&apos;s architecture does most of the work for you.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Static by default.&lt;/strong&gt; Every page is pre-rendered to HTML at build time. No server-side rendering, no database queries, no PHP execution. The baseline is already fast because there&apos;s nothing slow to do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Zero JavaScript by default.&lt;/strong&gt; Astro ships no client-side JS unless you explicitly add interactive components. Compare that to WordPress, where the average page loads dozens of scripts from themes, plugins, and third-party services.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Image optimization.&lt;/strong&gt; Astro&apos;s built-in &lt;code&gt;&amp;lt;Image&amp;gt;&lt;/code&gt; component generates responsive &lt;code&gt;srcset&lt;/code&gt; attributes, converts to WebP, and adds &lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt; and &lt;code&gt;decoding=&quot;async&quot;&lt;/code&gt; automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Font preloading.&lt;/strong&gt; Preload your primary web font in woff2 format. Mona Sans is preloaded in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; so it&apos;s available before the first paint.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View Transitions.&lt;/strong&gt; Astro&apos;s &lt;code&gt;&amp;lt;ClientRouter /&amp;gt;&lt;/code&gt; with &lt;code&gt;defaultStrategy: &apos;viewport&apos;&lt;/code&gt; prefetches links as they scroll into view, making navigation feel instant while keeping the initial load minimal.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;CDN headers.&lt;/strong&gt; Hashed assets under &lt;code&gt;/_astro/&lt;/code&gt; get &lt;code&gt;Cache-Control: public, max-age=31536000, immutable&lt;/code&gt;. They never need revalidation because the filename changes when the content changes. How you set these headers depends on your platform: Cloudflare Pages and Netlify use a &lt;code&gt;_headers&lt;/code&gt; file, Vercel uses &lt;code&gt;vercel.json&lt;/code&gt;, and other hosts typically use server config.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/No-Vary-Search&quot;&gt;No-Vary-Search&lt;/a&gt;.&lt;/strong&gt; UTM parameters break browser caching: &lt;code&gt;?utm_source=linkedin&lt;/code&gt; and &lt;code&gt;?utm_source=email&lt;/code&gt; are treated as different resources. The &lt;code&gt;No-Vary-Search&lt;/code&gt; response header tells the browser to ignore specified query parameters when matching cached responses:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;No-Vary-Search: key-order, params=(&quot;utm_source&quot; &quot;utm_medium&quot; &quot;utm_campaign&quot; &quot;utm_content&quot; &quot;utm_term&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same page, different UTM tags, one cached response. Supported in Chrome, degrades gracefully elsewhere.&lt;/p&gt;
&lt;h2&gt;6. Sitemaps and indexing&lt;/h2&gt;
&lt;h3&gt;Per-collection sitemaps&lt;/h3&gt;
&lt;p&gt;Astro&apos;s &lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/sitemap/&quot;&gt;&lt;code&gt;@astrojs/sitemap&lt;/code&gt;&lt;/a&gt; integration generates sitemaps, but the defaults are a single file with no &lt;code&gt;lastmod&lt;/code&gt; dates. Both are easy to fix.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;chunks&lt;/code&gt; option splits your sitemap by content type:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sitemap({
    entryLimit: 1000,
    chunks: {
        &apos;posts&apos;: (item) =&amp;gt; {
            if (isABlogPost(new URL(item.url).pathname)) return item;
        },
        &apos;videos&apos;: (item) =&amp;gt; {
            if (/\/videos\/[^/]+/.test(new URL(item.url).pathname)) return item;
        },
    },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this blog, that produces &lt;code&gt;sitemap-posts-0.xml&lt;/code&gt;, &lt;code&gt;sitemap-videos-0.xml&lt;/code&gt;, and &lt;code&gt;sitemap-pages-0.xml&lt;/code&gt; (the default bucket for unmatched URLs). Per-collection sitemaps make it much easier to debug indexing issues in Google Search Console and Bing Webmaster Tools, since each collection shows separately.&lt;/p&gt;
&lt;h3&gt;Git-based lastmod&lt;/h3&gt;
&lt;p&gt;For &lt;code&gt;lastmod&lt;/code&gt;, the &lt;code&gt;serialize&lt;/code&gt; callback adds dates. Frontmatter &lt;code&gt;publishDate&lt;/code&gt; only gives you a date, not a timestamp. File system timestamps reset on CI. The reliable source is git:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function gitLastmod(filePath) {
    const log = execSync(
        `git log -1 --format=&quot;%cI&quot; -- &quot;${filePath}&quot;`,
        { encoding: &apos;utf-8&apos; }
    ).trim();
    return log ? new Date(log) : null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you the actual timestamp of the last commit that touched each content file, falling back to frontmatter dates when git history isn&apos;t reliable (for example, after a bulk content migration).&lt;/p&gt;
&lt;h3&gt;IndexNow&lt;/h3&gt;
&lt;p&gt;Sitemaps are passive: you publish them and wait for crawlers to find the changes. &lt;a href=&quot;https://www.indexnow.org/&quot;&gt;IndexNow&lt;/a&gt; is the active counterpart. When you publish or update content, you push a notification to Bing, Yandex, and other supporting search engines, telling them the URL has changed.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt; ships an Astro integration that submits all built URLs to IndexNow automatically after each build:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// astro.config.mjs
import seoGraph from &apos;@jdevalk/astro-seo-graph/integration&apos;;

export default defineConfig({
    integrations: [
        seoGraph({
            indexNow: {
                key: &apos;your-indexnow-key&apos;,
                host: &apos;example.com&apos;,
                siteUrl: &apos;https://example.com&apos;,
            },
        }),
    ],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You also need a key verification route so search engines can confirm host ownership:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// src/pages/[your-key].txt.ts
import { createIndexNowKeyRoute } from &apos;@jdevalk/astro-seo-graph&apos;;

export const GET = createIndexNowKeyRoute({ key: &apos;your-indexnow-key&apos; });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When I first enabled this, the build pinged Bing with all 153 URLs on the site. Without IndexNow, you publish and wait for search engines to discover your changes on their next crawl, which can take days or weeks. With it, the search engine knows about your new or updated pages within seconds of the build finishing.&lt;/p&gt;
&lt;h3&gt;RSS feed&lt;/h3&gt;
&lt;p&gt;An RSS feed is still one of the most reliable discovery mechanisms. Feed readers, podcast apps, and an increasing number of AI agents consume RSS directly. Astro&apos;s &lt;a href=&quot;https://docs.astro.build/en/guides/rss/&quot;&gt;&lt;code&gt;@astrojs/rss&lt;/code&gt;&lt;/a&gt; package makes this straightforward: you create a route that pulls your content collection, renders each post&apos;s body to HTML, and returns a valid RSS 2.0 feed. The &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component advertises it with a &lt;code&gt;&amp;lt;link rel=&quot;alternate&quot; type=&quot;application/rss+xml&quot;&amp;gt;&lt;/code&gt; tag in the head so browsers and agents can discover it automatically.&lt;/p&gt;
&lt;p&gt;Include the full post content in your feed, not just excerpts. Truncated feeds frustrate readers and give AI systems less to work with. If someone is subscribed to your feed, they&apos;ve already opted in to reading your writing.&lt;/p&gt;
&lt;h3&gt;robots.txt&lt;/h3&gt;
&lt;p&gt;A dynamic &lt;code&gt;robots.txt&lt;/code&gt; route references the sitemap index and the schema map (see next section):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User-agent: *
Allow: /

Sitemap: https://joost.blog/sitemap-index.xml
Schemamap: https://joost.blog/schemamap.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;7. Agent discovery&lt;/h2&gt;
&lt;p&gt;Sitemaps and IndexNow help search engines find your content. Agent discovery helps AI systems understand it.&lt;/p&gt;
&lt;h3&gt;Schema endpoints and schema map&lt;/h3&gt;
&lt;p&gt;Schema endpoints serve a corpus-wide JSON-LD graph that lets an agent understand your entire site in one request. This is part of Microsoft&apos;s &lt;a href=&quot;https://github.com/nlweb-ai/NLWeb&quot;&gt;NLWeb&lt;/a&gt; specification. It&apos;s still early, but the setup is simple enough that it&apos;s worth having ready:&lt;/p&gt;
&lt;p&gt;Each endpoint collects every entry in a content collection, builds the full JSON-LD graph for each entry, and serves the combined result as &lt;code&gt;application/ld+json&lt;/code&gt;. This site has three: &lt;code&gt;/schema/post.json&lt;/code&gt;, &lt;code&gt;/schema/page.json&lt;/code&gt;, and &lt;code&gt;/schema/video.json&lt;/code&gt;. A schema map at &lt;code&gt;/schemamap.xml&lt;/code&gt; lists them all, like a sitemap but for structured data. The &lt;code&gt;Schemamap:&lt;/code&gt; directive in &lt;code&gt;robots.txt&lt;/code&gt; points agents to it. See the &lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/astro-seo-graph#schema-endpoints&quot;&gt;&lt;code&gt;astro-seo-graph&lt;/code&gt; documentation&lt;/a&gt; for the full implementation.&lt;/p&gt;
&lt;h3&gt;Markdown alternates&lt;/h3&gt;
&lt;p&gt;AI agents parse markdown more reliably than HTML. Astro sites are unusually well-placed to serve it: content collections already &lt;em&gt;are&lt;/em&gt; markdown, so there&apos;s no lossy HTML-to-markdown conversion — you hand the agent the source. &lt;code&gt;astro-seo-graph&lt;/code&gt; ships a &lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/astro-seo-graph#markdown-alternate&quot;&gt;&lt;code&gt;createMarkdownEndpoint&lt;/code&gt;&lt;/a&gt; factory that exposes a &lt;code&gt;.md&lt;/code&gt; URL for every page (frontmatter + body + a &lt;code&gt;X-Markdown-Tokens&lt;/code&gt; header so agents can size requests). The &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component auto-emits &lt;code&gt;&amp;lt;link rel=&quot;alternate&quot; type=&quot;text/markdown&quot; href=&quot;…&quot;&amp;gt;&lt;/code&gt; so agents discover it the same way browsers discover an RSS feed.&lt;/p&gt;
&lt;p&gt;For content negotiation — agents that send &lt;code&gt;Accept: text/markdown&lt;/code&gt; on the canonical URL — add a Cloudflare Transform Rule that rewrites the path when the header is present. The &lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/astro-seo-graph#3-accept-textmarkdown-content-negotiation-cloudflare&quot;&gt;README has the exact rule&lt;/a&gt;; it works on the free plan and needs no SSR.&lt;/p&gt;
&lt;h3&gt;llms.txt&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://llmstxt.org&quot;&gt;&lt;code&gt;llms.txt&lt;/code&gt; standard&lt;/a&gt; provides a machine-readable summary of your site at &lt;code&gt;/llms.txt&lt;/code&gt;. Think of it as a &lt;code&gt;robots.txt&lt;/code&gt; for AI: it tells language models what your site is about, what content is available, and where to find it. The &lt;code&gt;seoGraph()&lt;/code&gt; integration generates it automatically at build time from your built pages:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;seoGraph({
    llmsTxt: {
        title: &apos;My Site&apos;,
        siteUrl: &apos;https://example.com&apos;,
        summary: &apos;What this site is about.&apos;,
    },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No AI bot widely requests &lt;code&gt;llms.txt&lt;/code&gt; today. &lt;a href=&quot;/standards-dont-prove-themselves/&quot;&gt;That&apos;s not the point&lt;/a&gt;. Standards don&apos;t prove themselves before adoption. SEO plugins that ship &lt;code&gt;llms.txt&lt;/code&gt; across millions of sites create the supply that gives crawlers a reason to look for it.&lt;/p&gt;
&lt;h3&gt;NLWeb discovery&lt;/h3&gt;
&lt;p&gt;A &lt;code&gt;&amp;lt;link rel=&quot;nlweb&quot;&amp;gt;&lt;/code&gt; tag in the head points to the site&apos;s conversational endpoint, for AI agents that support Microsoft&apos;s &lt;a href=&quot;https://github.com/nlweb-ai/NLWeb&quot;&gt;NLWeb protocol&lt;/a&gt;. Early days, but the tag is one line.&lt;/p&gt;
&lt;h2&gt;8. Redirects and error handling&lt;/h2&gt;
&lt;h3&gt;Redirects&lt;/h3&gt;
&lt;p&gt;When you change a URL, delete a page, or consolidate duplicate content, you need redirects. How you configure them depends on your hosting platform. On Cloudflare Pages, a &lt;code&gt;_redirects&lt;/code&gt; file in &lt;code&gt;public/&lt;/code&gt; works. On Netlify, you can use the same &lt;code&gt;_redirects&lt;/code&gt; format or &lt;code&gt;netlify.toml&lt;/code&gt;. On Vercel, redirects go in &lt;code&gt;vercel.json&lt;/code&gt;. The syntax differs but the principle is the same: map every old URL to its new location with a 301 status.&lt;/p&gt;
&lt;p&gt;Keep this maintained. Every URL that ever existed and moved should have a redirect. Search engines transfer link equity through 301 redirects, and users who bookmarked the old URL still arrive where they should.&lt;/p&gt;
&lt;h3&gt;FuzzyRedirect on 404&lt;/h3&gt;
&lt;p&gt;For the URLs that slip through, the &lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/astro-seo-graph#fuzzy-404-redirect&quot;&gt;&lt;code&gt;FuzzyRedirect&lt;/code&gt;&lt;/a&gt; component from &lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt; acts as a safety net. It fetches your sitemap, computes Levenshtein distance against the current URL, and either auto-redirects (above 85% similarity) or shows a &quot;Did you mean...&quot; suggestion. Catches typos and old URL patterns without maintaining a redirect table.&lt;/p&gt;
&lt;h2&gt;9. Analytics and measurement&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://plausible.io/&quot;&gt;Plausible&lt;/a&gt;&lt;/strong&gt; for traffic analytics. Privacy-friendly, no cookie banner needed, lightweight script that doesn&apos;t slow down the page.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://search.google.com/search-console&quot;&gt;Google Search Console&lt;/a&gt;&lt;/strong&gt; for indexing status, structured data validation, and search performance. The per-collection sitemaps make debugging easier: if something isn&apos;t being indexed, you can see immediately whether it&apos;s a posts problem, a videos problem, or something else.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://www.bing.com/webmasters&quot;&gt;Bing Webmaster Tools&lt;/a&gt;&lt;/strong&gt; for the same reasons, and because Bing&apos;s index feeds AI products like Copilot and ChatGPT. If your content isn&apos;t indexed by Bing, it&apos;s invisible to a growing share of how people find information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Structured data validation.&lt;/strong&gt; Use Google&apos;s &lt;a href=&quot;https://search.google.com/test/rich-results&quot;&gt;Rich Results Test&lt;/a&gt; and &lt;a href=&quot;https://classyschema.org/Visualisation&quot;&gt;ClassySchema&lt;/a&gt; to verify your JSON-LD graph. Check that every &lt;code&gt;@id&lt;/code&gt; reference resolves, that the entity relationship tree is complete, and that trust signals like &lt;code&gt;publishingPrinciples&lt;/code&gt; and &lt;code&gt;copyrightHolder&lt;/code&gt; are present.&lt;/p&gt;
&lt;h2&gt;The full stack&lt;/h2&gt;
&lt;p&gt;Here&apos;s what it looks like assembled:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jdevalk/seo-graph&quot;&gt;&lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt;&lt;/a&gt; for the &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component, schema endpoints, schema map, IndexNow, &lt;code&gt;llms.txt&lt;/code&gt;, FuzzyRedirect, and build-time validation (includes &lt;code&gt;@jdevalk/seo-graph-core&lt;/code&gt; for the JSON-LD graph engine)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.astro.build/en/guides/integrations-guide/sitemap/&quot;&gt;&lt;code&gt;@astrojs/sitemap&lt;/code&gt;&lt;/a&gt; with &lt;code&gt;chunks&lt;/code&gt; for per-collection sitemaps and &lt;code&gt;serialize&lt;/code&gt; for git-based lastmod&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/vercel/satori&quot;&gt;satori&lt;/a&gt; + &lt;a href=&quot;https://sharp.pixelplumbing.com/&quot;&gt;sharp&lt;/a&gt; for auto-generated OG images&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/shishkin/astro-pagefind&quot;&gt;astro-pagefind&lt;/a&gt; for client-side search (which the &lt;code&gt;SearchAction&lt;/code&gt; in the schema points to)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/jdevalk/skills#-readability-check&quot;&gt;readability-check skill&lt;/a&gt; for AI-assisted content auditing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of it is open source. You can see it running live on this site: view source on any page for the JSON-LD graph, check the &lt;a href=&quot;/sitemap-index.xml&quot;&gt;sitemap index&lt;/a&gt;, the &lt;a href=&quot;/schemamap.xml&quot;&gt;schema map&lt;/a&gt;, or &lt;a href=&quot;https://classyschema.org/Visualisation?url=https%3A%2F%2Fjoost.blog%2F&quot;&gt;visualize the full graph&lt;/a&gt; for the homepage.&lt;/p&gt;
&lt;p&gt;Eighteen years after that first WordPress SEO guide, the principles haven&apos;t changed: make your content accessible to both humans and machines, structure it so search engines understand it, and make the whole thing fast. What has changed is that the machines reading your content are no longer just crawlers following links. They&apos;re AI agents that understand meaning, extract paragraphs, and walk knowledge graphs. The SEO stack should reflect that. This one does.&lt;/p&gt;
&lt;p&gt;If you&apos;re building an Astro site with an AI coding agent, point it at this post and let it set everything up for you.&lt;/p&gt;
</content:encoded><category>SEO</category><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Shipping pieces of the machine-readable web</title><link>https://joost.blog/seo-graph/</link><guid isPermaLink="true">https://joost.blog/seo-graph/</guid><description>I&apos;ve been arguing that the next open web needs machine-readable architecture. So I built a piece: seo-graph, a schema.org JSON-LD graph engine that now powers four sites and a CMS plugin. Built in a single session with Claude Code.</description><pubDate>Fri, 10 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I wrote that the open web doesn&apos;t need more defenders, it needs &lt;a href=&quot;/defending-open-web-not-enough/&quot;&gt;builders&lt;/a&gt;. That the next open web needs machine-readable architecture: content structured for machines, not just rendered for browsers. Linked knowledge graphs that search engines and AI agents can consume without reverse-engineering HTML.&lt;/p&gt;
&lt;p&gt;That&apos;s the argument. Here&apos;s what I actually built.&lt;/p&gt;
&lt;h2&gt;The problem that started it&lt;/h2&gt;
&lt;p&gt;I was building an SEO plugin for &lt;a href=&quot;/emdash-cms/&quot;&gt;EmDash&lt;/a&gt;, and the core problem was generating a valid schema.org &lt;code&gt;@graph&lt;/code&gt; for every page. Not just a flat snippet, but a proper linked graph: &lt;code&gt;WebSite&lt;/code&gt;, &lt;code&gt;WebPage&lt;/code&gt;, &lt;code&gt;Article&lt;/code&gt;, &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;BreadcrumbList&lt;/code&gt;, all wired together with &lt;code&gt;@id&lt;/code&gt; references so an agent or search engine can walk the relationships.&lt;/p&gt;
&lt;p&gt;I&apos;d already written that logic for this blog. It worked, but it was tangled into the Astro components. When I needed the same graph logic in EmDash, the choice was: copy it and maintain two versions, or extract it into something shareable.&lt;/p&gt;
&lt;p&gt;Before AI, &quot;extract this into a shared library&quot; was a decision you&apos;d defer for months. Set up the monorepo, extract the code, write the tests, publish to npm, migrate the consumers, verify nothing broke. That&apos;s a week of work, and it&apos;s boring work, the kind that loses every prioritization fight against shipping features. With Claude Code, I went from &quot;I should extract this&quot; to two npm packages shipping with over a hundred tests, four migrated consumers, and CI with provenance in one session. The decision to abstract stopped being &quot;someday&quot; and became &quot;right now, while I&apos;m thinking about it.&quot;&lt;/p&gt;
&lt;p&gt;That&apos;s the real shift AI enables. Not that it writes code faster. That it makes the &lt;em&gt;right architectural decision&lt;/em&gt; the easy one, instead of the expensive one you keep putting off.&lt;/p&gt;
&lt;h2&gt;What seo-graph is&lt;/h2&gt;
&lt;p&gt;The result is a monorepo with two packages on npm, both MIT-licensed:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/seo-graph-core&quot;&gt;&lt;strong&gt;&lt;code&gt;@jdevalk/seo-graph-core&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; is the engine. Pure TypeScript, no framework dependencies. Specialized builders for the most common entities (&lt;code&gt;buildWebSite&lt;/code&gt;, &lt;code&gt;buildWebPage&lt;/code&gt;, &lt;code&gt;buildArticle&lt;/code&gt;, &lt;code&gt;buildBreadcrumbList&lt;/code&gt;, &lt;code&gt;buildImageObject&lt;/code&gt;, &lt;code&gt;buildVideoObject&lt;/code&gt;) and a generic &lt;code&gt;buildPiece&amp;lt;T&amp;gt;&lt;/code&gt; that handles everything else: &lt;code&gt;Person&lt;/code&gt;, &lt;code&gt;Organization&lt;/code&gt;, &lt;code&gt;Product&lt;/code&gt;, &lt;code&gt;Blog&lt;/code&gt;, &lt;code&gt;Recipe&lt;/code&gt;, &lt;code&gt;Event&lt;/code&gt;, any schema.org type, with full autocomplete via Google&apos;s &lt;a href=&quot;https://github.com/google/schema-dts&quot;&gt;&lt;code&gt;schema-dts&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An &lt;code&gt;IdFactory&lt;/code&gt; keeps &lt;code&gt;@id&lt;/code&gt; references stable across the graph. &lt;code&gt;assembleGraph&lt;/code&gt; wraps everything into a proper JSON-LD envelope with deduplication and optional dangling-reference validation.&lt;/p&gt;
&lt;p&gt;In plain language: you tell it what&apos;s on your page (a blog post, a person, an organization, a product) and it produces a block of structured data that search engines and AI agents can read. Every entity gets a stable identifier so agents can follow the links between them: this article was written by this person, published on this website, filed under this category. That&apos;s the knowledge graph.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/jdevalk/seo-graph/tree/main/packages/astro-seo-graph&quot;&gt;&lt;strong&gt;&lt;code&gt;@jdevalk/astro-seo-graph&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; is the Astro integration. It ships a &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component that handles &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt;, meta description, canonical URLs, Open Graph, Twitter cards, hreflang alternates, and the JSON-LD graph in a single component. It also provides route factories for schema endpoints (&lt;code&gt;createSchemaEndpoint&lt;/code&gt;) and a schema map (&lt;code&gt;createSchemaMap&lt;/code&gt;) for agent discovery. Plus Zod helpers for content collection schemas.&lt;/p&gt;
&lt;p&gt;The third consumer is &lt;a href=&quot;https://github.com/jdevalk/emdash-plugin-seo&quot;&gt;&lt;code&gt;@jdevalk/emdash-plugin-seo&lt;/code&gt;&lt;/a&gt;, the EmDash SEO plugin that started this whole thing. It imports &lt;code&gt;assembleGraph&lt;/code&gt; from the core directly, since EmDash contributes metadata through hooks rather than templates.&lt;/p&gt;
&lt;h2&gt;Four consumers, one graph&lt;/h2&gt;
&lt;p&gt;The real test of an abstraction is whether it works for cases you didn&apos;t design for. Four consumers now use seo-graph in production:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This blog&lt;/strong&gt; (joost.blog) is an Astro site. It uses the full stack: &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, schema endpoints serving the corpus-wide JSON-LD graph, a schema map for discovery. The &lt;code&gt;BaseHead.astro&lt;/code&gt; component that used to be 130 lines of hand-written meta tags and JSON-LD is now a clean &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; call.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://limonaia.house&quot;&gt;limonaia.house&lt;/a&gt;&lt;/strong&gt; is a vacation rental site, also Astro. Completely different content type: &lt;code&gt;VacationRental&lt;/code&gt; instead of &lt;code&gt;Article&lt;/code&gt;. It was the first external consumer, and it plugged in with zero changes to the core or the integration. That&apos;s when I knew the abstraction boundary was right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://cocktail.glass&quot;&gt;cocktail.glass&lt;/a&gt;&lt;/strong&gt; is a cocktail recipe site. Yet another content type: &lt;code&gt;Recipe&lt;/code&gt; with ingredients, instructions, and nutrition data. Same graph engine, same &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; component, different schema.org types.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The EmDash SEO plugin&lt;/strong&gt; runs inside a CMS, not a static site generator. It doesn&apos;t use &lt;code&gt;&amp;lt;Seo&amp;gt;&lt;/code&gt; at all. It uses &lt;code&gt;assembleGraph&lt;/code&gt; and the &lt;code&gt;GraphEntity&lt;/code&gt; type from the core, with its own EmDash-specific piece builders. Same graph engine, completely different integration surface.&lt;/p&gt;
&lt;h2&gt;What the output looks like&lt;/h2&gt;
&lt;p&gt;Here&apos;s what seo-graph produces for a blog post on this site. You can &lt;a href=&quot;https://classyschema.org/Visualisation?url=https%3A%2F%2Fjoost.blog%2Fseo-graph%2F&quot;&gt;visualize the full graph for this post&lt;/a&gt; to see how the entities connect:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;@context&quot;: &quot;https://schema.org&quot;,
  &quot;@graph&quot;: [
    { &quot;@type&quot;: &quot;WebSite&quot;, &quot;@id&quot;: &quot;.../#WebSite&quot;, &quot;publisher&quot;: { &quot;@id&quot;: &quot;.../#Person&quot; }, &quot;hasPart&quot;: [{ &quot;@id&quot;: &quot;.../#Blog&quot; }] },
    { &quot;@type&quot;: &quot;Blog&quot;, &quot;@id&quot;: &quot;.../#Blog&quot;, &quot;isPartOf&quot;: { &quot;@id&quot;: &quot;.../#WebSite&quot; } },
    { &quot;@type&quot;: &quot;Person&quot;, &quot;@id&quot;: &quot;.../#Person&quot;, &quot;name&quot;: &quot;Joost de Valk&quot;, &quot;knowsAbout&quot;: [&quot;SEO&quot;, &quot;...&quot;], &quot;publishingPrinciples&quot;: &quot;...&quot; },
    { &quot;@type&quot;: &quot;WebPage&quot;, &quot;@id&quot;: &quot;.../my-post/&quot;, &quot;isPartOf&quot;: { &quot;@id&quot;: &quot;.../#WebSite&quot; }, &quot;copyrightHolder&quot;: { &quot;@id&quot;: &quot;.../#Person&quot; } },
    { &quot;@type&quot;: &quot;BlogPosting&quot;, &quot;isPartOf&quot;: [{ &quot;@id&quot;: &quot;.../my-post/&quot; }, { &quot;@id&quot;: &quot;.../#Blog&quot; }], &quot;author&quot;: { &quot;@id&quot;: &quot;.../#Person&quot; } },
    { &quot;@type&quot;: &quot;BreadcrumbList&quot;, &quot;itemListElement&quot;: [{ &quot;name&quot;: &quot;Home&quot; }, { &quot;name&quot;: &quot;Blog&quot;, &quot;item&quot;: { &quot;@id&quot;: &quot;.../#Blog&quot; } }, { &quot;name&quot;: &quot;My Post&quot; }] }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every entity links to others via &lt;code&gt;@id&lt;/code&gt; references. The &lt;code&gt;WebSite&lt;/code&gt; contains a &lt;code&gt;Blog&lt;/code&gt;. The &lt;code&gt;BlogPosting&lt;/code&gt; is part of both its &lt;code&gt;WebPage&lt;/code&gt; and the &lt;code&gt;Blog&lt;/code&gt;. The breadcrumb trail links back to the &lt;code&gt;Blog&lt;/code&gt; entity. An agent or search engine can walk these relationships and understand the full structure of your site.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/jdevalk/seo-graph/blob/main/AGENTS.md&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; in the repo has complete code examples for fourteen different site types, from personal blogs to e-commerce to vacation rentals.&lt;/p&gt;
&lt;h2&gt;Built for agents to use, not just humans to read&lt;/h2&gt;
&lt;p&gt;Schema.org JSON-LD is how the web communicates structured knowledge to machines. It&apos;s how Google understands your content. It&apos;s increasingly how AI agents will understand it too. But getting it right is tedious: the spec is massive, the &lt;code&gt;@id&lt;/code&gt; linking is fiddly, and every framework reimplements it from scratch.&lt;/p&gt;
&lt;p&gt;That&apos;s the problem seo-graph solves for the &lt;em&gt;publisher&lt;/em&gt;. But there&apos;s a second problem: how does the developer&apos;s AI agent know which schema.org entities to use? If you ask Claude Code or Cursor to &quot;add structured data to my Astro site,&quot; the agent needs to know that a blog post needs &lt;code&gt;WebSite&lt;/code&gt; + &lt;code&gt;WebPage&lt;/code&gt; + &lt;code&gt;BlogPosting&lt;/code&gt; + &lt;code&gt;BreadcrumbList&lt;/code&gt; + &lt;code&gt;Person&lt;/code&gt;, that a vacation rental needs a &lt;code&gt;VacationRental&lt;/code&gt; with a &lt;code&gt;RentAction&lt;/code&gt;, that a product page needs &lt;code&gt;Product&lt;/code&gt; with &lt;code&gt;BuyAction&lt;/code&gt; and &lt;code&gt;Offer&lt;/code&gt;. That knowledge lives in the schema.org spec, scattered across hundreds of pages.&lt;/p&gt;
&lt;p&gt;That&apos;s why the repo&apos;s &lt;a href=&quot;https://github.com/jdevalk/seo-graph/blob/main/AGENTS.md&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt; is 3,000 lines long. It tells AI coding agents exactly which pieces to pick for every common site type. Fourteen site type recipes (personal blog, e-commerce, local business, vacation rental, podcast, documentation, SaaS, and more), the full API reference, patterns for trust signals like &lt;code&gt;publishingPrinciples&lt;/code&gt; and copyright metadata, and guidance on actions (&lt;code&gt;BuyAction&lt;/code&gt;, &lt;code&gt;RentAction&lt;/code&gt;, &lt;code&gt;QuoteAction&lt;/code&gt;) that tell agents what they can &lt;em&gt;do&lt;/em&gt; with your content.&lt;/p&gt;
&lt;p&gt;The repo also ships &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;.cursorrules&lt;/code&gt;, and &lt;code&gt;.github/copilot-instructions.md&lt;/code&gt; as pointers, so whichever AI coding tool you use, it finds the guide automatically.&lt;/p&gt;
&lt;p&gt;This is what &quot;agent-ready&quot; looks like at the tooling level. The library makes your content machine-readable. The documentation makes the library itself machine-readable. Agents helping agents.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;seo-graph-core&lt;/code&gt; is deliberately small. Seven specialized builders, one generic typed builder, an ID factory, and a graph assembler. No opinions about your content model, your routing, or your framework. The dispatch logic lives in your code, not in the library. That&apos;s what lets the same core drive a blog, a vacation rental, a recipe site, and a CMS plugin without any of them fighting the abstraction.&lt;/p&gt;
&lt;p&gt;If you&apos;re building on Astro, &lt;code&gt;astro-seo-graph&lt;/code&gt; gives you the full integration. If you&apos;re building on something else, &lt;code&gt;seo-graph-core&lt;/code&gt; gives you the graph engine and you wire it into your own templates.&lt;/p&gt;
&lt;h2&gt;One piece at a time&lt;/h2&gt;
&lt;p&gt;I don&apos;t think any single library fixes the open web. But the machine-readable web gets built the same way anything real gets built: one small, concrete piece at a time. This is one piece. It&apos;s &lt;a href=&quot;https://github.com/jdevalk/seo-graph&quot;&gt;on GitHub&lt;/a&gt;, it&apos;s on npm, it&apos;s MIT-licensed, and it&apos;s in production.&lt;/p&gt;
&lt;p&gt;If you&apos;re building for the web and your pages don&apos;t have a linked JSON-LD graph yet, start there. The agents are already reading.&lt;/p&gt;
</content:encoded><category>Development</category><category>AI</category><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Defending the open web is not enough</title><link>https://joost.blog/defending-open-web-not-enough/</link><guid isPermaLink="true">https://joost.blog/defending-open-web-not-enough/</guid><description>Anil Dash calls it the endgame for the open web, and he&apos;s right. But the structural collapse has already started. The indie web is mostly gone, the winners have already left, and defense alone won&apos;t fix it. The question isn&apos;t how to protect what remains. It&apos;s what to build next.</description><pubDate>Tue, 07 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Anil Dash recently published &lt;a href=&quot;https://anildash.com/2026/03/27/endgame-open-web/&quot;&gt;Endgame for the Open Web&lt;/a&gt;, and it&apos;s a piece worth reading. His argument: Big Tech is systematically dismantling the open web through AI scraping, API lockdowns, and the erosion of open source norms. His prescription: defend the institutions. Support the Internet Archive, donate to the EFF, volunteer for Wikipedia.&lt;/p&gt;
&lt;p&gt;He&apos;s right about the threats. And his title, &quot;endgame,&quot; is the right word. We&apos;re not in the opening moves. We&apos;re deep into a collapse that&apos;s already well underway, and the game board looks very different from what most people defending the open web still picture.&lt;/p&gt;
&lt;p&gt;Let me start with what we&apos;re actually talking about. The open web is the part of the internet anyone can access without permission: websites you can visit without an app or an account, content you can link to, read, and build on. A personal blog is the open web. A Squarespace site is the open web. A WordPress site is the open web. A post that lives primarily inside a walled platform like Facebook or TikTok, where the platform controls who sees it and whether it&apos;s linkable, is not. The open web is defined by &lt;em&gt;access&lt;/em&gt;, not by what software powers it.&lt;/p&gt;
&lt;p&gt;That distinction matters, because Anil, and most people in this conversation, conflate the open web with open source. They&apos;re related, but they&apos;re not the same thing. Open source is about who controls the &lt;em&gt;tools&lt;/em&gt;. The open web is about who can access the &lt;em&gt;content&lt;/em&gt;. You can publish to the open web with proprietary software, and you can run open source software behind a login wall.&lt;/p&gt;
&lt;p&gt;The two get conflated because for a long time they went hand in hand. If you wanted to really own your content and infrastructure on the open web, self-hosting open source software was the path of least resistance. Open source was how you made the open web yours. But that&apos;s a historical pairing, not a definition, and it&apos;s worth keeping the concepts separate when what&apos;s happening to one isn&apos;t what&apos;s happening to the other.&lt;/p&gt;
&lt;p&gt;And it matters because the &lt;em&gt;technical&lt;/em&gt; open web is fine. Protocols still work. You can still put up an Astro site. You&apos;re reading this on one. What&apos;s collapsing is the &lt;em&gt;economic&lt;/em&gt; open web: the idea that you can publish independently, reach an audience, and build a sustainable business on open standards. That&apos;s the part in the endgame. Most people haven&apos;t noticed how far it&apos;s gone because the URLs still resolve.&lt;/p&gt;
&lt;h2&gt;How the endgame started&lt;/h2&gt;
&lt;p&gt;Anil documents real threats: AI scrapers that take everything and send nothing back. Traffic drops of 50%, 90% for some publishers. Conventions like robots.txt being ignored. Open APIs getting shut down. Open source projects drowning in AI-generated junk contributions.&lt;/p&gt;
&lt;p&gt;But he mostly frames these as attacks. They&apos;re not just attacks. They&apos;re incentives. And publishers are following them exactly where you&apos;d expect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Bots scrape content and send no traffic back. Publishers lose their audiences. Then they lose their revenue. Then they paywall or quit.&lt;/li&gt;
&lt;li&gt;Conventions like robots.txt get ignored. Publishers can&apos;t set terms anyone respects. Their only options are locking content behind authentication or accepting the loss.&lt;/li&gt;
&lt;li&gt;Revenue collapses. Publishers move to Substack, Spotify, and YouTube, where the platforms monetize for them. They trade ownership for survival.&lt;/li&gt;
&lt;li&gt;Open source projects drown in AI-generated junk contributions. They close to outsiders. Development slows. The tools powering the open web stagnate.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And there&apos;s a fifth chain that Anil doesn&apos;t mention: AI makes generic content free to produce. Just like generic code is now basically free, generic articles, summaries, and how-to guides cost nothing to generate. That collapses the value of commodity content to zero. Publishers who were already struggling to monetize now have to compete with infinite free alternatives to their work. There&apos;s an irony here: SEOs, the people who once helped grow the open web by making content discoverable, &lt;a href=&quot;/unintended-consequences-seo-for-everyone/#what-we-didnt-see-coming&quot;&gt;spent years flooding it&lt;/a&gt; with content optimized for search engines rather than readers. AI just automated what they were already doing, and made the economics of it collapse.&lt;/p&gt;
&lt;p&gt;None of this is a prediction. A lot of it has already happened. &lt;a href=&quot;https://digitalcontentnext.org/blog/2025/09/25/state-of-subscriptions-2025-pushing-past-the-paywall-plateau/&quot;&gt;77% of news publishers&lt;/a&gt; now focus on subscriptions, and Press Gazette tracks the &lt;a href=&quot;https://pressgazette.co.uk/paywalls/digital-subscribers-100k-club-ranking-worlds-biggest-paywalled-news-publishers-2025/&quot;&gt;100+ outlets with over 100,000 paying digital subscribers&lt;/a&gt;. &lt;a href=&quot;https://www.axios.com/2024/08/13/independent-journalists-substack-news&quot;&gt;Independent journalists are moving to Substack at scale&lt;/a&gt;, from Bari Weiss and Casey Newton to Jim Acosta, Chuck Todd, and Derek Thompson. Podcasters went exclusive on Spotify in &lt;a href=&quot;https://variety.com/2024/digital/news/joe-rogan-renews-spotify-deal-not-exclusive-1235895424/&quot;&gt;nine-figure deals&lt;/a&gt;, leaving the open podcast ecosystem behind. Not because they opposed openness, but because openness stopped paying the bills. The open web isn&apos;t being killed by a single dramatic attack. It&apos;s emptying out, slowly, as millions of publishers individually and rationally decide that publishing openly is a bad deal.&lt;/p&gt;
&lt;p&gt;Anil says we should fight back &quot;with the same ferocity with which we&apos;re being attacked.&quot; But this isn&apos;t a battle. It&apos;s an exodus. And you don&apos;t stop an exodus by fighting harder.&lt;/p&gt;
&lt;h2&gt;The winners already left&lt;/h2&gt;
&lt;p&gt;Look at who&apos;s getting the AI training deals. The Financial Times, News Corp, Axel Springer, The Atlantic, Condé Nast, Reddit. Most of them already paywalled their way out of the open web years ago. They have distribution, they have legal teams, they have leverage. They&apos;re getting paid &lt;em&gt;because&lt;/em&gt; they already left. The open web is fine if you&apos;re already not really on it. It&apos;s the people who stayed who are losing.&lt;/p&gt;
&lt;p&gt;This was never really about the open web broadly. It&apos;s about the &lt;em&gt;indie&lt;/em&gt; web: the solo experts, the niche publishers, the bloggers who knew more about one specific thing than almost anyone else in the world. The indie web is the part that&apos;s mostly already gone.&lt;/p&gt;
&lt;p&gt;And the dividing line is distribution. If you already have it, you get a deal. If you don&apos;t, how do you build one now? Not by ranking in search that increasingly summarizes your content without sending traffic. Not by publishing openly in a market flooded with free AI-generated alternatives.&lt;/p&gt;
&lt;p&gt;Maybe by moving to a managed platform that has its own audience. But then you have to ask whether you still own the training rights to your own work, or whether the platform already sold them. In 2024, Automattic &lt;a href=&quot;https://www.404media.co/tumblr-and-wordpress-to-sell-users-data-to-train-ai-tools/&quot;&gt;sold WordPress.com and Tumblr user content&lt;/a&gt; to OpenAI and Midjourney for AI training. Users had to opt out after the fact, if they noticed. That&apos;s the implicit deal on any managed platform: the platform decides what your content is worth, and to whom.&lt;/p&gt;
&lt;p&gt;AI companies are the canaries here. They&apos;ve already figured out the open web isn&apos;t producing enough quality to train frontier models on, and they&apos;re paying expert contractors directly through &lt;a href=&quot;https://scale.com/&quot;&gt;Scale AI&lt;/a&gt;, &lt;a href=&quot;https://surgehq.ai/&quot;&gt;Surge AI&lt;/a&gt;, and in-house expert networks to fill the gap. That&apos;s not a sign the open web is fine. It&apos;s a sign the collapse is already priced in, and the survivors are being picked.&lt;/p&gt;
&lt;h2&gt;Meanwhile, the biggest open source project on the web is busy suing itself&lt;/h2&gt;
&lt;p&gt;WordPress powers a huge chunk of the open web. If any project should be leading the response to these threats, it&apos;s WordPress. Instead, two of the ecosystem&apos;s biggest players have spent years &lt;a href=&quot;https://www.therepository.email/category/business-enterprise/wp-engine-v-automattic&quot;&gt;suing each other&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While the open web&apos;s economics collapse and AI reshapes how content gets consumed, WordPress is still litigating trademark disputes. Not building trust infrastructure. Not making content machine-readable. Not competing with managed platforms on experience. The leadership that should be defending and advancing the open web is too busy fighting to do either, and too distracted to address the &lt;a href=&quot;/wordpress-refactor-not-redecorate/&quot;&gt;deep architectural problems&lt;/a&gt; that have been holding the platform back for years.&lt;/p&gt;
&lt;p&gt;This isn&apos;t just a WordPress problem. It&apos;s a symptom. The open web&apos;s biggest institutions aren&apos;t focused on the existential threats. They&apos;re distracted by internal politics while the ground shifts beneath them.&lt;/p&gt;
&lt;h2&gt;Building what comes next&lt;/h2&gt;
&lt;p&gt;So: the indie web is mostly gone, the structural collapse of the economic open web is well underway, and defending the remains isn&apos;t a strategy. What is?&lt;/p&gt;
&lt;p&gt;Not &quot;defend harder&quot; or &quot;fund the Internet Archive more.&quot; Those are necessary, but they don&apos;t rebuild anything. The real question is what the &lt;em&gt;next&lt;/em&gt; open web looks like: one that&apos;s actually competitive, economically sustainable, and legible to both humans and machines from the start.&lt;/p&gt;
&lt;p&gt;Here&apos;s what that takes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trust infrastructure.&lt;/strong&gt;&amp;lt;br /&amp;gt;
The open web has no native way to prove who published something first, or whether content has been modified since publication. Provenance isn&apos;t a nice-to-have anymore. When AI can generate content indistinguishable from human writing, cryptographic proof of origin becomes foundational. Author credibility, entity consistency, first-party verification: these are the &lt;a href=&quot;/ai-optimization-is-replaying-early-seo-just-faster/&quot;&gt;signals that platforms are already weighting&lt;/a&gt; more heavily.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Machine-readable architecture.&lt;/strong&gt;&amp;lt;br /&amp;gt;
Content on the open web needs to be &lt;a href=&quot;/markdown-alternate/&quot;&gt;structured for machines&lt;/a&gt;, not just rendered for browsers. Honestly, &lt;a href=&quot;/build-websites-like-2005/&quot;&gt;clean semantic HTML&lt;/a&gt; would already be a massive improvement over most of what&apos;s out there. You don&apos;t need exotic formats if your markup is actually readable. From there, proper content models, typed data, and formats that AI agents can consume without reverse-engineering HTML are the next step. And agents need to be able to &lt;em&gt;act&lt;/em&gt;, not just read. Standards like &lt;a href=&quot;https://developer.chrome.com/blog/webmcp-epp&quot;&gt;WebMCP&lt;/a&gt; and WordPress&apos;s new &lt;a href=&quot;https://developer.wordpress.org/news/2025/11/introducing-the-wordpress-abilities-api/&quot;&gt;Abilities API&lt;/a&gt; point in the right direction: make open source platforms&apos; capabilities discoverable and composable by machines, not just by humans clicking through admin panels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Competitive defaults.&lt;/strong&gt;&amp;lt;br /&amp;gt;
Open source projects need to ship with sane, performant defaults that compete with managed platforms out of the box. I&apos;ve been &lt;a href=&quot;/videos/joost-de-valk-the-devastating-power-of-defaults/&quot;&gt;talking about the devastating power of defaults&lt;/a&gt; for over a decade. Not &quot;you can configure it to be fast,&quot; but fast by default. Not &quot;you can add structured data with a plugin,&quot; but structured data as a core feature. WordPress became famous for its five-minute install, a defining default that set the bar for its era. But it hasn&apos;t grasped what the 2026 version of that looks like.&lt;/p&gt;
&lt;h2&gt;Openness has to pay for itself&lt;/h2&gt;
&lt;p&gt;Everything above is about &lt;em&gt;how&lt;/em&gt; to build the next open web. But trust infrastructure, machine-readable content, and competitive defaults all assume something that hasn&apos;t been true for a while: that openness can sustain itself. It can&apos;t, not on its own. And this is the one thing the open web and open source have in common: neither of them works if the economics don&apos;t.&lt;/p&gt;
&lt;p&gt;Open source is not just code. It is a system of incentives. And right now, those incentives don&apos;t align. &lt;a href=&quot;/fair-wordpress-and-knowing-when-to-stop/&quot;&gt;FAIR&lt;/a&gt;, a federated package management system for WordPress built under the Linux Foundation, is a concrete case. The technical project delivered. The problem it solved was real. But when it came to hosting companies actually funding the infrastructure, the answer was no. Not because they disagreed. Because investment means cost, commitment, and risk. The current situation, however uncomfortable, was predictable enough. That&apos;s the pattern: companies benefiting enormously from open source infrastructure refusing to pay for its improvement, even when the alternative is quietly losing it.&lt;/p&gt;
&lt;p&gt;There&apos;s a silver lining, though. At the recent Cloudfest hackathon, the TYPO3 community picked up FAIR, adapted it for their ecosystem, and is now &lt;a href=&quot;https://openchannels.fm/fair-package-management-for-typo3/&quot;&gt;preparing to release it&lt;/a&gt;. The technical work is going to live on, just not in the ecosystem it was built for. Another open source community understood what WordPress hosts wouldn&apos;t.&lt;/p&gt;
&lt;p&gt;This same tension, of hosting companies extracting value without contributing proportionally, is what set off the WordPress lawsuit. Matt Mullenweg was pointing at a real structural problem. The mistake wasn&apos;t seeing the economic misalignment. It was turning the response into a personal and legal fight, ego-driven instead of system-driven. A solvable economic problem became an unwinnable battle of wills.&lt;/p&gt;
&lt;p&gt;The next open web needs business models that make openness economically sustainable from day one. And it needs the companies that profit most from it (hosting companies, search engines, ad networks, AI companies) to fund it. Not as charity, but because their businesses quite literally depend on a functioning open ecosystem.&lt;/p&gt;
&lt;p&gt;Money alone doesn&apos;t fix this, though. Funding has to come with proper governance: foundations, neutral stewardship, clear rules about what a project will and won&apos;t do. That&apos;s exactly what WordPress lacks and exactly why FAIR was built under the Linux Foundation in the first place. A well-funded project controlled by a single commercial entity isn&apos;t shared infrastructure. It&apos;s just a product with volunteers.&lt;/p&gt;
&lt;p&gt;There are promising signs. &lt;a href=&quot;https://www.x402.org/&quot;&gt;x402&lt;/a&gt;, an open standard for internet-native payments over HTTP backed by Cloudflare, Coinbase, Stripe, AWS and others, is already live and processing real transactions. If bots can pay instead of just scrape, publishing openly stops being charity and starts being a business again. Anthropic is investing $100M in credits and $4M in donations through &lt;a href=&quot;https://www.anthropic.com/glasswing&quot;&gt;Project Glasswing&lt;/a&gt; to find and fix security vulnerabilities in open source software. That&apos;s an AI company putting real money back into the infrastructure it was built on. Two companies isn&apos;t a movement. But it&apos;s more than nothing, and more than most.&lt;/p&gt;
&lt;h2&gt;The open web doesn&apos;t need more defenders.&amp;lt;br /&amp;gt;It needs builders.&lt;/h2&gt;
&lt;p&gt;Anil is right that something precious is at stake. Keep supporting the Archive and the EFF and Wikipedia. They matter. But defending the remains of the indie web is not the same thing as reviving it. The thing we actually cared about, independent publishers reaching audiences on their own terms, isn&apos;t coming back by being defended harder. It comes back, if it comes back at all, by being rebuilt on better foundations. Managed platforms can&apos;t do it: their business models depend on you staying, and none of them let you take your URL, your traffic, and your integrations with you. Only something open can.&lt;/p&gt;
&lt;p&gt;None of this is hypothetical. WebMCP is being standardized. x402 is being implemented. New open source CMSs are shipping with structured content and scoped permissions out of the box. The pieces exist. What&apos;s missing is the collective will to assemble them, and the funding, with the right governance, to sustain the people doing the work.&lt;/p&gt;
&lt;p&gt;Everything Big Tech built was possible because the web was open. Open content and open source trained their models. Open source powers their infrastructure. Open protocols let them scale. And now AI lets them build faster than ever, on the back of the same open ecosystem they&apos;ve helped hollow out. The anger at that is justified. But anger alone doesn&apos;t build anything. A better open web does. Let&apos;s build it.&lt;/p&gt;
</content:encoded><category>Open Source</category><category>AI</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress needs to refactor, not redecorate</title><link>https://joost.blog/wordpress-refactor-not-redecorate/</link><guid isPermaLink="true">https://joost.blog/wordpress-refactor-not-redecorate/</guid><description>WordPress&apos;s deepest problems aren&apos;t cosmetic, they&apos;re architectural. The responses to EmDash from Matt Mullenweg, Hendrik Luehrsen, and Brian Coords crystallize what needs to change: the data model, content storage, and plugin permissions.</description><pubDate>Fri, 03 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;When Cloudflare launched &lt;a href=&quot;/emdash-cms/&quot;&gt;EmDash CMS&lt;/a&gt; on April 1st, the reactions came fast — from Matt Mullenweg &lt;a href=&quot;https://ma.tt/2026/04/emdash-feedback/&quot;&gt;himself&lt;/a&gt;, from &lt;a href=&quot;https://kraut.press/2026/emdash-cms-shows-gutenberg-limits/&quot;&gt;Hendrik Luehrsen&lt;/a&gt; at Kraut.press, and from &lt;a href=&quot;https://www.briancoords.com/emdash-first-thoughts-and-takeaways-for-wordpress/&quot;&gt;Brian Coords&lt;/a&gt;. Each piece approached EmDash differently, but together they crystallized something I&apos;ve been thinking about for years: WordPress&apos;s deepest technical problems aren&apos;t at the surface. They&apos;re architectural. And the WordPress project keeps treating them as cosmetic.&lt;/p&gt;
&lt;p&gt;A Cloudflare piece on &lt;a href=&quot;https://www.cloudflare.com/the-net/modernize-applications/&quot;&gt;application modernization&lt;/a&gt; offers a useful way to think about this. When organizations modernize legacy applications, they generally pick from three approaches: cosmetic changes (move to new infrastructure, change nothing underneath), targeted refactoring (rework the architecture in specific areas while preserving what works), or a full rewrite (start from scratch).&lt;/p&gt;
&lt;p&gt;Most successful organizations mix and match, picking the right approach for each component based on risk and business value. The worst thing you can do is mistake cosmetic changes for real modernization.&lt;/p&gt;
&lt;p&gt;WordPress, right now, is doing exactly that. And the responses to EmDash show why.&lt;/p&gt;
&lt;h2&gt;The surface vs. the structure&lt;/h2&gt;
&lt;p&gt;Matt&apos;s &lt;a href=&quot;https://ma.tt/2026/04/emdash-feedback/&quot;&gt;response&lt;/a&gt; was generous in places. He acknowledged the engineering quality and called the Skills implementation &quot;brilliant.&quot; But his architectural arguments were almost entirely defensive. He suggested EmDash should adopt Gutenberg. He framed EmDash&apos;s sandboxed plugin model as impractical. He questioned Cloudflare&apos;s business motives.&lt;/p&gt;
&lt;p&gt;What he didn&apos;t do was engage with the structural critique. Not once.&lt;/p&gt;
&lt;p&gt;That&apos;s telling, because the structural critique is what every other respondent picked up on. Hendrik&apos;s piece went straight to the heart of it: Gutenberg maintains a structured block tree while you&apos;re editing, then serializes it to HTML comments in &lt;code&gt;post_content&lt;/code&gt; when it saves. Every system that needs that structure downstream (APIs, headless frontends, AI agents) has to parse it back out again. WordPress VIP built an entire Block Data API just to avoid this. The &lt;code&gt;parse_blocks()&lt;/code&gt; function exists because the storage model doesn&apos;t preserve what the editor already knows.&lt;/p&gt;
&lt;p&gt;That&apos;s not a content model. That&apos;s a workaround for the absence of one.&lt;/p&gt;
&lt;h2&gt;The data model WordPress never fixed&lt;/h2&gt;
&lt;p&gt;The content storage issue goes deeper than Gutenberg. WordPress&apos;s fundamental data model dates back to its origins as a blogging platform. Every piece of content (posts, pages, products, courses, events) lives in &lt;code&gt;wp_posts&lt;/code&gt;. Every piece of metadata lives in &lt;code&gt;wp_postmeta&lt;/code&gt; as key-value pairs. This is essentially a schema-less design wearing a relational database as a costume.&lt;/p&gt;
&lt;p&gt;In EmDash, each content type gets its own typed table. Custom fields become actual columns with proper types and indexes. This isn&apos;t exotic — it&apos;s how applications have stored data for decades. WordPress&apos;s approach made sense when the platform did one thing. It stopped making sense when plugins started turning WordPress into an application framework. And yet, twenty-plus years later, the schema remains essentially unchanged.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://make.wordpress.org/core/2026/03/31/extending-the-7-0-cycle/&quot;&gt;recent deferral of WordPress 7.0&lt;/a&gt; illustrates the problem in real time. The release was delayed because the team needs to revisit how real-time collaboration data is stored — the initial approach of stuffing it into postmeta wasn&apos;t going to hold up. They&apos;re now considering a custom table. This is exactly the pattern: a new feature runs into the limits of the existing data model, and the team has to work around it or pause to rethink.&lt;/p&gt;
&lt;p&gt;What makes this worse is what the delay is &lt;em&gt;for&lt;/em&gt;. Real-time collaborative editing — essentially Google Docs inside the block editor — will ship turned off by default. As Matt Cromwell &lt;a href=&quot;https://www.therepository.email/wordpress-collaboration-vs-ai-opportunity&quot;&gt;argued in The Repository&lt;/a&gt;, this is a feature that belongs in a plugin: the existing collaboration plugin Multicollab has around 300 active installs, not 3 million. The vast majority of WordPress sites will never use it. Yet a major release is being delayed to re-architect the database layer for this, while the architectural work that &lt;em&gt;every&lt;/em&gt; site would benefit from continues to wait.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://make.wordpress.org/core/2026/03/31/extending-the-7-0-cycle/&quot;&gt;deferral post&lt;/a&gt; itself is remarkably candid about the cost. WordPress sites are &quot;generally very read-heavy,&quot; it says, but collaboration &quot;inherently involves a writing state.&quot; The HTTP polling approach is described as a &quot;lowest-common-denominator&quot; mechanism whose &quot;broad compatibility comes at the cost of relative inefficiency.&quot; Hosts need time to &quot;monitor requests to the sync endpoints&quot; and &quot;perform profiling.&quot; This is core telling hosts: we&apos;re shipping a feature that fundamentally changes how your servers behave, but don&apos;t worry, it&apos;s off by default. That&apos;s not a feature launch. That&apos;s a liability disclaimer.&lt;/p&gt;
&lt;p&gt;And it gets worse. The core team is now publishing guides on &lt;a href=&quot;https://make.wordpress.org/core/2026/04/01/building-a-custom-sync-provider-for-real-time-collaboration/&quot;&gt;building custom sync providers&lt;/a&gt; — requiring separate WebSocket servers and custom infrastructure — with WordPress VIP&apos;s implementation as the reference. This is enterprise hosting infrastructure being built into core because a handful of VIP clients need it. That&apos;s not an open source project serving its community. That&apos;s a product roadmap shaped by the needs of one company&apos;s hosting platform. Ironic, given that Matt&apos;s criticism of EmDash was that Cloudflare built it to sell their services.&lt;/p&gt;
&lt;p&gt;But zoom back out, because the prioritization problem and the technical problem are two sides of the same coin. The reason a single database table can delay a major release is that the data model was never designed to evolve. You can put a React admin on top of &lt;code&gt;wp_posts&lt;/code&gt; and &lt;code&gt;wp_postmeta&lt;/code&gt;. You can add a block editor. You can ship full-site editing. The underlying data model doesn&apos;t care. It&apos;s still the same architecture, just rendered differently.&lt;/p&gt;
&lt;h2&gt;The plugin trust problem&lt;/h2&gt;
&lt;p&gt;Matt argued that plugins having full access to WordPress is a feature, not a bug, and that sandboxing &quot;breaks down as soon as you look at what most WordPress plugins do.&quot;&lt;/p&gt;
&lt;p&gt;This is like arguing that because some mobile apps need camera access, every app should get root access to the phone. Yes, some plugins need broad capabilities. That&apos;s an argument for a granular permission system, not for continuing to give every plugin the keys to the entire database — and the entire filesystem, since a plugin runs with the same permissions as the PHP process. Every modern platform (iOS, Android, browser extensions, cloud functions) moved to scoped permissions years ago. WordPress&apos;s full-trust model isn&apos;t a philosophical commitment to openness. It&apos;s a security debt that compounds with every plugin install.&lt;/p&gt;
&lt;h2&gt;What the insiders see&lt;/h2&gt;
&lt;p&gt;Perhaps the most telling response came from Brian Coords, an Automattic employee. His observation: WordPress has had the PHP infrastructure for custom post types and fields since version 3.0 in 2010, but has never shipped a core UI for managing them. Sixteen years later, the community still depends on third-party plugins for something that should be a core capability.&lt;/p&gt;
&lt;p&gt;Brian framed this as a prioritization problem, not an engineering one. He&apos;s right, and that makes it worse. It means WordPress &lt;em&gt;could&lt;/em&gt; have built this and chose not to. There&apos;s a pattern here: architectural improvements get proposed, sometimes even prototyped, but they never get prioritized. The features that move forward are the ones with a direct path to revenue or a visible demo. Modernizing the boot process, adding an autoloader, fixing the data model — none of that makes for a good keynote slide.&lt;/p&gt;
&lt;p&gt;Brian also identified what he called &quot;decision fatigue&quot;: WordPress now offers so many layered approaches to the same problems (core blocks, patterns, block styles, theme.json, custom CSS, classic themes, block themes) that developers spend more time choosing &lt;em&gt;how&lt;/em&gt; to build something than actually building it. For AI agents, which can&apos;t navigate that kind of ambiguity the way experienced developers have learned to, this is a hard wall.&lt;/p&gt;
&lt;h2&gt;Refactor, don&apos;t redecorate&lt;/h2&gt;
&lt;p&gt;I didn&apos;t write about EmDash because I think WordPress should die. I spent fifteen years building Yoast SEO, and I chair &lt;a href=&quot;https://poststatus.com/&quot;&gt;Post Status&lt;/a&gt;, a foundation for the WordPress professional community. I care about this ecosystem. But caring about it means being honest about where it is.&lt;/p&gt;
&lt;p&gt;WordPress doesn&apos;t need to become EmDash. A ground-up rewrite of the platform that still powers more of the web than any other CMS would be reckless. But it does need real refactoring: targeted, meaningful architectural changes that address the structural problems rather than just painting over them.&lt;/p&gt;
&lt;p&gt;Four changes would transform WordPress without breaking it.&lt;/p&gt;
&lt;h3&gt;Fix the data model&lt;/h3&gt;
&lt;p&gt;Replace &lt;code&gt;wp_posts&lt;/code&gt; and &lt;code&gt;wp_postmeta&lt;/code&gt; with proper typed tables per content type, while preserving the existing APIs. This sounds radical, but it&apos;s been done before, in the WordPress ecosystem itself. When we launched &lt;a href=&quot;https://yoast.com/developer-blog/the-exciting-new-technology-that-is-indexables/&quot;&gt;Indexables in Yoast SEO&lt;/a&gt; in 2020, we replaced a sprawling mess of postmeta queries with a single, properly typed table. All public-facing APIs stayed the same. The data model underneath changed completely. It was hard. But it can be done, and it&apos;s easier at the core level, since core controls the schema. Automattic proved this again with WooCommerce&apos;s migration from its legacy data model to &lt;a href=&quot;https://developer.woocommerce.com/docs/features/high-performance-order-storage/&quot;&gt;High-Performance Order Storage&lt;/a&gt; — a tested, backwards-compatible approach to exactly this kind of schema change.&lt;/p&gt;
&lt;p&gt;The first step would be a real database abstraction layer: a &quot;WP-DBAL&quot; that lets &lt;code&gt;wpdb&lt;/code&gt; move to a saner paradigm with a compatibility layer underneath. Right now, most plugin authors still use &lt;a href=&quot;https://developer.wordpress.org/reference/functions/dbdelta/&quot;&gt;&lt;code&gt;dbDelta&lt;/code&gt;&lt;/a&gt; to execute raw SQL when they need custom tables. That&apos;s how thin the current abstraction is. Ari Stathopoulos, whose SQLite compatibility layer made WordPress Playground possible, has already proven that WordPress can run on a completely different database engine. A proper DBAL would take that same principle further, making the schema migration straightforward without breaking backwards compatibility.&lt;/p&gt;
&lt;h3&gt;Store content as structured data&lt;/h3&gt;
&lt;p&gt;Stop serializing blocks to HTML comments in &lt;code&gt;post_content&lt;/code&gt;. Store them as JSON instead, or at minimum alongside the HTML. The block editor already maintains a structured tree while editing; the problem is that it throws that structure away on save. Keeping it as JSON means APIs, headless frontends, and AI agents can work with WordPress content natively, without reverse-engineering structure out of markup. I opened a &lt;a href=&quot;https://core.trac.wordpress.org/ticket/45471&quot;&gt;Trac ticket&lt;/a&gt; seven years ago just to allow caching of &lt;code&gt;parse_blocks&lt;/code&gt; results — a band-aid for the performance cost of repeatedly re-parsing that serialized content. It&apos;s still open. This is the single biggest unlock for making content machine-readable, and it&apos;s what EmDash does with &lt;a href=&quot;https://github.com/portabletext/portabletext&quot;&gt;Portable Text&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Scope plugin permissions&lt;/h3&gt;
&lt;p&gt;Move from a full-trust plugin model to a permission-scoped execution layer. Not every plugin needs access to everything. A contact form plugin doesn&apos;t need to read the users table. A caching plugin doesn&apos;t need to write to the posts table. Every other modern platform figured this out years ago. WordPress can too.&lt;/p&gt;
&lt;p&gt;This is the biggest lift of the four. WordPress&apos;s entire plugin ecosystem was built on the assumption of full access, and retrofitting a permission model without breaking thousands of existing plugins is a genuinely hard problem. But hard isn&apos;t the same as impossible, and deferring it indefinitely isn&apos;t a strategy. It&apos;s just accumulating risk.&lt;/p&gt;
&lt;h3&gt;Modernize the PHP foundation&lt;/h3&gt;
&lt;p&gt;WordPress still doesn&apos;t have an autoloader. Every file is loaded via manual &lt;code&gt;require&lt;/code&gt; calls, a pattern that modern PHP left behind years ago. At Yoast, we built a &lt;a href=&quot;https://github.com/Yoast/wordpress-develop-mirror/tree/modernize&quot;&gt;fully working, backwards-compatible autoloader&lt;/a&gt; for WordPress core seven years ago. In 2024, Ari Stathopoulos proposed an &lt;a href=&quot;https://make.wordpress.org/core/2024/02/01/proposal-implement-a-php-autoloader-in-wordpress-core/&quot;&gt;even simpler approach&lt;/a&gt; in a formal core proposal. Neither made it into core. An autoloader is table stakes for any modern PHP project. It would unlock proper namespacing, reduce memory usage, and make the codebase navigable for both humans and AI agents. This is the lowest-risk, highest-leverage change on this list, and the fact that it hasn&apos;t happened yet says everything about WordPress&apos;s priorities.&lt;/p&gt;
&lt;p&gt;None of this is theoretical. The community has shown, repeatedly, that it can be done. The pattern is always the same: working code, proven approach, no action.&lt;/p&gt;
&lt;p&gt;These aren&apos;t small changes, but they&apos;re not a ground-up rewrite either. They&apos;re the refactoring work that WordPress has been deferring for more than a decade.&lt;/p&gt;
&lt;h2&gt;The stakes&lt;/h2&gt;
&lt;p&gt;The risk isn&apos;t that WordPress changes too much. The risk is that it keeps treating surface-level changes as if they were modernization. Gutenberg was genuine modernization when it was conceived, but after eight years it&apos;s still not finished, and the foundation underneath it never changed. The environment has changed. The way we build software has changed. WordPress&apos;s architecture needs to change with it, or the ecosystem that depends on it will look elsewhere. Many already are. I wrote recently about how &lt;a href=&quot;/do-you-need-a-cms/&quot;&gt;the CMS market itself may not be growing anymore&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;People don&apos;t want a CMS — they want a website, and the ways to get one have multiplied far beyond the traditional CMS category. The competitive pressure WordPress faces isn&apos;t just from other CMSes. It&apos;s from platforms that skip the concept entirely: Shopify, Substack, static Astro sites, AI-generated sites, and now EmDash. The market is fragmenting, and consumer expectations are shifting toward simpler, more opinionated tools.&lt;/p&gt;
&lt;p&gt;That competitive pressure makes the architectural stagnation doubly dangerous. WordPress isn&apos;t just falling behind technically while competitors in its own category innovate. It&apos;s falling behind while the category itself is shrinking. When a company the size of Cloudflare looks at the CMS landscape and decides to build a new one from scratch rather than build on top of WordPress, that&apos;s not a side project. That&apos;s a market signal.&lt;/p&gt;
&lt;p&gt;I called EmDash &lt;a href=&quot;/emdash-cms/&quot;&gt;the most interesting thing to happen to content management in years&lt;/a&gt;. Not because of the technology stack, but because it&apos;s designed for how we actually work now: AI agents building sites, structured content that machines can parse, deployment at the edge. WordPress is postponing a major release for weeks over a single database table. In that same timeframe, one developer at Cloudflare built an entire CMS from scratch — with structured content, scoped plugins, and edge deployment out of the box. That&apos;s the gap. WordPress could have owned that future. Instead, Cloudflare saw the opening that WordPress&apos;s architectural stagnation created, and walked right through it.&lt;/p&gt;
&lt;p&gt;But it&apos;s not too late. WordPress still has the largest install base, the biggest community, and decades of institutional knowledge. The talent is there. The ideas are there. The working code is there. What&apos;s missing is the willingness to prioritize the foundation over the facade. If WordPress starts making the right architectural decisions now, it can still catch up. But the window is closing.&lt;/p&gt;
</content:encoded><category>WordPress</category><category>Open Source</category><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>EmDash: a CMS built for 2026</title><link>https://joost.blog/emdash-cms/</link><guid isPermaLink="true">https://joost.blog/emdash-cms/</guid><description>Cloudflare just launched EmDash, a CMS designed for how we actually build in 2026: AI agents, structured content, edge deployment. Here&apos;s why it matters.</description><pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;import { Image } from &apos;astro:assets&apos;;
import seoPanelImage from &apos;./images/emdash-seo-panel.png&apos;;
import adminImage from &apos;./images/emdash-admin.png&apos;;&lt;/p&gt;
&lt;p&gt;The way we build software changed forever at the end of 2025. Suddenly our AI agents went from smart auto-complete to actually being able to develop software. We stopped writing boilerplate and started describing intent. But the tools we build &lt;em&gt;with&lt;/em&gt; haven&apos;t caught up, especially content management systems. Most CMSes were designed for a world where humans click through admin panels, plugins run with full trust, and deployment means uploading files to a shared host.&lt;/p&gt;
&lt;p&gt;That world is gone. And today &lt;a href=&quot;https://blog.cloudflare.com/emdash-wordpress/&quot;&gt;Cloudflare launched&lt;/a&gt; something built for the one we&apos;re actually in. And no, despite it being April 1st, it&apos;s not a joke.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://emdashcms.com&quot;&gt;EmDash&lt;/a&gt; is the most interesting thing to happen to content management in years. Not because it&apos;s built on Astro (though it is), but because it&apos;s the first CMS designed from the ground up for how we work in 2026: AI agents building sites, structured content that machines can parse and manipulate easily, and deployment at the edge.&lt;/p&gt;
&lt;h2&gt;What EmDash actually is&lt;/h2&gt;
&lt;p&gt;EmDash is a full CMS built on &lt;a href=&quot;https://astro.build&quot;&gt;Astro&lt;/a&gt;. End-to-end TypeScript. Deploys to Cloudflare Workers, Netlify, or Vercel. SQLite locally, Cloudflare D1 in production. Images on disk or R2/S3.&lt;/p&gt;
&lt;p&gt;But the tech stack isn&apos;t the point. The &lt;em&gt;design philosophy&lt;/em&gt; is.&lt;/p&gt;
&lt;p&gt;Every architectural decision in EmDash seems to have been made with the same question: &quot;What if an AI agent needs to do this?&quot; Content is stored as portable text — structured JSON, not HTML strings — which means an agent can read, modify, and generate content without parsing markup. Custom content types get their own database tables with typed fields, so an agent can reason about the schema programmatically. There&apos;s an MCP server for direct CMS interaction, a CLI that outputs JSON, and documentation specifically structured for AI consumption.&lt;/p&gt;
&lt;p&gt;Editing happens on the frontend. You see your actual site while you edit it, not a disconnected admin panel (though you can edit there too). Internationalization is built in from day one. So is redirect management, full-text search, and core SEO functionality.&lt;/p&gt;
&lt;p&gt;And it looks a lot like WordPress, which will make people moving over feel right at home. There&apos;s even a &lt;a href=&quot;https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/themes/porting-wp-themes.mdx&quot;&gt;complete guide for porting WordPress themes&lt;/a&gt;, with mapping tables from WordPress template tags to EmDash API calls that are practically designed to be fed to an AI agent.&lt;/p&gt;
&lt;p&gt;&amp;lt;Image src={adminImage} alt=&quot;EmDash&apos;s post editor showing a familiar WordPress-like interface with title, featured image, rich text editor, categories, and publish controls&quot; class=&quot;rounded-lg border border-slate-200 dark:border-slate-700 shadow-card my-6 w-full cursor-zoom-in&quot; data-zoomable /&amp;gt;&lt;/p&gt;
&lt;h2&gt;The plugin security model is the real story&lt;/h2&gt;
&lt;p&gt;Any CMS lives or dies by its plugin ecosystem. The challenge has always been balancing extensibility with security: the more a plugin can do, the more damage a bad one can cause.&lt;/p&gt;
&lt;p&gt;EmDash takes a fundamentally different approach. Each plugin runs in an isolated worker environment with granular permissions. A plugin has to explicitly request access to content, network calls, or specific APIs. The UI layer is defined through a JSON schema similar to Slack&apos;s Block Kit, which means plugins can&apos;t inject arbitrary HTML or JavaScript into the admin.&lt;/p&gt;
&lt;p&gt;There&apos;s a plugin marketplace too, though currently API-only. Submissions go through automated security scanning powered by Workers AI, with Llama Guards handling content classification. It&apos;s not perfect — automated scanning never catches everything — but it&apos;s a meaningful layer of protection built into the platform from the start. And if you&apos;re a WordPress plugin developer, there&apos;s a &lt;a href=&quot;https://github.com/emdash-cms/emdash/blob/main/docs/src/content/docs/migration/porting-plugins.mdx&quot;&gt;guide for porting plugins&lt;/a&gt; that maps WordPress hooks and APIs to their EmDash equivalents.&lt;/p&gt;
&lt;h2&gt;What I think is genuinely strong&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;It&apos;s agent-native, not agent-compatible.&lt;/strong&gt;&amp;lt;br /&amp;gt;
Most CMSes are bolting on AI features — a chatbot here, a content generator there. EmDash is different: the entire architecture assumes AI agents are first-class users. The MCP server means Claude, Cursor, or any agent can create content types, manage entries, configure plugins, and deploy — all programmatically. The CLI outputs JSON. The documentation is structured for machine consumption. Round-trip markdown support means you can export content, edit it in any tool (or let an agent edit it), and import it back without loss. This isn&apos;t an AI feature. It&apos;s an AI-native CMS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The AI site generation shows where this is heading.&lt;/strong&gt;&amp;lt;br /&amp;gt;
EmDash includes a playground that spins up full sites from a prompt: theme, content structure, sample data, all generated by models running on Workers AI. Today it&apos;s a demo tool. But it shows the trajectory: describe what you want, get a working site. That&apos;s not a gimmick when the entire CMS is structured data an agent can manipulate.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authentication uses passkeys by default.&lt;/strong&gt;&amp;lt;br /&amp;gt;
No passwords to leak, no brute-force attacks to defend against. Combined with pluggable SSO and built-in role-based access control, the security posture is strong out of the box.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;not-prose grid sm:grid-cols-[1fr_280px] gap-6 items-start my-8&quot;&amp;gt;
&amp;lt;div class=&quot;prose prose-slate sm:prose-lg max-w-none&quot;&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The SEO foundation is solid.&lt;/strong&gt;&amp;lt;br /&amp;gt;
Built-in SEO controls, metadata hooks for plugins, structured content that search engines can parse cleanly. It&apos;s not a full SEO suite, but the architecture makes it straightforward to build one.&lt;/p&gt;
&lt;p&gt;&amp;lt;/div&amp;gt;
&amp;lt;Image src={seoPanelImage} alt=&quot;EmDash&apos;s built-in SEO panel showing fields for SEO title, meta description, canonical URL, and a noindex toggle&quot; class=&quot;rounded-lg border border-slate-200 dark:border-slate-700 shadow-card w-full cursor-zoom-in&quot; data-zoomable /&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;What&apos;s still unproven&lt;/h2&gt;
&lt;p&gt;Let&apos;s be honest about the challenges.&lt;/p&gt;
&lt;p&gt;EmDash was built over two months using AI coding agents. That&apos;s remarkable speed, and it shows what&apos;s possible when you build on a modern foundation like Astro without decades of legacy to navigate. But a CMS is a long-term commitment, and two months of AI-assisted development is not the same as two months of battle-testing in production. The real test starts now.&lt;/p&gt;
&lt;p&gt;The plugin ecosystem is empty. WordPress has over 60,000 plugins. EmDash has a marketplace architecture but no community yet. History shows that CMS adoption is driven more by available plugins and themes than by technical merit. Ghost, Craft, Statamic; all technically excellent, none have built the ecosystem needed for broad adoption. EmDash will need to solve that same chicken-and-egg problem.&lt;/p&gt;
&lt;p&gt;The open-source story needs clarity. It&apos;s MIT-licensed, which is great. But it&apos;s still a Cloudflare project, and developers will want to see long-term governance commitments before investing their time.&lt;/p&gt;
&lt;p&gt;Deployment is flexible: one click deploys to Cloudflare Workers, Netlify, or Vercel, and it&apos;s supported on any modern hosting platform. That said, the Cloudflare integration is the most polished (D1 for the database, R2 for storage), and it&apos;ll be worth testing the other hosts to see how well the multi-platform story holds up in practice.&lt;/p&gt;
&lt;h2&gt;Why I&apos;m going to build on it&lt;/h2&gt;
&lt;p&gt;Every CMS generation reflects the tools used to build with it. WordPress was shaped by FTP, PHP, and shared hosting — and it shows. Squarespace by drag-and-drop visual builders. The next generation will be shaped by AI agents, and the CMS that wins will be the one those agents can work with most effectively.&lt;/p&gt;
&lt;p&gt;That&apos;s what EmDash gets right. Structured content an agent can read and write natively. A typed schema it can introspect. An MCP server it can talk to directly. A plugin system with clear boundaries it can develop within safely. This isn&apos;t a CMS with AI features added on top. It&apos;s a CMS where AI is a first-class builder.&lt;/p&gt;
&lt;p&gt;When I &lt;a href=&quot;/do-you-need-a-cms/&quot;&gt;moved this blog to Astro&lt;/a&gt;, I thought I&apos;d left CMSes behind. Then I spent an evening building an EmDash theme for this site and started migrating content — because a CMS that an AI agent can build on this naturally is hard to resist. But the bigger picture is agencies building client sites, creators who need multi-user workflows, anyone building more than a static site. For them, a CMS isn&apos;t optional. The question is what a good one looks like when your primary development partner is an AI agent.&lt;/p&gt;
&lt;p&gt;EmDash is my answer. It&apos;s built on Astro, deploys to Cloudflare, and adds the CMS layer with the kind of architecture I&apos;d design if starting from scratch: sandboxed plugins, structured content, TypeScript throughout, edge deployment. But what seals it is that I can point Claude at it and say &quot;build me a theme&quot;, and the structured documentation, typed APIs, and MCP server mean it actually can.&lt;/p&gt;
&lt;p&gt;I&apos;m planning to develop on and with EmDash, and I&apos;ll share more about what I&apos;m building as it takes shape. For now, go look at &lt;a href=&quot;https://emdashcms.com&quot;&gt;EmDash&lt;/a&gt; and form your own opinion. The launch is today, and regardless of where it ends up, it&apos;s asking the right question: what should a CMS look like when AI agents are doing most of the building?&lt;/p&gt;
&lt;p&gt;&amp;lt;dialog id=&quot;img-zoom-modal&quot; style=&quot;padding:0;border:none;background:transparent;max-width:90vw;max-height:90vh;margin:auto;outline:none;&quot;&amp;gt;
&amp;lt;img id=&quot;img-zoom-target&quot; style=&quot;max-width:100%;max-height:90vh;border-radius:8px;display:block;&quot; alt=&quot;&quot; /&amp;gt;
&amp;lt;/dialog&amp;gt;
&amp;lt;style&amp;gt;{&lt;code&gt;#img-zoom-modal::backdrop { background: rgba(0,0,0,0.7); backdrop-filter: blur(4px); }&lt;/code&gt;}&amp;lt;/style&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;script&amp;gt;{&lt;code&gt;(function() {   const modal = document.getElementById(&apos;img-zoom-modal&apos;);   const target = document.getElementById(&apos;img-zoom-target&apos;);   document.querySelectorAll(&apos;[data-zoomable]&apos;).forEach(img =&amp;gt; {     img.addEventListener(&apos;click&apos;, () =&amp;gt; {       target.src = img.src;       target.alt = img.alt;       modal.showModal();     });   });   modal.addEventListener(&apos;click&apos;, () =&amp;gt; modal.close()); })();&lt;/code&gt;}&amp;lt;/script&amp;gt;&lt;/p&gt;
</content:encoded><category>AI</category><category>Development</category><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Ask Joost: AI-powered answers from my blog</title><link>https://joost.blog/ai-powered-answers-ask-joost/</link><guid isPermaLink="true">https://joost.blog/ai-powered-answers-ask-joost/</guid><description>I built an AI-powered Q&amp;A feature that lets you ask questions about anything I&apos;ve written. Here&apos;s how it works, and why I built it on Cloudflare Workers AI.</description><pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have written quite a bit. Over the years, this blog has accumulated posts about WordPress, SEO, open source governance, CMS market share, and plenty more. Finding the right post for a specific question means searching, skimming, and hoping the title matches what you&apos;re looking for. That&apos;s not great.&lt;/p&gt;
&lt;p&gt;So I built &lt;a href=&quot;/ask-joost/&quot;&gt;Ask Joost&lt;/a&gt;: a page where you can ask a natural language question and get a direct answer, sourced from my blog posts.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Want this on your own site? I&apos;ve &lt;a href=&quot;https://github.com/jdevalk/ask-endpoint&quot;&gt;open sourced it on GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;The system has three layers: a build-time index, hybrid retrieval, and an LLM that generates grounded answers.&lt;/p&gt;
&lt;h3&gt;1. A search index built at deploy time&lt;/h3&gt;
&lt;p&gt;Every time the site builds, a script scans my content directories for blog posts, pages, and videos. It extracts titles, descriptions, categories, publication dates, and full text, then generates a searchable index that ships with the site as a static JavaScript module.&lt;/p&gt;
&lt;p&gt;That index also includes embeddings, so there’s no separate database or vector store to maintain. And importantly, it skips &lt;code&gt;draft: true&lt;/code&gt; content, so unpublished posts never make it into the public Ask index.&lt;/p&gt;
&lt;p&gt;For video content, the index can also pull in generated transcripts. That makes videos searchable too, though they’re treated as weaker sources than written posts and pages.&lt;/p&gt;
&lt;h3&gt;2. Hybrid retrieval: keywords + embeddings + a bit of query rewriting&lt;/h3&gt;
&lt;p&gt;When you ask a question, it hits the &lt;code&gt;/ask&lt;/code&gt; endpoint — a Cloudflare Pages Function. Retrieval uses a blend of keyword search and semantic search.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Keyword scoring&lt;/strong&gt; tokenizes your query and scores documents based on term frequency, with extra weight for matches in titles, descriptions, keywords, and URLs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Semantic search&lt;/strong&gt; uses Cloudflare Workers AI’s &lt;code&gt;bge-base-en-v1.5&lt;/code&gt; embedding model. At build time, each indexed document gets embedded. At query time, the question gets embedded too, and cosine similarity finds content that’s conceptually related even when the exact words differ.&lt;/p&gt;
&lt;p&gt;On top of that, the retrieval layer does a little bit of query normalization. It expands common aliases and shorthand, things like &lt;code&gt;wp&lt;/code&gt; to &lt;code&gt;WordPress&lt;/code&gt;, or &lt;code&gt;gutenberg&lt;/code&gt; to &lt;code&gt;gutenberg block editor&lt;/code&gt; and &lt;code&gt;block editor&lt;/code&gt; to the same, so short or informal queries still find the right content.&lt;/p&gt;
&lt;p&gt;For follow-up questions, it also augments vague queries with the previous turn. So if you ask “what about governance?” after a WordPress question, retrieval gets a bit more context before the model ever sees anything.&lt;/p&gt;
&lt;p&gt;Each indexed document also has a search weight. Pages get a slight boost, videos get a slight demotion, and blog posts sit in the middle. That helps the system prefer cleaner, more authoritative written sources without making videos undiscoverable.&lt;/p&gt;
&lt;p&gt;Individual posts can also override their default weight by setting &lt;code&gt;searchWeight&lt;/code&gt; in their frontmatter. A value above &lt;code&gt;1.0&lt;/code&gt; promotes a page in results; a value below &lt;code&gt;1.0&lt;/code&gt; demotes it. This is useful when you know a particular post is the canonical answer on a topic, or when an older post has been superseded and shouldn&apos;t rank as highly anymore.&lt;/p&gt;
&lt;h3&gt;3. An LLM that generates the answer&lt;/h3&gt;
&lt;p&gt;When the Ask page calls the endpoint, it uses streaming generation by default. The top search results are packaged up as context and sent to Llama 3.3 70B running on &lt;a href=&quot;https://developers.cloudflare.com/workers-ai/&quot;&gt;Cloudflare Workers AI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The prompt keeps the model on a short leash:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;answer &lt;strong&gt;only&lt;/strong&gt; from the provided context;&lt;/li&gt;
&lt;li&gt;say so when the site doesn’t contain enough information;&lt;/li&gt;
&lt;li&gt;always cite sources with markdown links;&lt;/li&gt;
&lt;li&gt;prefer newer posts when my thinking has changed over time;&lt;/li&gt;
&lt;li&gt;treat vague follow-up questions as part of the ongoing conversation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each source passed to the model includes its content type and publication date. That gives the model enough context to distinguish between “this is what Joost thought in 2021” and “this is probably his current view.”&lt;/p&gt;
&lt;p&gt;If the model fails, times out, or returns something unusable, the endpoint falls back to a deterministic summary built from the search results. So the system degrades gracefully instead of just erroring out.&lt;/p&gt;
&lt;h2&gt;Better sources, better answers&lt;/h2&gt;
&lt;p&gt;One thing I cared about a lot was source quality. Not all content is equally good source material. Pages like “&lt;a href=&quot;/about-me/&quot;&gt;About&lt;/a&gt;” are often more authoritative than an old blog post. Video transcripts are useful for retrieval, but they’re rougher as quoted sources because they’re derived from captions. So the system uses them for discovery, while still preferring cleaner written sources in the final answer when possible.&lt;/p&gt;
&lt;p&gt;The source list shown below each answer is also filtered down to the posts the model actually referenced. The function parses the markdown links in the answer and only shows those sources, instead of dumping every item that happened to be in the retrieval context.&lt;/p&gt;
&lt;p&gt;Each visible source can also include a content-type label and publication date, which makes it easier to see whether you’re looking at a page, a blog post, or a video, and whether the source is current or old.&lt;/p&gt;
&lt;h2&gt;Streaming and conversation support&lt;/h2&gt;
&lt;p&gt;Answers stream in as they’re generated, so you don’t just sit there waiting for the full response to appear all at once. That improves perceived speed a lot, especially for longer answers.&lt;/p&gt;
&lt;p&gt;The page also supports follow-up questions within the same conversation. It keeps a short exchange history client-side, sends it along with new requests, and uses a stable session ID so Cloudflare can keep the conversation pinned to the same model instance.&lt;/p&gt;
&lt;p&gt;That last part matters because Workers AI supports &lt;a href=&quot;https://developers.cloudflare.com/workers-ai/features/prompt-caching/&quot;&gt;prompt caching&lt;/a&gt; via session affinity. In practice, that means the system prompt and recent conversation history can stay hot in memory, making follow-ups faster and cheaper.&lt;/p&gt;
&lt;h2&gt;Why this architecture&lt;/h2&gt;
&lt;p&gt;I wanted something that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Has no ongoing cost at rest.&lt;/strong&gt; The search index is static. The function only runs when someone asks a question.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Doesn’t require a vector database.&lt;/strong&gt; For a site of this size, embeddings can just live in the generated index.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Degrades gracefully.&lt;/strong&gt; If the AI layer fails, there’s still a deterministic fallback.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stays inspectable.&lt;/strong&gt; The retrieval logic is straightforward enough to debug, tune, and reason about.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Is NLWeb-compatible.&lt;/strong&gt; The &lt;code&gt;/ask&lt;/code&gt; endpoint follows the &lt;a href=&quot;https://github.com/nlweb-ai/NLWeb&quot;&gt;NLWeb protocol&lt;/a&gt;, so agents and tools that speak NLWeb can query the site directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is one of those cases where “boring architecture” is a feature. There’s no orchestration layer, no separate retrieval service, and no complex storage stack — just a static site, a generated index, a function, and Workers AI.&lt;/p&gt;
&lt;h2&gt;The tech stack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Astro&lt;/strong&gt; for the static site&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Pages Functions&lt;/strong&gt; for the &lt;code&gt;/ask&lt;/code&gt; endpoint&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Workers AI&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@cf/baai/bge-base-en-v1.5&lt;/code&gt; for embeddings&lt;/li&gt;
&lt;li&gt;&lt;code&gt;@cf/meta/llama-3.3-70b-instruct-fp8-fast&lt;/code&gt; for answer generation&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A build-time indexing script&lt;/strong&gt; that generates the searchable index, embeddings, metadata, and transcript-backed content from markdown&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The code is pretty small. The endpoint logic is now split into focused modules for config, retrieval, and generation, but it’s still a lightweight setup rather than a framework-heavy one.&lt;/p&gt;
&lt;h2&gt;Observability and tuning&lt;/h2&gt;
&lt;p&gt;As soon as you build something like this, you start wanting ways to inspect what it’s doing.&lt;/p&gt;
&lt;p&gt;So the Ask endpoint also has a debug mode for inspecting retrieval results, score breakdowns, and timing. That makes it easier to tune ranking, understand why a query matched a certain document, and test improvements without guessing.&lt;/p&gt;
&lt;p&gt;That’s been useful while tuning things like alias expansion, follow-up handling, source extraction, and weighting between pages, posts, and videos.&lt;/p&gt;
&lt;h2&gt;Use it on your own site&lt;/h2&gt;
&lt;p&gt;I’ve extracted the core into an open source package: &lt;a href=&quot;https://github.com/jdevalk/ask-endpoint&quot;&gt;ask-endpoint&lt;/a&gt;. It gives you a drop-in NLWeb-compatible &lt;code&gt;/ask&lt;/code&gt; endpoint for markdown-based sites on Cloudflare Pages.&lt;/p&gt;
&lt;p&gt;The setup is straightforward: point it at your content, generate the index during build, add the function, and connect a Workers AI binding. The README has the full walkthrough.&lt;/p&gt;
&lt;h2&gt;Try it&lt;/h2&gt;
&lt;p&gt;Head to &lt;a href=&quot;/ask-joost/&quot;&gt;/ask-joost/&lt;/a&gt; and ask something. Try “do you think I need a CMS?”, “what happened with WordPress governance?”, or “how do you think AI affects SEO?” and see what comes back.&lt;/p&gt;
&lt;p&gt;The answers still aren’t perfect. They’re constrained by what I’ve written, and the model can still miss nuance. But for a relatively small, low-cost system layered on top of a static site, it’s remarkably useful, and a lot more usable than making people dig through archives by hand.&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Do you need a CMS?</title><link>https://joost.blog/do-you-need-a-cms/</link><guid isPermaLink="true">https://joost.blog/do-you-need-a-cms/</guid><description>For twenty years, wanting a website meant needing a CMS. That&apos;s no longer true. The real question people ask now is simpler: how do I get my content on the web?</description><pubDate>Sat, 21 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For twenty years, &quot;I want a website&quot; meant &quot;I need a CMS.&quot; WordPress, Joomla, Drupal: the conversation was always about &lt;em&gt;which&lt;/em&gt; one. That framing is outdated. People never wanted a CMS. They want a website.&lt;/p&gt;
&lt;h2&gt;The real competitive landscape&lt;/h2&gt;
&lt;p&gt;The ways to get content on the web have multiplied. Shopify if you&apos;re selling something. Squarespace or Wix if you want something that looks good without touching code. Substack if you just want to write. A static site generator if you want performance and control.&lt;/p&gt;
&lt;p&gt;CMS market share conversations obsess over WordPress vs. Drupal vs. Joomla. I know, because &lt;a href=&quot;/cms-market-share/&quot;&gt;I&apos;ve been tracking those numbers&lt;/a&gt; for years. But looking at those numbers, the real movement isn&apos;t between CMSes. It&apos;s &lt;em&gt;away&lt;/em&gt; from CMSes entirely. The competitors that are actually growing are the ones that aren&apos;t traditional CMSes at all. I &lt;a href=&quot;/wordpress-market-share-shrinking/&quot;&gt;wrote about this in 2022&lt;/a&gt; when WordPress&apos; market share first started declining. The winners then were Wix and Squarespace. That trend has only accelerated.&lt;/p&gt;
&lt;h2&gt;My own answer: I don&apos;t need one&lt;/h2&gt;
&lt;p&gt;I ran joost.blog on WordPress for years. I built Yoast SEO. I&apos;m probably the last person you&apos;d expect to leave WordPress behind (even if only for a few sites). But when I relaunched this site recently, I moved to &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt; on Cloudflare Pages.&lt;/p&gt;
&lt;p&gt;The site is now a collection of Markdown files that get built into static HTML and deployed to a global CDN. No database. No server. No PHP. I still have everything that matters: full-text search, comments, structured data, RSS, auto-generated OG images. What I dropped was the overhead.&lt;/p&gt;
&lt;h3&gt;But what about SEO?&lt;/h3&gt;
&lt;p&gt;I built Yoast SEO, so you&apos;d think this is where a static site falls short. It doesn&apos;t. Everything Yoast SEO does on WordPress, I can do in Astro. XML sitemaps, meta tags, canonical URLs, Open Graph tags, structured data with full JSON-LD schema graphs, auto-generated social share images: it&apos;s all there. In fact, it&apos;s &lt;em&gt;easier&lt;/em&gt; to get right on a static site because you control the entire HTML output. There&apos;s no theme or plugin conflict messing with your head tags. No render-blocking resources injected by something you forgot you installed. What you build is what gets served.&lt;/p&gt;
&lt;p&gt;The SEO features that a CMS plugin provides aren&apos;t magic. They&apos;re HTML output. And any modern static site generator can produce that same HTML, often cleaner.&lt;/p&gt;
&lt;h2&gt;What I gained&lt;/h2&gt;
&lt;p&gt;The result aligns with something I &lt;a href=&quot;/build-websites-like-2005/&quot;&gt;wrote about last year&lt;/a&gt;: the web works best when you keep it simple. Clean HTML that every crawler can read, including AI systems that don&apos;t execute JavaScript at all. Near-zero security surface. No updates to manage. No plugins to keep compatible. Hosting costs that round to zero. Page load times that are hard to beat with any dynamic setup.&lt;/p&gt;
&lt;p&gt;This isn&apos;t about nostalgia for a simpler web. It&apos;s about recognizing that for a site like mine, a blog with some static pages, the complexity of a CMS was solving problems I didn&apos;t have while creating problems I did.&lt;/p&gt;
&lt;h2&gt;Where you genuinely need a CMS&lt;/h2&gt;
&lt;p&gt;Let me be clear: there are real use cases where a CMS earns its complexity. If you have a team of editors who need to collaborate on content daily, with roles, approval chains, and scheduling. If your content model is highly dynamic and relational. If you&apos;re running e-commerce, membership, or LMS functionality where the site &lt;em&gt;is&lt;/em&gt; the application. If your content needs to change per visitor through personalization.&lt;/p&gt;
&lt;p&gt;I&apos;m building &lt;a href=&quot;https://rondo.club/&quot;&gt;Rondo&lt;/a&gt; on WordPress for exactly this reason. WordPress works well as an &lt;a href=&quot;/wordpress-architecture-base-ai/&quot;&gt;application framework&lt;/a&gt;, and Rondo needs the kind of dynamic, user-facing functionality that a static site simply can&apos;t provide. These aren&apos;t edge cases. They represent a lot of websites. But they don&apos;t represent &lt;em&gt;most&lt;/em&gt; websites. Most websites are a handful of pages and maybe a blog.&lt;/p&gt;
&lt;h2&gt;The editing question&lt;/h2&gt;
&lt;p&gt;The last real argument for a CMS on simpler sites is the editing experience. &quot;My client can&apos;t edit Markdown files and commit them to a git repository.&quot; That&apos;s a fair point. Today.&lt;/p&gt;
&lt;p&gt;But think about what&apos;s already changing. If you can tell a chatbot &quot;update the opening hours on my contact page&quot; or &quot;publish a post about our spring menu&quot; and it edits the file, commits, and deploys, what&apos;s the admin panel for? I built this entire Astro site with AI assistance. The next step, editing content through conversation, is not a big leap. It&apos;s a small one.&lt;/p&gt;
&lt;p&gt;The CMS&apos;s visual editing interface was a solution to a human limitation: most people can&apos;t (or don&apos;t want to) write code to update their website. AI is removing that limitation. Not in some theoretical future, but now. When editing a static site becomes as easy as sending a message, the CMS&apos;s core advantage for the majority of websites disappears.&lt;/p&gt;
&lt;h2&gt;So what&apos;s a CMS actually for?&lt;/h2&gt;
&lt;p&gt;A CMS still has a real future in genuinely complex scenarios: multi-user collaboration, dynamic content, application-level functionality. But for the millions of sites that are essentially &quot;some pages and maybe a blog&quot;? The answer to &quot;do I need a CMS?&quot; is increasingly: no. You need a website. And there are more ways than ever to have one.&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>AI optimization is replaying early SEO, just faster</title><link>https://joost.blog/ai-optimization-is-replaying-early-seo-just-faster/</link><guid isPermaLink="true">https://joost.blog/ai-optimization-is-replaying-early-seo-just-faster/</guid><description>If you were around for early SEO, the current AI-content wave should feel familiar. The difference is speed: what used to take years now takes weeks.</description><pubDate>Wed, 11 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;If you were around for early SEO, the current AI-content wave should feel familiar. The difference is speed: what used to take years now takes weeks.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;The first era of SEO was not subtle. Keyword stuffing, link farms, doorway pages, directories: entire businesses existed to game ranking systems rather than serve users. I&apos;ve lived that age and it wasn&apos;t pretty. Yeah, sure, for a while it worked. Then search engines evolved, penalties got harsher, and the arms race shifted toward quality signals.&lt;/p&gt;
&lt;p&gt;We&apos;re replaying that cycle with AI content. Same incentives, same tactics, same outcome. The only real change is the feedback loop: it&apos;s shorter, and far more volatile.&lt;/p&gt;
&lt;h2&gt;The early-SEO playbook we&apos;re repeating&lt;/h2&gt;
&lt;p&gt;If you strip away the tooling, the pattern is simple: when distribution is cheap and ranking signals are gameable, people will try to scale exploitation before they scale usefulness.&lt;/p&gt;
&lt;p&gt;Early SEO looked like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Scale over substance.&lt;/strong&gt; Thousands of pages built to match query patterns, not solve problems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signal manipulation.&lt;/strong&gt; Keywords, anchor text, link schemes, directory listings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thin content.&lt;/strong&gt; Minimum effort to get indexed; maximum volume to get clicks.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most important lesson from that era wasn&apos;t &quot;SEO is bad.&quot; It was that any system measured by ranking will be gamed until quality signals improve.&lt;/p&gt;
&lt;h2&gt;The AI-era equivalents&lt;/h2&gt;
&lt;p&gt;Now we have a new way to manufacture content, and the incentives are even stronger:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Prompt-spun farms.&lt;/strong&gt; Entire sites generated at near-zero cost, optimized for long-tail queries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Programmatic SEO on steroids.&lt;/strong&gt; Instead of templated pages for &quot;best X in Y,&quot; we now generate original-ish text for each variation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Agentic automation.&lt;/strong&gt; Teams running content factories like software pipelines: ideate → generate → publish → iterate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&quot;AI SEO&quot; as the new link building.&lt;/strong&gt; Everyone is trying to reverse-engineer how large models cite sources and surface results.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tactics changed. The intent didn&apos;t. It&apos;s still the same old game: grabbing a quick win from a distribution loophole before the algorithms get smart enough to close it.&lt;/p&gt;
&lt;h2&gt;Why it&apos;s faster this time&lt;/h2&gt;
&lt;p&gt;In early SEO, the production bottleneck was human labor. That kept the cycle slow. In AI, the bottleneck is no longer writing; it&apos;s &lt;em&gt;verification&lt;/em&gt; and &lt;em&gt;trust.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;That changes the tempo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Generation cost is near zero.&lt;/strong&gt; You can spin a thousand pages before lunch, heck, before you&apos;ve had your first coffee.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Iteration is instant.&lt;/strong&gt; Change a prompt, re-run the system, ship again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Platforms can detect faster, but spammers can adapt faster too.&lt;/strong&gt; Every &quot;penalty&quot; is a new dataset to train against.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feedback loops collapse quality.&lt;/strong&gt; Models are trained on outputs, which means mediocrity becomes input. That creates a race to the bottom unless quality signals are explicit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When content can be mass-produced and mass-reproduced, the only durable advantage is &lt;em&gt;proven&lt;/em&gt; value.&lt;/p&gt;
&lt;h2&gt;What platforms will do (and already are doing)&lt;/h2&gt;
&lt;p&gt;Search engines and LLM platforms aren&apos;t blind to this. We&apos;re already seeing the direction of travel:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Stronger quality signals.&lt;/strong&gt; Depth, originality, and usefulness are weighted higher than keyword coverage. See Google&apos;s recent &lt;a href=&quot;https://developers.google.com/search/blog/2026/02/discover-core-update&quot;&gt;Discover update&lt;/a&gt;, or this &lt;a href=&quot;https://www.youtube.com/watch?v=8hWV3DVSRf0&quot;&gt;video from Glenn&lt;/a&gt; about the December update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Provenance and identity.&lt;/strong&gt; Author credibility, entity consistency, and first-party data matter more. Note how &lt;a href=&quot;https://developers.google.com/search/docs/fundamentals/creating-helpful-content&quot;&gt;this Google documentation&lt;/a&gt; was updated in in Feb 2026, specifically targeting bylines and author reputation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Downranking boilerplate.&lt;/strong&gt; Templates, paraphrases, and thin content are being actively filtered.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the same arc as early SEO, just in fast-forward. The platform&apos;s long-term incentive is to improve the user outcome, even if it means burning a lot of low-value content along the way.&lt;/p&gt;
&lt;h2&gt;What actually works for builders &amp;amp; publishers&lt;/h2&gt;
&lt;p&gt;If the content arms race is speeding up, the only sustainable strategy is to produce things the algorithm &lt;em&gt;wants&lt;/em&gt; to trust.&lt;/p&gt;
&lt;p&gt;That means content that is &lt;em&gt;earned&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Original data.&lt;/strong&gt; Benchmarks, experiments, research, or unique datasets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Real-world experience.&lt;/strong&gt; Advice that only exists because you&apos;ve done the work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Insight synthesis.&lt;/strong&gt; Connecting ideas in a way that feels new, not rephrased.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And it means building distribution you control:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Email, communities, product surfaces, and direct relationships.&lt;/li&gt;
&lt;li&gt;A recognizable voice that makes people search &lt;em&gt;for you&lt;/em&gt;, not just your topic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;SEO still matters, but it becomes the &lt;em&gt;output&lt;/em&gt; of being the best source, not the goal itself. Note that I&apos;m not saying you can&apos;t use AI for this, of course you can, and you should.&lt;/p&gt;
&lt;h3&gt;A note on Technical SEO&lt;/h3&gt;
&lt;p&gt;This, by all means, does not mean we shouldn&apos;t be playing with new technical optimizations, figuring out what works with all the new crawlers going around. We shouldn&apos;t draw conclusions on them too quickly and we should give the platforms clear feedback on how we think this &lt;em&gt;could&lt;/em&gt; all work better. SEOs have always played a role in bettering (&lt;em&gt;and&lt;/em&gt; worsening) the web, we should keep playing that role.&lt;/p&gt;
&lt;h2&gt;A practical playbook&lt;/h2&gt;
&lt;p&gt;If you&apos;re publishing in this environment, here&apos;s what will keep working even as platforms tighten the screws:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create proof-of-work content.&lt;/strong&gt; Ship &lt;em&gt;real&lt;/em&gt; case studies, benchmarks, real results, public experiments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Go deep, not wide.&lt;/strong&gt; Fewer, higher-quality pieces that build topical authority.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build distribution you own.&lt;/strong&gt; A reader base is more defensible than a ranking.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tie content to product value.&lt;/strong&gt; If your writing is inseparable from what you build, it&apos;s harder to commoditize.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Embrace editorial scarcity.&lt;/strong&gt; Don&apos;t ship more just because you can; ship better because you must.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The real lesson&lt;/h2&gt;
&lt;p&gt;The lesson from early SEO wasn&apos;t &quot;never optimize.&quot; It was &quot;optimize for users, because the platform will eventually force you to.&quot;&lt;/p&gt;
&lt;p&gt;AI simply accelerates that reckoning. The penalty for low-value content arrives faster, and the reward for real value compounds sooner.&lt;/p&gt;
&lt;p&gt;If you&apos;re building content strategy in 2026, the winning move is the same as it was in 2006, just faster:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Make something that deserves to be ranked.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>FAIR, WordPress, and Knowing When to Stop</title><link>https://joost.blog/fair-wordpress-and-knowing-when-to-stop/</link><guid isPermaLink="true">https://joost.blog/fair-wordpress-and-knowing-when-to-stop/</guid><description>Over the past year, we, Karim &amp; Joost, have written and talked extensively about the challenges facing WordPress. We’ve been critical. At times, very critical.</description><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Over the past year, we, Karim &amp;amp; Joost, have written and talked extensively about the challenges facing WordPress. We &lt;a href=&quot;/path-forward-for-wordpress/&quot;&gt;proposed a path forward&lt;/a&gt;, &lt;a href=&quot;/wordpress-roadmap/&quot;&gt;laid out what should be on the roadmap&lt;/a&gt;, and &lt;a href=&quot;https://marucchi.com/wordpress-leadership-continued/&quot;&gt;been critical&lt;/a&gt;. At times, &lt;a href=&quot;/wordpress-leadership/&quot;&gt;very critical&lt;/a&gt;. Of leadership, of governance, of the way decisions are made and enforced. We stand by that criticism.&lt;/p&gt;
&lt;p&gt;Matt’s way of operating has, in our view, been harmful. It has led to a fractured community. It has created distrust. It has made contributors feel unsafe or unheard. That is not good for WordPress. It is not good for open source. And it is not something we should normalize.&lt;/p&gt;
&lt;p&gt;But something else has become clear to us over the past months.&lt;/p&gt;
&lt;p&gt;Even when you believe change is necessary, even when you build a credible alternative, you still need a broader ecosystem willing to step up.&lt;/p&gt;
&lt;p&gt;And that’s where FAIR stops as far as WordPress goes.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;Why FAIR exists&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;FAIR was never about “taking over” WordPress. It was never about forking, nor did it exist for the sake of ego or power. It was a response to a structural problem.&lt;/p&gt;
&lt;p&gt;The WordPress ecosystem has grown into a massive economic engine. Hosting companies, agencies, plugin developers, SaaS providers, thousands of businesses rely on it. Those of us who believe in it think of it more as an open web operating system than a simple CMS. Yet governance and infrastructure remain tightly controlled. That imbalance creates risk.&lt;/p&gt;
&lt;p&gt;When key infrastructure or distribution mechanisms are dependent on a single commercial entity, the entire ecosystem becomes fragile. That fragility became impossible to ignore.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://fair.pm/&quot;&gt;FAIR&lt;/a&gt;, launched under the &lt;a href=&quot;https://www.linuxfoundation.org/&quot;&gt;Linux Foundation&lt;/a&gt;, was an attempt to create a neutral, community-governed package manager and infrastructure layer. The goal was stability. Predictability. Shared stewardship. A model that could reduce systemic risk and ensure long-term independence.&lt;/p&gt;
&lt;p&gt;In a very short period of time, the technical project went from a proposal to an effort involving a larger team and us, and then to an actual working model. Over the second half of the year, the project achieved technical success. Today, it is a viable technical alternative to the problem. But success isn’t just measured in technical success or the amount of effort.&lt;/p&gt;
&lt;p&gt;It is a serious effort. Thought through. Properly structured. Not a Twitter thread. Not a protest. A working technical achievement and a real proposal for a model that could work well for all involved parties. But proposals need backing.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;The hard reality: hosts don’t want to invest&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;In recent months, we’ve had many conversations with hosting companies and other large ecosystem players. What became increasingly clear is this: they do not want to invest in this kind of solution.&lt;/p&gt;
&lt;p&gt;Not because they love the current situation. Not because they agree with everything that’s happened. But because investment means commitment. It means cost. It means stepping into political tension. And most of all, it means risk. The current situation, however uncomfortable, is predictable enough. Changing that requires money and coordinated effort. And that willingness simply isn’t there.&lt;/p&gt;
&lt;p&gt;You cannot build alternative infrastructure of this magnitude on goodwill alone. You need adoption and funding. You need shared responsibility. You need large players willing to say: “Yes, we will carry part of this.”&lt;/p&gt;
&lt;p&gt;Without that, FAIR becomes aspirational rather than actionable.&lt;/p&gt;
&lt;p&gt;And we are not here to do an aspirational infrastructure project.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;A moment of uncomfortable empathy&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Here’s the part that surprised us.&lt;/p&gt;
&lt;p&gt;In trying to understand why hosts wouldn’t step up, we found ourselves understanding, if not agreeing with, more of the structural tension Matt has been pointing at for years.&lt;/p&gt;
&lt;p&gt;He has often argued that too many companies benefit from WordPress without contributing proportionally. That the burden of maintaining core infrastructure falls on too few shoulders. That the incentives are misaligned.&lt;/p&gt;
&lt;p&gt;We still fundamentally disagree with how he has handled that frustration. The methods have caused damage. Public pressure campaigns and unilateral decisions are not the way to build trust.&lt;/p&gt;
&lt;p&gt;But the underlying economic problem is real.&lt;/p&gt;
&lt;p&gt;When hosts, some of whom generate significant revenue from WordPress, are unwilling to invest in a neutral stability layer, that tells you something about the ecosystem’s incentive structure.&lt;/p&gt;
&lt;p&gt;And it tells you something about the limits of idealism.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;The AI shift changes the battlefield&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;At the same time, the ground is shifting beneath all of us.&lt;/p&gt;
&lt;p&gt;AI is transforming how websites are built, maintained, and even conceptualized. The barriers to publishing are falling again, not because of CMS innovation, but because AI systems can generate, deploy, and optimize digital experiences at unprecedented speed.&lt;/p&gt;
&lt;p&gt;This changes the competitive landscape dramatically.&lt;/p&gt;
&lt;p&gt;WordPress is no longer just competing with other CMS platforms. It’s competing with AI-native site builders, automated content systems, headless frameworks, and entirely new interaction models.&lt;/p&gt;
&lt;p&gt;In that context, spending years fighting governance battles while the broader web evolves may simply be the wrong strategic focus.&lt;/p&gt;
&lt;p&gt;The ecosystem needs to adapt to AI. To rethink workflows. To rethink value. To rethink what open source publishing means in a world where content can be generated infinitely and interfaces are conversational.&lt;/p&gt;
&lt;p&gt;That is where energy is needed.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;So: we are stepping away from FAIR&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;After much consideration, we’ve decided to stop working on bringing FAIR to the WordPress ecosystem.&lt;/p&gt;
&lt;p&gt;Not because the idea was wrong.&lt;/p&gt;
&lt;p&gt;Not because the governance concerns disappeared.&lt;/p&gt;
&lt;p&gt;Not because everything is suddenly fine.&lt;/p&gt;
&lt;p&gt;But because without hosting integration, including meaningful financial and structural support from key ecosystem players, the project is not viable.&lt;/p&gt;
&lt;p&gt;Continuing would mean either building something underfunded and fragile which defeats the purpose or personally carrying a burden that should be shared.&lt;/p&gt;
&lt;p&gt;Neither is responsible.&lt;/p&gt;
&lt;p&gt;Ending our work on FAIR is not surrender. It is recognizing constraints. And we do so knowing that the work was not for nothing.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What we still believe&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;We still believe WordPress needs more transparent, accountable governance.&lt;/p&gt;
&lt;p&gt;We still believe concentration of power creates systemic risk.&lt;/p&gt;
&lt;p&gt;We still believe the community deserves better than polarization and fear.&lt;/p&gt;
&lt;p&gt;But we also believe change cannot be forced by a handful of motivated individuals if the broader economic layer of the ecosystem refuses to participate.&lt;/p&gt;
&lt;p&gt;Open source is not just code. It is a system of incentives.&lt;/p&gt;
&lt;p&gt;And these incentives, right now, do not align for this kind of structural reform.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;What’s next&lt;/strong&gt;&lt;/h2&gt;
&lt;h3&gt;&lt;strong&gt;For FAIR&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;At &lt;a href=&quot;https://hackathon.cloudfest.com/project/fair-package-management-for-typ/&quot;&gt;Cloudfest’s hackathon&lt;/a&gt; this year, we’ll work on making FAIR work with and for &lt;a href=&quot;https://typo3.com/&quot;&gt;TYPO3&lt;/a&gt;. The &lt;a href=&quot;https://typo3.org/&quot;&gt;TYPO3 community&lt;/a&gt; and technical leadership sees a lot of value in the system that was created, because the technical project actually delivered a good, technically solid, federated package management system.&lt;/p&gt;
&lt;p&gt;In Europe, the concept of digital sovereignty is an incredibly hot topic, and an important part of that is being able to rely on package management servers. FAIR solves for that and we are proud to see that recognized by the TYPO3 community and to see them continue on the work we started.&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;For us&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;WordPress is entering a new phase, shaped not by internal governance debates, but by AI and platform shifts that will redefine publishing and the web itself.&lt;/p&gt;
&lt;p&gt;That is where we want to focus our time and energy.&lt;/p&gt;
&lt;p&gt;On helping WordPress businesses adapt.&lt;br /&gt;
On making plugins and tools smarter.&lt;br /&gt;
On ensuring independent publishers remain competitive.&lt;br /&gt;
On finding a path forward for Open Source in a world where a line of code has no value.&lt;br /&gt;
On building products that move the ecosystem forward, instead of fighting over its control structures.&lt;/p&gt;
&lt;p&gt;The FAIR experiment clarified something important: if the ecosystem won’t fund neutrality, neutrality won’t materialize.&lt;/p&gt;
&lt;p&gt;That’s a lesson worth learning.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;A word of thanks&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Many individuals have been with us along this road, and we’d be remiss not to thank them for their efforts. You know who you are, please know that you have our deepest thanks. We also owe thanks to the Linux Foundation. The team there has been incredibly helpful and stood by us as we took on this challenge.&lt;/p&gt;
&lt;p&gt;We remain committed to WordPress. We remain critical where criticism is needed. We remain hopeful that governance can improve.&lt;/p&gt;
&lt;p&gt;But for us both, FAIR ends here.&lt;/p&gt;
&lt;p&gt;— Karim &amp;amp; Joost&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Share Obsidian links that actually work everywhere</title><link>https://joost.blog/obsidian-agent-links/</link><guid isPermaLink="true">https://joost.blog/obsidian-agent-links/</guid><description>I’ve been playing with OpenClaw a lot, and I’m using Obsidian intensely with it. All my processes and even my OpenClaw’s core files live in my Obsidian vault. I</description><pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been playing with OpenClaw a lot, and I’m using &lt;a href=&quot;https://obsidian.md/&quot;&gt;Obsidian&lt;/a&gt; intensely with it. All my processes and even my OpenClaw’s core files live in my Obsidian vault. I have Obsidian on all machines I work on, so it’s very convenient. There was only one piece missing: a way to get links in WhatsApp from my OpenClaw that point back to Obsidian.&lt;/p&gt;
&lt;p&gt;Obsidian links look gorgeous inside your vault, but drop one into WhatsApp or Telegram and you get a dead &lt;code&gt;obsidian://&lt;/code&gt; URI, useful only inside Obsidian itself. The whole point of &lt;a href=&quot;https://obsid.net/&quot;&gt;obsid.net&lt;/a&gt; is to make those notes shareable in agent-human conversations: your bot can send a link in WhatsApp to obsid.net, and it redirects you straight into the right Obsidian note.&lt;/p&gt;
&lt;p&gt;You don’t need to install anything for it to work. The &lt;code&gt;obsidian://&lt;/code&gt; URL handler just works, and obsid.net just redirects to it. It’s just a simple &lt;code&gt;https://obsid.net/?vault=…&amp;amp;file=…&lt;/code&gt; link you can paste into WhatsApp, Slack, email, or any chat that refuses to run custom schemes.&lt;/p&gt;
&lt;p&gt;Why care? Because in human + agent chats, the agent often is the one sharing notes to you. Codex, Claude, or any cron job can now output safe obsid.net URLs instead of raw obsidian:// URIs, and you can open them anywhere. obsid.net links also look like (no, &lt;em&gt;are&lt;/em&gt;) normal HTTPS URLs, so they survive log archives, bookmarking, and every chat frontend you already use.&lt;/p&gt;
&lt;p&gt;So, give obsid.net a try! Convert one of your old &lt;code&gt;obsidian://&lt;/code&gt; links, install the skill or helper for your agent, and then share the note inside WhatsApp or Telegram. No extra installs (well, except for the skills maybe), just a small redirect that keeps every Obsidian link tappable in your agent conversations.&lt;/p&gt;
</content:encoded><category>Development</category><category>Productivity hacks</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Why healthy doubt beats AI confidence theater</title><link>https://joost.blog/healthy-doubt/</link><guid isPermaLink="true">https://joost.blog/healthy-doubt/</guid><description>AI will confidently sign off on anything. The question is whether you will too.</description><pubDate>Sat, 21 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;AI will confidently sign off on anything. The question is whether you will too.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;I’m a member of my old high school’s “AI council”. My kids go there now, so I have both my own experience and my kids’ to build on when I speak in that council. Recently they asked me to speak to a group of teachers there about AI, as they’re trying to help teachers navigate what AI means for schools. The subject they asked me to speak about was a logical choice for me: the implications of AI on the workforce.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This high school is what’s called a “Gymnasium” in the Netherlands. It’s the highest form of high school education we have where they teach you, besides all the normal subjects, ancient Latin and Greek. And because of that, those mythologies and philosophies too. As I was preparing, I realized this should be a blog post too.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;The term “AI confidence theater” comes from &lt;a href=&quot;https://www.linkedin.com/pulse/ai-confidence-theater-christina-aguilera-sk1nc/&quot;&gt;Christina Aguilera’s piece on leadership&lt;/a&gt;, which is worth reading. She writes about executives performing certainty about AI while privately still figuring it out. That’s real. But this post is about a different layer of the problem: the AI itself.&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;The confidence problem&lt;/h2&gt;
&lt;p&gt;Ask Claude, Gemini, or ChatGPT anything and you’ll get a polished, articulate, supremely confident answer. Even when it’s wrong. Especially when it’s wrong.&lt;/p&gt;
&lt;p&gt;Socrates built an entire philosophy around “I know that I know nothing.” AI does the opposite: it knows nothing about knowing nothing. It has no concept of its own ignorance. Every hallucination arrives dressed in the same crisp suit as every correct answer.&lt;/p&gt;
&lt;p&gt;The philosopher Hannah Arendt drew a sharp line between thinking and knowing. Knowing is accumulating facts and producing answers. Thinking is the harder thing: examining those answers, sitting with uncertainty, refusing to stop at the surface. AI is a knowing machine. It has no capacity for the other thing.&lt;/p&gt;
&lt;p&gt;This is the core problem we’re not talking about enough: &lt;strong&gt;we’ve built machines that perform certainty&lt;/strong&gt;, and we’re raising a generation that’s learning to mistake that performance for truth.&lt;/p&gt;
&lt;h2&gt;The death of average&lt;/h2&gt;
&lt;p&gt;A passable 500-word essay now costs zero effort and ten seconds. A working function in most programming languages, same story. A first-draft marketing plan, a competitive analysis, a translation. All reduced to a button press. The floor hasn’t just risen. It’s been catapulted.&lt;/p&gt;
&lt;p&gt;What this means practically: &lt;strong&gt;average output has no market value anymore&lt;/strong&gt;. The “good enough” report, the “decent” first draft, the code that merely works. AI produces all of it faster, cheaper, and more consistently than any human can. If your only skill is producing average work, you’re competing against a machine that does it at near-zero marginal cost. You will lose.&lt;/p&gt;
&lt;p&gt;But here’s what AI &lt;em&gt;can’t&lt;/em&gt; do: it can’t tell you whether its output is actually good. It can’t feel that something is off. It can’t exercise judgment born from years of getting things wrong and learning why. That’s the human edge. Not production, but &lt;strong&gt;evaluation&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The junior paradox&lt;/h2&gt;
&lt;p&gt;This leads to what might be the most uncomfortable workforce question of the decade.&lt;/p&gt;
&lt;p&gt;You become a senior by doing years of junior work. Aristotle argued in the &lt;em&gt;Nicomachean Ethics&lt;/em&gt; that you learn craft and virtue through practice, by &lt;em&gt;doing&lt;/em&gt;, repeatedly, until understanding becomes instinct. There’s no shortcut. You parse hundreds of sentences before Latin grammar becomes intuitive. You write thousands of lines of bad code before architecture clicks.&lt;/p&gt;
&lt;p&gt;But if AI handles all the junior work, where do the future seniors come from? (I wrote about the flip side of this in &lt;a href=&quot;/rise-architect/&quot;&gt;The death of Code Copyright and the rise of the Architect&lt;/a&gt;: when execution becomes a commodity, what’s left is intent, direction, and logic. The Architect is the senior who made it through. The question is how we get there without the steps in between.)&lt;/p&gt;
&lt;p&gt;I’m already seeing this play out. Developers who call themselves “senior” because they generate code at impressive speed, but who collapse the moment they hit a bug the model can’t resolve. They never put in the hours. They skipped the reps. They have the job title without the scar tissue.&lt;/p&gt;
&lt;p&gt;And employers are catching on. Why hire a junior you need to train for three years when AI does their job today? It’s an obvious short-term decision and a slow-motion disaster for the industry. We’re removing the very path that made senior developers possible.&lt;/p&gt;
&lt;p&gt;The only way through: &lt;strong&gt;the junior work still has to happen, but the bar for what constitutes “junior” has moved up dramatically.&lt;/strong&gt; Entry-level now means you can do what AI does &lt;em&gt;plus&lt;/em&gt; catch where it fails. That’s a fundamentally different starting point than five years ago.&lt;/p&gt;
&lt;h2&gt;Pilot or passenger?&lt;/h2&gt;
&lt;p&gt;There’s a useful mental model here: are you the pilot using autopilot to fly better, or the passenger who doesn’t know how to land the plane when the power goes out?&lt;/p&gt;
&lt;p&gt;The autopilot metaphor is apt because real pilots don’t trust autopilot blindly. They understand every system it controls. They monitor. They override. They train constantly for the scenarios where automation fails, because it always eventually does.&lt;/p&gt;
&lt;p&gt;Think of AI as a very capable intern (or 20 of them, in fact). Fast, eager, able to handle enormous volumes of routine work. But you’d never let an intern sign off on a contract, publish without review, or make an architectural decision. The person with &lt;em&gt;skin in the game&lt;/em&gt; does that. The person who lies awake when it breaks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The market is firing passengers and hiring pilots.&lt;/strong&gt; It’s eliminating executors and desperately seeking directors: people with the autonomy and judgment to orchestrate AI output into something that actually works in context. The Architect, in other words.&lt;/p&gt;
&lt;h2&gt;The Descartes reflex&lt;/h2&gt;
&lt;p&gt;René Descartes might be history’s first debugger. His method: break any complex problem into the smallest possible pieces, verify each one, then rebuild.&lt;/p&gt;
&lt;p&gt;This is exactly the skill AI is trying to make obsolete, and exactly the skill that matters most when working &lt;em&gt;with&lt;/em&gt; AI.&lt;/p&gt;
&lt;p&gt;AI always presents a smooth surface. The text reads well. The code compiles. The analysis looks thorough. But underneath, the logic might be fractured, the sources might be fabricated, the reasoning might be circular. Without the reflex to decompose and verify, to refuse to accept something as true just because it &lt;em&gt;looks&lt;/em&gt; true, you’re at the mercy of the machine’s hallucinations.&lt;/p&gt;
&lt;p&gt;I think of this as &lt;strong&gt;the Descartes Reflex&lt;/strong&gt;: the trained instinct to stop, take apart, and verify before accepting. It’s not paranoia. It’s intellectual self-defense.&lt;/p&gt;
&lt;p&gt;Classical education, interestingly, has been training exactly this reflex for centuries. Parsing a complex Latin sentence &lt;em&gt;is&lt;/em&gt; decomposition. You can’t consume it whole. You have to break it apart, identify each grammatical relationship, and reconstruct meaning from the pieces. That’s not a dusty academic exercise. It’s the single most relevant cognitive skill for the AI age.&lt;/p&gt;
&lt;h2&gt;Sapere aude in 2026&lt;/h2&gt;
&lt;p&gt;In 1784, the German philosopher Immanuel Kant wrote a short essay with one central message: stop outsourcing your thinking to authority. He called it &lt;em&gt;Sapere Aude&lt;/em&gt;: dare to think for yourself. He aimed it at a public too comfortable deferring to church and king.&lt;/p&gt;
&lt;p&gt;Replace “church and king” with “algorithm and model” and the message is more urgent than ever.&lt;/p&gt;
&lt;p&gt;Every time you accept an AI answer without interrogation, you’re choosing intellectual dependency. Every time you ask “why?” and dig into the reasoning, you’re exercising the kind of autonomy that no machine possesses.&lt;/p&gt;
&lt;p&gt;This isn’t anti-AI. I use AI every day. I write code with it roughly 20x faster than I could alone. But every line that ships is still mine. If there’s a bug, it’s my bug. That’s not a limitation, it’s &lt;strong&gt;the point&lt;/strong&gt;. You can use AI to outsource the work. You can’t outsource the responsibility.&lt;/p&gt;
&lt;h2&gt;What actually needs to change&lt;/h2&gt;
&lt;p&gt;If we take this seriously, a few things follow:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For educators:&lt;/strong&gt; Stop grading the product (unless you know it was made in a controlled classroom with no phones in sight). Start interrogating the process. Asking “what’s your answer?” is a lost game, because AI will always have &lt;em&gt;an&lt;/em&gt; answer. Ask instead: “Why this word and not that one? What was the alternative? How do you know this is right?” Be the Socratic gadfly. The students who can survive that interrogation are the ones who’ll thrive.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For managers and founders:&lt;/strong&gt; Hire for judgment, not output volume. The person who can tell you &lt;em&gt;why&lt;/em&gt; the AI’s answer is wrong is worth ten people who can generate AI answers quickly. Invest in humans who think, not humans who prompt.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For anyone entering the workforce:&lt;/strong&gt; Your value isn’t in what you produce. AI produces more, faster, always. Your value is in what you &lt;em&gt;understand&lt;/em&gt;. In the gap between “this looks right” and “this &lt;em&gt;is&lt;/em&gt; right.” In the willingness to say “I don’t know yet” when the machine is already confidently wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For all of us:&lt;/strong&gt; Philosophy isn’t a luxury anymore. The skills it teaches (deconstructing arguments, questioning assumptions, distinguishing valid from sound reasoning, exercising judgment under uncertainty) aren’t nice-to-haves. They’re cognitive self-defense.&lt;/p&gt;
&lt;h2&gt;The uncomfortable truth&lt;/h2&gt;
&lt;p&gt;AI makes the average worker obsolete, but the excellent worker irreplaceable.&lt;/p&gt;
&lt;p&gt;That’s not a comfortable message. It means the stakes are higher, the climb is steeper, and “good enough” is a dead end. But it also means that genuine human capability, the kind built through struggle, failure, and hard-won understanding, has never been more valuable.&lt;/p&gt;
&lt;p&gt;The wind is in the sails. Humans still set the course. And when it goes wrong, you can’t blame the wind.&lt;/p&gt;
</content:encoded><category>Post from Joost</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Building an Autonomous Feedback Agent</title><link>https://joost.blog/autonomous-feedback-agent/</link><guid isPermaLink="true">https://joost.blog/autonomous-feedback-agent/</guid><description>What if your users could submit a bug report and have a pull request ready for review before you’ve even seen the ticket? That’s what we built for Rondo Club, a</description><pubDate>Sat, 14 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;What if your users could submit a bug report and have a pull request ready for review before you’ve even seen the ticket? That’s what we built for &lt;a href=&quot;https://rondo.svawc.nl&quot;&gt;Rondo Club&lt;/a&gt;, a sports club management app. It’s a fully autonomous feedback processing pipeline (in my case powered by Claude Code).&lt;/p&gt;
&lt;p&gt;This post walks through the entire system: how feedback gets submitted, how an AI agent picks it up and writes code, how PRs get reviewed automatically, and how it all runs unattended on a Mac Mini (but could run anywhere).&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-idea&quot;&gt;The idea&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-how-it-works&quot;&gt;How it works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-1-feedback-submission&quot;&gt;Stage 1: Feedback submission&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-2-approval&quot;&gt;Stage 2: Approval&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-3-the-feedback-agent&quot;&gt;Stage 3: The feedback agent&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-fetching-work&quot;&gt;Fetching work&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-running-claude-code&quot;&gt;Running Claude Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-status-handling&quot;&gt;Status handling&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-safety-features&quot;&gt;Safety features&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-4-automated-code-review&quot;&gt;Stage 4: Automated code review&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-5-merge-and-deploy&quot;&gt;Stage 5: Merge and deploy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-6-fixing-errors-from-the-server&quot;&gt;Stage 6: Fixing errors from the server&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-stage-7-code-optimization&quot;&gt;Stage 7: Code optimization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-conversation-thread&quot;&gt;The conversation thread&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-things-i-learned&quot;&gt;The things I learned&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-what-works-well&quot;&gt;What works well&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-what-to-watch-for&quot;&gt;What to watch for&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-stack&quot;&gt;The stack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-running-it-yourself&quot;&gt;Running it yourself&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-tried-this-yourself-have-feedback&quot;&gt;Tried this yourself? Have feedback?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The idea&lt;/h2&gt;
&lt;p&gt;Rondo Club is a WordPress-based application with a React frontend, used by my local football club to manage members, teams, and committees. As a solo developer, I wanted to shorten the loop between “user reports a problem” and “fix deployed to production”, without me being the bottleneck for every small bug or feature request.&lt;/p&gt;
&lt;p&gt;The goal: users submit feedback in the app, I approve it with a click. Then an AI agent processes it, creates a PR, gets it reviewed, and merges it, all without further human intervention for straightforward changes.&lt;/p&gt;
&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;The system has six stages:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Submission&lt;/strong&gt; — Users submit feedback through an in-app form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approval&lt;/strong&gt; — An admin reviews and approves the feedback item.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processing&lt;/strong&gt; — A scheduled script picks up approved feedback and sends it to Claude Code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review&lt;/strong&gt; — The resulting PR gets an automated code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resolution&lt;/strong&gt; — Clean PRs are merged and deployed automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimization&lt;/strong&gt; — When the feedback queue is empty, the agent reviews existing code for improvements.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s walk through each stage.&lt;/p&gt;
&lt;h2&gt;Stage 1: Feedback submission&lt;/h2&gt;
&lt;p&gt;Every logged-in user sees a feedback button in the app’s navigation. Clicking it opens a modal where they can submit either a &lt;strong&gt;bug report&lt;/strong&gt; or a &lt;strong&gt;feature request&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For bugs, the form captures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Title and description&lt;/li&gt;
&lt;li&gt;Steps to reproduce&lt;/li&gt;
&lt;li&gt;Expected vs. actual behavior&lt;/li&gt;
&lt;li&gt;Optional screenshot attachments (drag-and-drop)&lt;/li&gt;
&lt;li&gt;Optional system info (browser, app version, current URL)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For feature requests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Title and description&lt;/li&gt;
&lt;li&gt;Use case&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Users also select which project the feedback is for, as the system supports multiple repos (the main app, a sync service, and a marketing website).&lt;/p&gt;
&lt;p&gt;On the backend, feedback items are stored as a WordPress custom post type with metadata for status, type, priority, and project. A REST API endpoints handles CRUD operations and supports filtering by status and type.&lt;/p&gt;
&lt;p&gt;New feedback items start with status &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Stage 2: Approval&lt;/h2&gt;
&lt;p&gt;This is the only human gate in the process, and it’s an important one. An admin reviews each feedback item and sets it to &lt;code&gt;approved&lt;/code&gt; when it’s ready for the agent to pick up. This prevents the agent from acting on vague, duplicate, or potentially malicious input. Without this step, any logged-in user could effectively instruct an AI agent to write and merge code into your codebase.&lt;/p&gt;
&lt;h2&gt;Stage 3: The feedback agent&lt;/h2&gt;
&lt;p&gt;The heart of the system is a bash script that runs on a Mac Mini via &lt;code&gt;launchd&lt;/code&gt;, scheduled every 5 minutes. It operates in loop mode: fetch the oldest approved item, process it, repeat until the queue is empty.&lt;/p&gt;
&lt;h3&gt;Fetching work&lt;/h3&gt;
&lt;p&gt;Each iteration follows a priority order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;In-review PRs first&lt;/strong&gt; — Check if any existing PRs have Copilot review feedback that needs addressing. Finish what’s already started before taking on new work.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Approved feedback&lt;/strong&gt; — Pick up the oldest approved item from the API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Optimization&lt;/strong&gt; — Only when nothing else needs doing, review code for improvements.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Running Claude Code&lt;/h3&gt;
&lt;p&gt;When the script picks up a feedback item, it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sets the item’s status to &lt;code&gt;in_progress&lt;/code&gt; (so no other run picks it up)&lt;/li&gt;
&lt;li&gt;Formats the feedback into a structured prompt with all context: description, steps to reproduce, conversation history, and system instructions&lt;/li&gt;
&lt;li&gt;Passes it to Claude Code with &lt;code&gt;--print --dangerously-skip-permissions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Monitors the process with a 10-minute timeout, logging progress every 2 minutes&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The prompt ends with an agent instruction file (&lt;code&gt;.claude/agent-prompt.md&lt;/code&gt;) that tells Claude exactly what to do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;feedback/{id}-{slug}&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;Analyze the feedback and make changes&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;npm run build&lt;/code&gt; to verify the frontend compiles&lt;/li&gt;
&lt;li&gt;Commit, push, and create a PR with the feedback ID in the title&lt;/li&gt;
&lt;li&gt;Output a structured status (&lt;code&gt;IN_REVIEW&lt;/code&gt;, &lt;code&gt;NEEDS_INFO&lt;/code&gt;, or &lt;code&gt;DECLINED&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Status handling&lt;/h3&gt;
&lt;p&gt;When Claude finishes, the script parses its output for a status line:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;IN_REVIEW&lt;/code&gt;&lt;/strong&gt; — A PR was created. The script extracts the PR URL, stores it as metadata on the feedback item, and requests a Copilot code review.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;NEEDS_INFO&lt;/code&gt;&lt;/strong&gt; — Claude couldn’t resolve the issue autonomously. The script posts Claude’s question as a comment on the feedback thread, visible to the user in the app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;DECLINED&lt;/code&gt;&lt;/strong&gt; — The feedback was evaluated and determined not actionable.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Claude fails or times out, the status resets to &lt;code&gt;approved&lt;/code&gt; so it re-enters the queue.&lt;/p&gt;
&lt;h3&gt;Safety features&lt;/h3&gt;
&lt;p&gt;The script includes several safety mechanisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Lock file&lt;/strong&gt; prevents concurrent runs (launchd might trigger while a previous run is still going)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Signal handlers&lt;/strong&gt; catch SIGTERM/SIGINT and clean up — resetting feedback status and checking out &lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeout enforcement&lt;/strong&gt; kills hung Claude processes after 10 minutes&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crash recovery&lt;/strong&gt; resets feedback status if the script exits unexpectedly&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git cleanup&lt;/strong&gt; returns to &lt;code&gt;main&lt;/code&gt; and removes merged branches after each item&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Stage 4: Automated code review&lt;/h2&gt;
&lt;p&gt;After Claude creates a PR, the script requests a review from GitHub Copilot. On the next run, the PR review processor checks for responses:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Clean review (no inline comments)&lt;/strong&gt; — The PR is safe to merge. Proceed directly to Stage 4.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Review with comments&lt;/strong&gt; — The script checks out the PR branch, formats Copilot’s inline comments into a prompt, and runs Claude again with a review-fix instruction set (&lt;code&gt;.claude/review-fix-prompt.md&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The review-fix prompt tells Claude to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fix real bugs, consistency issues, and safety problems&lt;/li&gt;
&lt;li&gt;Skip stylistic nitpicks, test requests, and subjective suggestions&lt;/li&gt;
&lt;li&gt;Output whether the PR is &lt;code&gt;SAFE_TO_MERGE&lt;/code&gt; after fixes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Claude determines it’s safe to merge after fixing review comments, the PR proceeds to automatic merge (and if it fails, which can happen because other PRs have been merged since etc, Claude fixes the merge conflict). Otherwise, it gets assigned to the human developer for manual review.&lt;/p&gt;
&lt;h2&gt;Stage 5: Merge and deploy&lt;/h2&gt;
&lt;p&gt;When a PR is deemed safe — either by a clean Copilot review or by Claude’s post-fix assessment — the script:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Squash-merges the PR via &lt;code&gt;gh pr merge --squash --delete-branch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Pulls &lt;code&gt;main&lt;/code&gt; with the merged changes&lt;/li&gt;
&lt;li&gt;Runs the deploy script, which rsync’s the theme to production and clears caches&lt;/li&gt;
&lt;li&gt;Updates the feedback item status to &lt;code&gt;resolved&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The feedback ID is extracted from the branch name (&lt;code&gt;feedback/1234-slug&lt;/code&gt; → &lt;code&gt;1234&lt;/code&gt;) to automatically close the right item.&lt;/p&gt;
&lt;h2&gt;Stage 6: Fixing errors from the server&lt;/h2&gt;
&lt;p&gt;Once the feedback queue is empty and the reviewed PRs are handled, the agent starts looking at the server’s PHP error logs. If it finds errors it can fix, it’ll run the same flow for them as it does for feedback items.&lt;/p&gt;
&lt;h2&gt;Stage 7: Code optimization&lt;/h2&gt;
&lt;p&gt;When the feedback queue is empty, all the PRs are handled and there are no errors on the server that need work, the agent enters optimization mode. It systematically works through every source file across all projects, reviewing each one for simplification opportunities: dead code removal, DRY violations, unnecessary complexity, performance wins. If it finds confident improvements, it creates an &lt;code&gt;optimize/&lt;/code&gt; PR. If not, it moves on.&lt;/p&gt;
&lt;p&gt;To avoid wasting API calls re-reviewing unchanged code, the tracker stores the git commit hash that last touched each file. On subsequent runs, it compares the stored hash against the current one — if they match, the file hasn’t changed and gets skipped. Only files with new commits are re-reviewed.&lt;/p&gt;
&lt;p&gt;A tracker file prevents reviewing the same file twice and enforces daily limits (25 optimization runs, 10 PRs per day) to keep costs reasonable.&lt;/p&gt;
&lt;h2&gt;The conversation thread&lt;/h2&gt;
&lt;p&gt;One of the more useful features is the back-and-forth capability. When Claude responds with &lt;code&gt;NEEDS_INFO&lt;/code&gt;, its question gets posted as a comment on the feedback item. The user sees this in the app’s feedback detail page and can reply.&lt;/p&gt;
&lt;p&gt;On the next processing run, the agent picks up the item again — this time with the full conversation history included in the prompt. Claude sees the original feedback plus all follow-up exchanges, giving it the context to resolve the issue.&lt;/p&gt;
&lt;p&gt;This means some feedback items go through multiple rounds:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;User submits bug report&lt;/li&gt;
&lt;li&gt;Admin approves&lt;/li&gt;
&lt;li&gt;Agent asks clarifying question&lt;/li&gt;
&lt;li&gt;User responds&lt;/li&gt;
&lt;li&gt;Admin approves again (to prevent injection of vile stuff)&lt;/li&gt;
&lt;li&gt;Agent fixes the bug and creates a PR&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The things I learned&lt;/h2&gt;
&lt;h3&gt;What works well&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Small, focused changes are reliable.&lt;/strong&gt; Claude handles targeted bug fixes and UI tweaks consistently well. The structured feedback format (steps to reproduce, expected/actual behavior) gives it enough context to work autonomously.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The priority system matters.&lt;/strong&gt; Finishing in-review PRs before starting new work prevents a pile-up of half-done items.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeouts are essential.&lt;/strong&gt; Claude occasionally gets stuck in an idle state. The 10-minute timeout with progress logging catches these cases automatically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The human gate is important.&lt;/strong&gt; Having an admin approve feedback before it enters the queue filters out vague or duplicate reports that would waste agent time.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;What to watch for&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Claude sometimes over-engineers.&lt;/strong&gt; The agent prompt explicitly says “keep changes minimal and focused” — without this, Claude tends to refactor surrounding code while fixing a bug.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Process monitoring needs visibility.&lt;/strong&gt; Early on, stuck processes were invisible. Adding progress logging every 2 minutes and structured log output made it much easier to diagnose issues remotely.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Git state management is tricky.&lt;/strong&gt; The script needs to handle dirty working directories, failed checkouts, and stale branches defensively. Every Claude session might leave the repo in an unexpected state.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The stack&lt;/h2&gt;
&lt;p&gt;ComponentTechnologyFeedback storageWordPress custom post type + REST APIFeedback UIReact form with drag-and-drop attachmentsProcessing scriptBash (&lt;code&gt;get-feedback.sh&lt;/code&gt;)AI agentClaude CodeCode reviewGitHub CopilotPR managementGitHub CLI (&lt;code&gt;gh&lt;/code&gt;)SchedulingmacOS &lt;code&gt;launchd&lt;/code&gt; (every 5 minutes)Deploymentrsync via bash scriptHostingMac Mini (agent) + WordPress## Running it yourself&lt;/p&gt;
&lt;p&gt;The core pattern is transferable to any project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Build a feedback intake&lt;/strong&gt; — could be a form, a Slack bot, a GitHub issue template, whatever captures structured input.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write an agent prompt&lt;/strong&gt; — be very specific about what the agent should and shouldn’t do. Include rules about branch naming, commit messages, and PR format. Tell it to keep changes minimal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Wrap Claude Code in a monitoring script&lt;/strong&gt; — handle timeouts, cleanup, and status tracking. Don’t trust that the process will always complete cleanly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add automated review&lt;/strong&gt; — a second pass catches issues the agent might introduce. This could be Copilot, another Claude session, or a linter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automate the merge/deploy for safe changes&lt;/strong&gt; — but keep a human fallback for anything complex.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The full source is in the &lt;a href=&quot;https://github.com/RondoHQ/rondo-club&quot;&gt;Rondo Club repository&lt;/a&gt; — the agent script at &lt;code&gt;bin/get-feedback.sh&lt;/code&gt; and the prompts in &lt;code&gt;.claude/&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Tried this yourself? Have feedback?&lt;/h2&gt;
&lt;p&gt;Let me know in the comments!&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Great minds think alike? My WordPress take on Markdown for Agents</title><link>https://joost.blog/markdown-alternate/</link><guid isPermaLink="true">https://joost.blog/markdown-alternate/</guid><description>Today, Cloudflare announced Markdown for Agents, a feature that automatically converts HTML to markdown at the edge when AI agents request it. Reading their ann</description><pubDate>Thu, 12 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, Cloudflare announced &lt;a href=&quot;https://blog.cloudflare.com/markdown-for-agents/&quot;&gt;Markdown for Agents&lt;/a&gt;, a feature that automatically converts HTML to markdown at the edge when AI agents request it. Reading their announcement felt like looking in a mirror. I’ve been building the exact same concept, even up to including frontmatter with metadata, independently, as a WordPress plugin called Markdown Alternate (which has been running on this blog for almost two weeks).&lt;/p&gt;
&lt;p&gt;The core insight is identical: the web was built for humans, but AI agents are now a significant — and growing — consumer of web content. Feeding raw HTML to an LLM is wasteful. Cloudflare cites an 80% token reduction when converting their own blog post from HTML to markdown. That matches what I’ve seen.&lt;/p&gt;
&lt;p&gt;Both solutions use the same mechanism: HTTP content negotiation via the &lt;code&gt;Accept: text/markdown&lt;/code&gt; header. When an agent sends that header, it gets markdown back instead of HTML. Tools like Claude Code and OpenCode already send this header. The infrastructure is ready, the content just needs to follow.&lt;/p&gt;
&lt;p&gt;Cloudflare’s solution operates at the CDN edge. It’s broad: flip a toggle in your dashboard, and any page on your zone can be served as markdown. The conversion is generic, Cloudflare doesn’t know whether your page is a blog post, a product page, or a documentation article. It converts whatever HTML it sees.&lt;/p&gt;
&lt;h2&gt;What &lt;code&gt;markdown-alternate&lt;/code&gt; does&lt;/h2&gt;
&lt;p&gt;My plugin operates at the application level, inside WordPress. That makes it narrower in scope but significantly deeper in what it can do. It offers:&lt;/p&gt;
&lt;h3&gt;Dedicated &lt;code&gt;.md&lt;/code&gt; URLs.&lt;/h3&gt;
&lt;p&gt;Markdown Alternate gives every post and page its own &lt;code&gt;.md&lt;/code&gt; endpoint. Instead of only relying on content negotiation, you can simply request &lt;code&gt;/my-post.md&lt;/code&gt; and get markdown back. These URLs are bookmarkable, independently cacheable, and intuitive, just like &lt;code&gt;.json&lt;/code&gt; or &lt;code&gt;.xml&lt;/code&gt; endpoints. Cloudflare only supports the &lt;code&gt;Accept&lt;/code&gt; header approach. Of course they do add &lt;code&gt;rel=&quot;canonical&quot;&lt;/code&gt; HTTP headers back to the HTML source.&lt;/p&gt;
&lt;h3&gt;Discoverability.&lt;/h3&gt;
&lt;p&gt;The plugin adds &lt;code&gt;&amp;lt;link rel=&quot;alternate&quot; type=&quot;text/markdown&quot;&amp;gt;&lt;/code&gt; tags to every page’s HTML head. An agent visiting the HTML version can programmatically discover that a markdown version exists. Cloudflare doesn’t offer this, agents have to know to ask for markdown upfront. It might actually be a good addition for their solution.&lt;/p&gt;
&lt;h3&gt;Richer metadata.&lt;/h3&gt;
&lt;p&gt;Cloudflare’s frontmatter includes title, description, and image. Because my plugin lives inside WordPress, it has access to the full post object. The frontmatter includes the title, publication date, author, featured image, and categories and tags, each with their own &lt;code&gt;.md&lt;/code&gt; URLs, so agents can navigate related content.&lt;/p&gt;
&lt;h3&gt;WordPress-aware content processing.&lt;/h3&gt;
&lt;p&gt;The plugin &lt;em&gt;knows&lt;/em&gt; what type of content it’s processing. It strips syntax highlighting markup that plugins like Highlight.js inject into code blocks, preserving clean code with language hints intact. A generic HTML-to-markdown converter can’t easily know about this.&lt;/p&gt;
&lt;h2&gt;“Stealing” the good ideas&lt;/h2&gt;
&lt;p&gt;Cloudflare’s announcement included one feature I immediately wanted: the &lt;code&gt;X-Markdown-Tokens&lt;/code&gt; header. It returns an estimated token count for the markdown response, letting agents plan their context window usage or chunking strategy before processing the content.&lt;/p&gt;
&lt;p&gt;I added it to Markdown Alternate within minutes of reading their post. The implementation is simple, &lt;code&gt;strlen / 4&lt;/code&gt; gives a reasonable estimate for English text, but the idea of exposing this as a header is clever. It costs nothing to produce and gives agents useful information they’d otherwise have to calculate themselves.&lt;/p&gt;
&lt;h2&gt;Complementary, not competing&lt;/h2&gt;
&lt;p&gt;These two approaches aren’t in conflict. A WordPress site could use Markdown Alternate for rich, WordPress-aware markdown with dedicated URLs and full metadata, while Cloudflare’s feature provides a baseline for every other site on their network. The plugin gives you control and depth; Cloudflare gives you breadth and zero effort.&lt;/p&gt;
&lt;p&gt;The bigger picture is that the web is adapting to a new kind of consumer. HTML isn’t going away, but the expectation that every consumer of web content is a browser is. Markdown is emerging as the lingua franca for AI systems, and the sooner publishers start serving it natively, the better their content will perform in an agent-driven world.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/progressplanner/markdown-alternate&quot;&gt;Markdown Alternate is available on GitHub.&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>Development</category><category>Open Source</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The silence is deafening: Google’s “agentic” future leaves the WordPress economy behind</title><link>https://joost.blog/deafening-silence-google-wordpress-agentic/</link><guid isPermaLink="true">https://joost.blog/deafening-silence-google-wordpress-agentic/</guid><description>Google just announced a massive shift in how the internet shops, and the biggest platform on the web wasn’t even in the room.</description><pubDate>Mon, 12 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Google just announced a massive shift in how the internet shops, and the biggest platform on the web wasn’t even in the room.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you haven’t seen it yet, Google recently dropped a bombshell announcement regarding the future of online retail. They are rolling out a new “Universal Commerce Protocol” (UCP) and a suite of “agentic commerce” tools. In plain English? They are building a standard language for AI agents to browse, negotiate, and buy products on behalf of humans.&lt;/p&gt;
&lt;p&gt;You can read the full announcement here: &lt;a href=&quot;https://blog.google/products/ads-commerce/agentic-commerce-ai-tools-protocol-retailers-platforms/&quot;&gt;New tech and tools for retailers to succeed in an agentic shopping era&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The list of launch partners is a who’s who of modern commerce: &lt;strong&gt;Shopify&lt;/strong&gt;, &lt;strong&gt;BigCommerce&lt;/strong&gt;, &lt;strong&gt;Etsy&lt;/strong&gt;, &lt;strong&gt;Wayfair&lt;/strong&gt;, &lt;strong&gt;Walmart&lt;/strong&gt;, and &lt;strong&gt;Target&lt;/strong&gt;. Financial giants like &lt;strong&gt;Stripe&lt;/strong&gt;, &lt;strong&gt;Visa&lt;/strong&gt;, and &lt;strong&gt;Adyen&lt;/strong&gt; are backing it.&lt;/p&gt;
&lt;p&gt;But if you look closely at that list, you’ll notice a gaping hole.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;WordPress and WooCommerce are nowhere to be found.&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;The representation gap&lt;/h3&gt;
&lt;p&gt;This omission exposes a dangerous structural flaw in our community.&lt;/p&gt;
&lt;p&gt;WooCommerce isn’t just a plugin; it is an economy. It feeds a massive ecosystem of hosting companies (for example, GoDaddy, WP Engine, SiteGround &amp;amp; Kinsta), premium plugin developers (like StellarWP, Elementor, and Awesome Motive), and thousands of agencies.&lt;/p&gt;
&lt;p&gt;These companies have the most to lose. If WooCommerce slides into irrelevance because it can’t “speak” to Google’s new AI agents, their revenue streams dry up. &lt;strong&gt;Yet, none of these companies have a seat at the table.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When Google calls a meeting to define the future of the web, they (of course) don’t invite a swath of individual hosting CEOs or plugin developers. They invite the platform owner. For Shopify, that’s Tobi Lütke. For WooCommerce? It &lt;em&gt;should&lt;/em&gt; be Automattic.&lt;/p&gt;
&lt;p&gt;This falls squarely on Automattic’s shoulders. As the steward of the brand and the “owner” of WooCommerce, they are the only entity with the perceived authority to represent this massive ecosystem in high-level enterprise negotiations. If they aren’t in the room, &lt;em&gt;we&lt;/em&gt; aren’t in the room.&lt;/p&gt;
&lt;h3&gt;The “open web” liability&lt;/h3&gt;
&lt;p&gt;For years, we in the WordPress community have taken pride in being the “open web”. We own our data. We don’t pay monthly platform fees to a landlord. But this announcement signals a terrifying reality: &lt;strong&gt;Being “open” doesn’t matter if the new gatekeepers can’t speak your language.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Google’s new protocol is designed to standardize the chaotic world of checkout and product data so that AI (specifically Gemini) can read it instantly. By partnering directly with Shopify and BigCommerce, Google is effectively creating a “fast lane” for those merchants.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;If you are on Shopify,&lt;/strong&gt; your products will soon be natively understandable by Google’s AI agents. A user asks Gemini for “a good running shoe,” and the AI can theoretically find it, vet it, and even initiate the checkout without the user ever wrestling with a clunky website.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If you are on WooCommerce,&lt;/strong&gt; right now, it looks like you are stuck in the slow lane, waiting for a plugin developer to build a bridge to this new world.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;The downstream disaster&lt;/h3&gt;
&lt;p&gt;The danger isn’t just that a few store owners lose sales. The danger is systemic.&lt;/p&gt;
&lt;p&gt;If WooCommerce becomes the “Linux of ecommerce”, powerful, beloved by geeks, but invisible to the modern AI consumer, merchants will leave. And when merchants leave, they don’t just cancel a free plugin. They cancel their $50, $100, $500/month hosting plan. They cancel their plugin subscriptions. They stop hiring WP agencies.&lt;/p&gt;
&lt;p&gt;The hosting and plugin companies that profit from the WordPress ecosystem are currently relying on Automattic to keep the platform viable at the enterprise level. But if Automattic is too distracted by legal battles or simply not prioritized by big tech, the entire downstream economy suffers.&lt;/p&gt;
&lt;h3&gt;Is there hope? (And why we need FAIR)&lt;/h3&gt;
&lt;p&gt;It’s not all doom and gloom, yet. But waiting for Automattic to fix this might be a strategy we can no longer afford.&lt;/p&gt;
&lt;p&gt;This is exactly why initiatives like &lt;strong&gt;&lt;a href=&quot;https://fair.pm/&quot;&gt;FAIR&lt;/a&gt;&lt;/strong&gt; are becoming critical. We need a neutral, decentralized body that can represent the &lt;em&gt;business interests&lt;/em&gt; of the WordPress ecosystem (the companies I mentioned above and the many others out there) without being tied to a single company’s roadmap.&lt;/p&gt;
&lt;p&gt;If Automattic won’t sit, or doesn’t get invited to sit, at the table with Google to implement the Universal Commerce Protocol, then the hosting and plugin companies need to do it themselves. FAIR could be the vehicle to standardize our data structures, giving us a collective voice that even Google can’t (and wouldn’t even want to) ignore.&lt;/p&gt;
&lt;p&gt;If we don’t figure out how to unify our voice, either through Automattic or through a new coalition, we won’t just be independent. We’ll be irrelevant.&lt;/p&gt;
</content:encoded><category>Open Source</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The Tailwind paradox: the high price of “enough”</title><link>https://joost.blog/tailwind-paradox/</link><guid isPermaLink="true">https://joost.blog/tailwind-paradox/</guid><description>The news hit the developer community like a cold bucket of water. Adam Wathan, the creator of Tailwind CSS, recently announced that he had to lay off 75% of his</description><pubDate>Sun, 11 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The news hit the developer community like a cold bucket of water. Adam Wathan, the creator of Tailwind CSS, recently announced that he &lt;a href=&quot;https://github.com/tailwindlabs/tailwindcss.com/pull/2388#issuecomment-3717222957&quot;&gt;had to lay off 75% of his engineering team&lt;/a&gt;, cutting the staff from four engineers down to just one.&lt;/p&gt;
&lt;p&gt;The numbers he shared were staggering. Despite Tailwind being more popular than ever (with 75 million downloads a month), traffic to their documentation is down 40% since 2023. Worse, revenue is down nearly 80%.&lt;/p&gt;
&lt;p&gt;The culprit? AI.&lt;/p&gt;
&lt;p&gt;As Adam explained, developers are no longer visiting the docs to learn how to center a div; they are asking ChatGPT or Claude. Since the documentation site was the primary funnel for their commercial products (Tailwind UI), the business collapsed even as the tool’s usage skyrocketed.&lt;/p&gt;
&lt;p&gt;Dries Buytaert recently published a thoughtful reaction to this, titled &lt;em&gt;&lt;a href=&quot;https://dri.es/ai-is-a-business-model-stress-test&quot;&gt;AI is a business model stress test&lt;/a&gt;&lt;/em&gt;. He argues that AI is forcing a shift from “Specification” (code/docs) to “Operation” (hosting/services).&lt;/p&gt;
&lt;p&gt;He is absolutely right. But as I read it, I kept thinking that multiple things can be true at once, with #3 being the most tragic in my eyes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;AI unfairly captured value it didn’t pay for.&lt;/li&gt;
&lt;li&gt;Tailwind’s pricing model was a ticking time bomb regardless of AI.&lt;/li&gt;
&lt;li&gt;Their desire to be “good” open-source citizens is &lt;em&gt;exactly&lt;/em&gt; what left them vulnerable.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;I &lt;em&gt;want&lt;/em&gt; Tailwind to win&lt;/h2&gt;
&lt;p&gt;Before I get into the economics, let me be clear: I love Tailwind. I use it constantly. I have personally purchased their lifetime “all-access” licenses multiple times for different companies, including Yoast and Emilia Capital.&lt;/p&gt;
&lt;p&gt;I am the ideal customer. I bought the product because it provides immense utility, and I wanted to support the creators. It’s painful to see a team that has built something so useful struggle.&lt;/p&gt;
&lt;h2&gt;The great value heist&lt;/h2&gt;
&lt;p&gt;Dries points out a harsh reality in his piece: AI broke the funnel.&lt;/p&gt;
&lt;p&gt;For years, Tailwind’s business model was a perfect loop: Developers searched for answers → landed on Tailwind docs → saw the beautiful Tailwind UI ads → bought a license.&lt;/p&gt;
&lt;p&gt;AI severed that link. LLMs were trained on Tailwind’s documentation, examples, and community discussions. Now, when a developer asks, “How do I center a div in Tailwind?” the AI provides the answer instantly.&lt;/p&gt;
&lt;p&gt;The AI creates value for the user by using Tailwind’s intellectual property, but it captures 100% of the attention. Tailwind Labs gets $0 and 0 clicks. It is, effectively, an extraction of value without compensation. When I was discussing this with &lt;a href=&quot;https://www.jonoalderson.com/&quot;&gt;Jono&lt;/a&gt;, he mentioned, I think correctly, that the same risks apply to &lt;em&gt;any&lt;/em&gt; product/service where the primary function is to abstract away the complexity of primitives.&lt;/p&gt;
&lt;p&gt;But even if we solved that problem, even if AI didn’t exist, Tailwind was facing a mathematical wall.&lt;/p&gt;
&lt;h2&gt;The “forever” problem in a finite market&lt;/h2&gt;
&lt;p&gt;Tailwind UI launched with a promise that developers (myself included) love: Pay once, own it forever.&lt;/p&gt;
&lt;p&gt;In the beginning, this looks like a rocket ship. You have a wide-open Total Addressable Market (TAM). Every sale is a massive injection of cash upfront. It feels like valid, sustainable growth.&lt;/p&gt;
&lt;p&gt;But financially, a lifetime license is a liability. You collect all the revenue you will &lt;em&gt;ever&lt;/em&gt; get from a customer on Day 1, but you commit to supporting them and updating the product forever.&lt;/p&gt;
&lt;p&gt;There is a finite number of frontend developers in the world. There is a smaller number who use Tailwind, and a smaller number still willing to pay $299 for UI components.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lifetime pricing was never going to be a good idea in any finite market.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;The tragedy of “enough”&lt;/h2&gt;
&lt;p&gt;This is the part that breaks my heart. Knowing the open-source ethos, the creators almost certainly looked at their early success and thought, &lt;em&gt;“This is enough.”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;They didn’t want to be the “greedy” corporation extracting a monthly toll from developers. They wanted to be the good guys. They likely looked at their bank balance, saw millions from the initial rush of lifetime sales, and calculated that they could run the company indefinitely on that pile + a trickle of new sales.&lt;/p&gt;
&lt;p&gt;The irony is brutal: had they been more “greedy,” they would be safe.&lt;/p&gt;
&lt;p&gt;If the Tailwind team had ignored its modest instincts and implemented a standard SaaS subscription model, it would currently have a recurring revenue floor.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;With subscriptions,&lt;/strong&gt; a 40% drop in traffic hurts growth, but the existing millions of users keep the lights on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With lifetime deals,&lt;/strong&gt; the existing millions of users are financially irrelevant. The company relies entirely on &lt;em&gt;new&lt;/em&gt; customers to survive.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By trying to be fair to their community, they stripped their business of the resilience it needed to survive a market shift.&lt;/p&gt;
&lt;h2&gt;The lesson&lt;/h2&gt;
&lt;p&gt;Dries is right that the market is shifting from “Specification” to “Operation”. He’s also right that AI is a business model stress-test. Maybe even more so than he puts in his words.&lt;/p&gt;
&lt;p&gt;Because for the next generation of founders, the lesson is starker. “Enough” is a concept that applies to personal finance, not corporate operations. A company needs flow, not just a pile.&lt;/p&gt;
&lt;p&gt;I want Tailwind to be around for another ten years. But for that to happen, they (and future founders) need to realize that sustainable revenue models aren’t about greed; they are about survival.&lt;/p&gt;
</content:encoded><category>Development</category><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>From installation to integration: Making plugins “agent-ready”</title><link>https://joost.blog/agent-ready-plugins/</link><guid isPermaLink="true">https://joost.blog/agent-ready-plugins/</guid><description>In my last post, I discussed why a design system is the “visual rail” AI needs. But the “Architect” I’ve been describing doesn’t just care about how a site look</description><pubDate>Wed, 24 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my &lt;a href=&quot;/vibe-coding-trap-design-system/&quot;&gt;last post&lt;/a&gt;, I discussed why a design system is the “visual rail” AI needs. But the “Architect” I’ve &lt;a href=&quot;/rise-architect/&quot;&gt;been describing&lt;/a&gt; doesn’t just care about how a site looks; they care about how it functions.&lt;/p&gt;
&lt;p&gt;The problem today is that even the smartest AI is often a “clueless” collaborator. You ask a coding assistant to “add a contact form to the sidebar,” and instead of using the powerful form plugin you already have installed, it starts writing a custom database table and a raw PHP &lt;code&gt;mail()&lt;/code&gt; function from scratch. It’s not being “creative”, it’s just unaware that the tools for the job are already sitting in your site’s plugins folder.&lt;/p&gt;
&lt;p&gt;To fix this, we have to stop treating plugins like “black boxes” and start treating them as &lt;strong&gt;self-advertising modules&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;The communication gap is (mostly) solved&lt;/h2&gt;
&lt;p&gt;The good news is that we already have the “telephone line” for this conversation. The WordPress AI team recently introduced the &lt;a href=&quot;https://make.wordpress.org/ai/2025/07/17/abilities-api/&quot;&gt;Abilities API&lt;/a&gt; and the &lt;a href=&quot;https://make.wordpress.org/ai/2025/07/17/mcp-adapter/&quot;&gt;MCP adapter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Abilities API creates a centralized registry where plugins can formally register their functionalities with well-defined schemas. This means that, once installed, a plugin can “hand a menu” to an AI. But for an Architect to plan a site, they need to see that menu &lt;em&gt;before&lt;/em&gt; the plugin is ever installed.&lt;/p&gt;
&lt;h2&gt;The discovery layer: &lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The emerging &lt;strong&gt;&lt;a href=&quot;https://agents.md/&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt; standard is the right choice for our ecosystem. It isn’t just a text file for a crawler; it’s a briefing document for the AI agents that will be doing the building.&lt;/p&gt;
&lt;p&gt;By placing an &lt;code&gt;AGENTS.md&lt;/code&gt; file in the root of a plugin, we provide a human-and-AI-readable introduction that links directly to a more technical &lt;strong&gt;&lt;code&gt;abilities.yml&lt;/code&gt;&lt;/strong&gt;. This YAML file acts as a static export of what the plugin’s registered abilities do, following the same schema as the Abilities API:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;abilities:
  - id: &quot;my-seo-plugin/analyze-content-seo&quot;
    label: &quot;Analyze content SEO&quot;
    description: &quot;Analyzes post content for SEO improvements.&quot;
    input_schema:
      type: &quot;object&quot;
      properties:
        post_id:
          type: &quot;integer&quot;
          description: &quot;The post identifier.&quot;
          required: true
    output_schema:
      type: &quot;number&quot;
      description: &quot;The SEO score in percentage.&quot;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this pairing, the Architect can scan a dozen plugins on a remote repository and know exactly which ones fit the site’s requirements, down to the exact data types required, without downloading a single byte of PHP.&lt;/p&gt;
&lt;h2&gt;Discovery via the FAIR.pm supply chain&lt;/h2&gt;
&lt;p&gt;This vision requires a trusted way to query and distribute this information. This is where &lt;a href=&quot;https://fair.pm/&quot;&gt;FAIR.pm&lt;/a&gt; comes in.&lt;/p&gt;
&lt;p&gt;If the FAIR network indexes these &lt;code&gt;AGENTS.md&lt;/code&gt; and &lt;code&gt;abilities.yml&lt;/code&gt; files, the global supply chain for WordPress becomes machine-readable. An Architect can query FAIR to ask: &lt;em&gt;“Find me a trusted package that exposes an ability to analyze a post’s SEO, preferably with an output schema of ‘percentage’.”&lt;/em&gt; FAIR provides both the trust and the map, while the &lt;code&gt;AGENTS.md&lt;/code&gt; ecosystem provides the technical contract.&lt;/p&gt;
&lt;p&gt;Of course, this could be done on the WordPress.org plugin directory too, and it would be nice if it did, but that currently does not include any commercial plugins. FAIR is planning on allowing for &lt;em&gt;all&lt;/em&gt; plugins from every host, which would make this a lot more inclusive of every plugin out there.&lt;/p&gt;
&lt;h2&gt;The “agent-first” developer experience (AX)&lt;/h2&gt;
&lt;p&gt;For twenty years, plugin developers have obsessed over the user experience (UX). In the “Rise of the Architect” era, developers must at least &lt;em&gt;also&lt;/em&gt; prioritize the agent experience (AX).&lt;/p&gt;
&lt;p&gt;If we don’t create a standard like this, plugins may provide a beautiful settings page, but they are invisible to the modern architect. We have to find a way for plugins to be considered in AI assembly/development processes &lt;em&gt;before&lt;/em&gt; they’re installed.&lt;/p&gt;
&lt;h2&gt;Conclusion: legibility is the new portability&lt;/h2&gt;
&lt;p&gt;Plugins aren’t going away, but they are changing from being “destination apps” inside the WordPress admin to being “service providers” for an AI-driven assembly process.&lt;/p&gt;
&lt;p&gt;By adopting a shared manifest of capabilities and plugging into decentralized networks like FAIR.pm, we ensure that the WordPress ecosystem remains a coherent library of tools, even if they’re spread across many different repositories. If we give the AI a map of our abilities, it can finally stop reinventing the wheel and start helping us build the house we want using already existing parts.&lt;/p&gt;
</content:encoded><category>Development</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Vibe coding is a trap: why WordPress needs a design system NOW</title><link>https://joost.blog/vibe-coding-trap-design-system/</link><guid isPermaLink="true">https://joost.blog/vibe-coding-trap-design-system/</guid><description>In my last post, I argued that WordPress needs to become a “Base AI”: a structured foundation that AI can understand and build upon. Before that, I wrote about</description><pubDate>Tue, 23 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my &lt;a href=&quot;/wordpress-architecture-base-ai/&quot;&gt;last post&lt;/a&gt;, I argued that WordPress needs to become a “Base AI”: a structured foundation that AI can understand and build upon. Before that, I wrote about &lt;a href=&quot;/rise-architect/&quot;&gt;The Rise of the Architect&lt;/a&gt;, the person who will shift from writing code to orchestrating systems.&lt;/p&gt;
&lt;p&gt;But there is a massive obstacle standing in the way of this future: WordPress is currently a “system” without a shared language.&lt;/p&gt;
&lt;p&gt;We are seeing this play out in real-time with tools like &lt;a href=&quot;https://telex.automattic.ai/&quot;&gt;Telex&lt;/a&gt;. It’s a glimpse into “Vibe Coding”, the ability to describe a UI and watch the AI manifest it. It’s seductive. It’s fun. And if we aren’t careful, it’s going to lead to a maintenance nightmare that makes the “spaghetti code” of the 2000s look like a masterpiece.&lt;/p&gt;
&lt;h2&gt;The seduction of the “vibe”&lt;/h2&gt;
&lt;p&gt;Tools like Telex allow you to bypass the traditional development process. You want a pricing table with a specific “bounce”? You describe it, the AI generates the React and CSS, and it appears.&lt;/p&gt;
&lt;p&gt;To a “Site Builder” persona, this might feel like magic. To an Architect, this feels like a ticking time bomb.&lt;/p&gt;
&lt;p&gt;The problem with “vibe-coded” blocks is that they are disposable code. They are isolated islands of logic and style. When an AI generates 20 different blocks and plugins for your site based on 20 different prompts, you haven’t built a website; you’ve built 20 independent technical debt projects.&lt;/p&gt;
&lt;p&gt;If you decide to change your brand’s primary blue or adjust your site’s spacing logic, you can’t just update a central variable. You have to “re-vibe” the entire site.&lt;/p&gt;
&lt;h2&gt;The missing foundation: a design system&lt;/h2&gt;
&lt;p&gt;This brings me back to a point I’ve &lt;a href=&quot;/wordpress-admin-ui-needs-to-be-better/&quot;&gt;made before&lt;/a&gt;: WordPress desperately needs a unified design system.&lt;/p&gt;
&lt;p&gt;For years, the lack of a standardized Admin UI has hampered WordPress. Every plugin developer had to invent their own buttons, toggles, and layout logic. This was already a bad user experience for humans, but it’s a catastrophic “architectural” failure for AI.&lt;/p&gt;
&lt;p&gt;An AI doesn’t need to be “creative” with your layout; it needs constraints.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Without a design system:&lt;/strong&gt; AI is like a toddler with a bucket of clay. It can mold anything, but nothing it makes will ever fit perfectly with the next thing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;With a design system:&lt;/strong&gt; AI is an Architect with a box of Legos. Everything snaps together because the rules, the tokens for spacing, color, and typography, are pre-defined.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;System-first vs. prompt-first&lt;/h2&gt;
&lt;p&gt;The industry is currently obsessed with “prompt-first” development (Telex). But the “Architect” I described in my earlier posts knows that the future must be “system-first.”&lt;/p&gt;
&lt;p&gt;In a system-first world, the AI doesn’t write the CSS for a button. Instead, the AI &lt;em&gt;selects&lt;/em&gt; the “Primary button” component from the WordPress Design System and configures it. The “Base AI” ensures that the output is predictable, maintainable, and, most importantly, updatable.&lt;/p&gt;
&lt;p&gt;If WordPress remains a collection of “vibes” rather than a rigid system of components, we are effectively building a web of disposable components. We are trading the long-term health of our sites for the short-term high of watching an AI generate a pretty block in ten seconds.&lt;/p&gt;
&lt;h2&gt;The choice&lt;/h2&gt;
&lt;p&gt;We are at a fork in the road.&lt;/p&gt;
&lt;p&gt;We can follow the path of unstructured generation, where AI fills WordPress with an endless stream of unmaintainable, bespoke code. Or, we can finally do the hard work of building a robust design system that provides the “rails” the AI needs. Of course, we’d then also have to ensure plugins and themes use that design system, but I think that with a good carrot approach (in which we promote those that use the design system), we can go a long way.&lt;/p&gt;
&lt;p&gt;If we want the “Rise of the Architect” to be a success, we need to stop asking AI to be a painter and start asking it to be an assembler. But first, we have to give it the right base parts.&lt;/p&gt;
</content:encoded><category>Development</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The generalization tax: why WordPress is still the smart architectural base</title><link>https://joost.blog/wordpress-architecture-base-ai/</link><guid isPermaLink="true">https://joost.blog/wordpress-architecture-base-ai/</guid><description>In my previous post, I discussed the demise of code copyright and mentioned what Dries referred to as the generalization tax. This had me thinking more about wh</description><pubDate>Mon, 22 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In my previous post, I discussed &lt;a href=&quot;/rise-architect/&quot;&gt;the demise of code copyright&lt;/a&gt; and mentioned what Dries referred to as the generalization tax. This had me thinking more about what that means for WordPress and investing in its ecosystem.&lt;/p&gt;
&lt;p&gt;For twenty years, the winning strategy in software was to build the “base layer”. WordPress won this race by becoming the engine for 40%+ of the web. It succeeded by being everything to everyone.&lt;/p&gt;
&lt;p&gt;But as AI code generation matures, a new marketing threat is emerging. It is the promise of the “Bespoke Engine”. Why deal with the overhead of WordPress when an AI can generate a lean, custom-built solution just for your specific needs?&lt;/p&gt;
&lt;h3&gt;The two sides of the bet: Bull vs. Bear&lt;/h3&gt;
&lt;p&gt;In the world of investing, your outlook on a market is usually described as being either “Bullish” or “Bearish.”&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The Bear case (pessimistic):&lt;/strong&gt; A “bear” believes a market or product is headed for a decline. The Bear Case for WordPress is that it has become too heavy. It pays a massive “Generalization Tax” where hundreds of developers spend thousands of hours writing code to handle edge cases for millions of users. The Bear argues that AI will allow developers to bypass this bloat entirely by building tiny, custom engines from scratch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The Bull case (optimistic):&lt;/strong&gt; A “bull” believes a market will grow or remain dominant. The Bull Case for WordPress is that its massive scale is its greatest security. Because it is used by 40% of the web, its core logic is battle-tested in a way that no custom AI engine could ever be.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Why I am a proponent of the bull case&lt;/h3&gt;
&lt;p&gt;I lean toward the Bull case, but not because I want to protect the old way of doing things. I believe WordPress is the superior long-term option because it is the only way to prevent “AI Slop”.&lt;/p&gt;
&lt;p&gt;Using WordPress (along with robust tools like ACF) as a base for custom applications is a sophisticated architectural choice. If you let an AI generate a “lean, custom engine” from scratch today, you are creating a massive maintenance liability for tomorrow. Without a standardized base, that AI-generated code will eventually become “slop”: unpatchable, insecure, and disconnected from the global web standards.&lt;/p&gt;
&lt;p&gt;The WordPress bull case is actually about risk mitigation:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;The standardized logic:&lt;/strong&gt; WordPress handles the “boring” but critical logic (Users, Auth, Database, Security) that has been battle-tested over two decades.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The battle-tested infrastructure:&lt;/strong&gt; By using a generalized base for basic needs, you ensure that your custom features are built on a foundation that receives global security updates.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The architect’s canvas:&lt;/strong&gt; The &lt;a href=&quot;/rise-architect/&quot;&gt;modern architect&lt;/a&gt; might use AI to build high-value custom plugins on top of this base, rather than wasting time reinventing the wheel.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;The marketing threat: “architecture” vs. “bespoke”&lt;/h3&gt;
&lt;p&gt;Even if the “bespoke” promise is technically fragile, the WordPress community needs a coherent answer to this marketing narrative. Right now, it doesn’t have one.&lt;/p&gt;
&lt;p&gt;The industry is currently being told that “custom is faster.” We need to tell the story of the modern architect. In this story, the architect doesn’t spend time on the “bricks” of a login screen or a database schema. They use WordPress as their “base” to ensure their vision doesn’t turn into a technical debt nightmare three years down the road.&lt;/p&gt;
&lt;h3&gt;Conclusion: investing in maintainable scale&lt;/h3&gt;
&lt;p&gt;From an investment perspective, the value isn’t just in the market share. It is in a maintainable scale. The risk of the “AI-bespoke” movement is that it creates a fragmented web of one-off systems that will fail the moment the AI model or the underlying language evolves.&lt;/p&gt;
&lt;p&gt;The “Generalization Tax” is real, but it is a price worth paying for a foundation that won’t crumble. If WordPress wants to win the next decade, it has to move from selling “the website builder” to selling &lt;strong&gt;“the foundation for AI-driven website architecture”&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Read my next post to understand why I think &lt;a href=&quot;/vibe-coding-trap-design-system/&quot;&gt;vibe coding is a trap&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Development</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The death of Code Copyright (and the rise of the Architect)</title><link>https://joost.blog/rise-architect/</link><guid isPermaLink="true">https://joost.blog/rise-architect/</guid><description>We are witnessing a strange paradox in software development. Thanks to AI code generation, more open source code is being created today than ever before. Yet, s</description><pubDate>Sun, 21 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are witnessing a strange paradox in software development. Thanks to AI code generation, more open source code is being created today than ever before. Yet, simultaneously, the value of a single line of code has never been lower.&lt;/p&gt;
&lt;p&gt;The commons (the vast library of open source projects that allowed AI models to learn how to code in the first place) is now being disrupted by the very technology it enabled.&lt;/p&gt;
&lt;h3&gt;The erosion of the license&lt;/h3&gt;
&lt;p&gt;In the traditional open source model, licenses like the GPL or MIT protect the expression of an idea. If you used someone’s code, you followed their rules. But AI has created a massive loophole.&lt;/p&gt;
&lt;p&gt;When code can be rebuilt from scratch in seconds, the copyright on the specific syntax does not matter much. If a developer uses AI to replicate your tool’s exact functionality without copying a single line of your original source, they haven’t “stolen” your code in a legal sense. They have “developed it themselves”.&lt;/p&gt;
&lt;p&gt;This makes building small utility pieces of software less interesting as a way of making money. These tools are now so easily copied that attribution and protection are nearly impossible to enforce. You do not need to attribute if you did not use the code, even if you fully stole the idea and the functionality.&lt;/p&gt;
&lt;h3&gt;The Architect’s toolkit: intent, direction &amp;amp; logic&lt;/h3&gt;
&lt;p&gt;If the code itself isn’t worth much, what is? We are seeing the value move upstream, away from the manual labor of coding and toward the high-level design of the solution.&lt;/p&gt;
&lt;p&gt;In the past, a developer was often a glorified bricklayer. Success was found in the manual labor of laying down stable, reusable bricks of code. But when AI can manufacture and lay those bricks instantly, the value of the bricklayer vanishes.&lt;/p&gt;
&lt;p&gt;The value now lies in the Architect. This new role isn’t about writing syntax. It is about providing the three things AI cannot yet originate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Intent:&lt;/strong&gt; Defining the “why.” Identifying exactly what problem needs solving and why it matters to the user.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Direction:&lt;/strong&gt; Steering the solution. Knowing which path to take when AI offers infinite variations and ensuring the project stays true to its goal.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Logic:&lt;/strong&gt; The structural “how.” Designing the underlying flow and logic that ensures separate systems work together as a cohesive whole.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The irony is that these architectural components (the soul of the software) cannot be protected by open source licenses or copyright. We are moving into an era where intent is everything, and execution is a commodity.&lt;/p&gt;
&lt;h3&gt;The value of the public domain&lt;/h3&gt;
&lt;p&gt;While the legal protection of a license is fading, the fact that code lives in the public domain matters more than ever. This vast, shared library of logic is what allows us all to develop faster. We are no longer starting from zero. We are starting from a collective baseline of human knowledge that AI has made instantly accessible.&lt;/p&gt;
&lt;p&gt;By removing the friction of syntax and boilerplate, developers are free to have much grander ideas. We can now build complete, complex solutions much faster than we could five years ago. We are no longer limited by how many bricks we can lay in a day. We are only limited by the scale of our vision.&lt;/p&gt;
&lt;h3&gt;The “Dries” model: code as a blueprint&lt;/h3&gt;
&lt;p&gt;A perfect example of this shift comes from Dries Buytaert, the founder of Drupal. In a &lt;a href=&quot;https://dri.es/adaptable-drupal-modules-code-meant-to-be-adapted-not-installed&quot;&gt;recent blog post&lt;/a&gt;, Dries discussed “Adaptable Modules.” These are site-specific pieces of code that are not meant to be installed, but rather used as a starting point for AI to reshape.&lt;/p&gt;
&lt;p&gt;Dries is sharing his architectural choices without worrying about what he calls the “generalization tax,” which is the massive effort required to make code work for everyone. He provides the blueprint (his logic and direction) and lets AI handle the manual labor of adapting it to a specific site.&lt;/p&gt;
&lt;p&gt;In this new world, the win is not in protecting the bricks or ensuring your name is on every derivative work. The win is in the proliferation of the solution. Dries might not even care about getting recognition when his code is repurposed, but that is a luxury of the established.&lt;/p&gt;
&lt;h3&gt;Vision is the new competitive edge&lt;/h3&gt;
&lt;p&gt;For the developer just starting out today, the landscape has shifted permanently. The protection once offered by copyright is dissolving into a sea of AI-generated syntax.&lt;/p&gt;
&lt;p&gt;The rise of the architect means that your value no longer lives in the files you commit to GitHub. It lives in your ability to define intent, maintain direction, and master the logic of a solution.&lt;/p&gt;
&lt;p&gt;You can no longer compete on the bricks. To survive and thrive in this new era, you have to compete on vision.&lt;/p&gt;
&lt;p&gt;Read my next post about &lt;a href=&quot;/wordpress-architecture-base-ai/&quot;&gt;the generalization tax&lt;/a&gt;, and why I still think WordPress is a great base for AI.&lt;/p&gt;
</content:encoded><category>Development</category><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>A new path forward for WordPress, and for the open web</title><link>https://joost.blog/path-forward-for-wordpress/</link><guid isPermaLink="true">https://joost.blog/path-forward-for-wordpress/</guid><description>In December, I wrote about the state of leadership in the WordPress ecosystem. I shared how too much power rests with one person, and how the lack of transparen</description><pubDate>Fri, 06 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In December, I wrote about the &lt;a href=&quot;/wordpress-leadership/&quot;&gt;state of leadership in the WordPress ecosystem&lt;/a&gt; and &lt;a href=&quot;/wordpress-roadmap/&quot;&gt;what should be on WordPress&apos;s roadmap&lt;/a&gt;. I shared how too much power rests with one person, and how the lack of transparent governance puts contributors and businesses alike in difficult positions. That post ended with a call: &lt;em&gt;we need to lead&lt;/em&gt;. That wasn’t rhetorical. It was a pivot.&lt;/p&gt;
&lt;p&gt;Since then, a lot has happened. Today, I want to share what came next and where it’s going. This is the story of &lt;strong&gt;FAIR&lt;/strong&gt;, a project I first named in that same blog post: &lt;strong&gt;Federated and Independent Repositories&lt;/strong&gt;. At the time, it was just a placeholder for an idea. Now, it’s real, and it’s running.&lt;/p&gt;
&lt;h3&gt;The moment the ecosystem shifted&lt;/h3&gt;
&lt;p&gt;In October, Sarah Savage &lt;a href=&quot;https://aspirepress.org/a-vision-for-aspirepress-and-a-community-run-org-mirror/&quot;&gt;published a post introducing AspirePress&lt;/a&gt;, a community-run mirror of the WordPress.org plugin and theme repositories. That post was a spark. It showed what was technically possible, and voiced what many of us had been saying in smaller rooms for years: the ecosystem needed options.&lt;/p&gt;
&lt;p&gt;Then, in early December, twenty core contributors &lt;a href=&quot;https://www.therepository.email/wordpress-contributors-and-community-leaders-call-for-governance-reform-in-rare-open-letter&quot;&gt;wrote an open letter&lt;/a&gt; calling for governance reform in WordPress. These weren’t newcomers. They were committers, team leads, people who had spent years helping build the platform. Their message added weight to the concerns many had been feeling in private.&lt;/p&gt;
&lt;p&gt;Shortly after, &lt;a href=&quot;https://marucchi.com/wordpress-leadership-continued/&quot;&gt;Karim Marucchi&lt;/a&gt; and I published our thoughts. We proposed a dual-track forward: one that addressed technical issues like centralization, update bottlenecks, and discoverability, and one that introduced a different approach to governance, governance that would be transparent, accountable, and neutral.&lt;/p&gt;
&lt;p&gt;That combination turned out to be a catalyst. Even more so after Matt &lt;a href=&quot;https://wordpress.org/news/2025/01/jkpress/&quot;&gt;blogged about it&lt;/a&gt; in a sarcastic post on WordPress.org. People began reaching out, and conversations happening in parallel started to overlap.&lt;/p&gt;
&lt;h3&gt;From parallel threads to shared momentum&lt;/h3&gt;
&lt;p&gt;Several groups had been working on related problems, each bringing different perspectives, needs, and capabilities. Rather than announce a new umbrella, we focused on connecting the dots between these parallel efforts. We’re not naming everyone in that alignment publicly yet, but it’s safe to say: this &lt;em&gt;is&lt;/em&gt; a group of groups, and that’s its strength.&lt;/p&gt;
&lt;p&gt;What followed were weeks of collaboration. We mapped out which parts of the ecosystem needed reinforcement. Some things were urgent: plugin update services, the plugin and theme directories, static assets like emojis and avatars, and the dashboard feeds. We began with mirrors and drop-in replacements, but the plan was always broader than that.&lt;/p&gt;
&lt;p&gt;We designed a system that could serve as a complete distribution layer for WordPress. It would work with the core as it exists today, stay compatible while removing the bottlenecks created by centralization, and be governed independently of .org.&lt;/p&gt;
&lt;h3&gt;FAIR: federated, independent, and live&lt;/h3&gt;
&lt;p&gt;FAIR is now a technical project under the Linux Foundation. Its technology is governed by a community-led Technical Steering Committee (TSC) and built by contributors from across the WordPress ecosystem. The TSC chose three amazing community leaders as its chairs: Carrie Dils, Mika Epstein, and Ryan McCue. Together, they built a decentralized package management system, federation-ready mirrors, support for commercial plugins, cryptographic signing, and more in a relatively short amount of time.&lt;/p&gt;
&lt;p&gt;The goal of FAIR is &lt;strong&gt;not&lt;/strong&gt; to fork WordPress. We’re still using the same core software. We’re not building a separate platform. We are adding a new distribution layer and putting our own governance on top of it. It’s a new path within WordPress, not outside it.&lt;/p&gt;
&lt;p&gt;You can still install WordPress from WordPress.org, and that won’t change. But if you want more control over how plugins are delivered, or a system that supports decentralization, FAIR gives you that choice.&lt;/p&gt;
&lt;p&gt;This work builds on tools like Composer and package managers from the Linux world, but with a clear focus on usability for real WordPress users. Most people won’t even know how it works under the hood. They’ll just know it works.&lt;/p&gt;
&lt;h3&gt;Why this matters&lt;/h3&gt;
&lt;p&gt;When I wrote about WordPress leadership, I meant it. Change in open source doesn’t happen from the outside. It occurs when people show up, do the work, and offer a better option. That’s what we’ve done.&lt;/p&gt;
&lt;p&gt;FAIR is not a protest. It’s not a fork. It is a &lt;strong&gt;contribution&lt;/strong&gt;. It reflects our belief that WordPress deserves better infrastructure and more accountable governance, and that we can build that together.&lt;/p&gt;
&lt;p&gt;This project results from months of collaboration across companies, countries, and communities. Dozens and dozens of people have already worked on it, and more are joining daily.&lt;/p&gt;
&lt;p&gt;You can learn more at &lt;strong&gt;&lt;a href=&quot;https://fair.pm&quot;&gt;fair.pm&lt;/a&gt;&lt;/strong&gt;. There’s plenty of work still ahead, but the foundations are in place, and the path is open.&lt;/p&gt;
&lt;p&gt;If you believe in the open web, in WordPress as shared infrastructure, and in a future where the people who contribute get a say in how the platform evolves, join us!&lt;/p&gt;
&lt;p&gt;Other people we’ve been collaborating with have blogged as well:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://marucchi.com/introducing-the-fair-package-manager-for-wordpress/&quot;&gt;Karim Marucchi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://journal.rmccue.io/488/building-a-stronger-ecosystem/&quot;&gt;Ryan McCue&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://siobhanmckeown.com/a-way-forward-with-fair/&quot;&gt;Siobhan McKeown&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Plus, the &lt;a href=&quot;https://www.linuxfoundation.org/press/linux-foundation-announces-the-fair-package-manager-project-for-open-source-content-management-system-stability&quot;&gt;press release from the Linux Foundation&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Innovation in WordPress: a look at plugin development</title><link>https://joost.blog/innovation-in-wordpress/</link><guid isPermaLink="true">https://joost.blog/innovation-in-wordpress/</guid><description>Recent observations have highlighted a significant surge in new plugin submissions to the WordPress repository, as noted in this post. We also know that Automat</description><pubDate>Thu, 05 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Recent observations have highlighted a significant surge in new plugin submissions to the WordPress repository, as noted in &lt;a href=&quot;https://make.wordpress.org/plugins/2025/05/21/the-wordpress-ecosystem-is-growing-new-plugin-submissions-have-doubled-in-2025/&quot;&gt;this post&lt;/a&gt;. We also know that Automattic recently “unpaused” their contributions, leading to some pretty critical articles like &lt;a href=&quot;https://www.searchenginejournal.com/wordpress-unpauses-development-but-has-it-run-out-of-time/548199/&quot;&gt;this one from Roger Montti&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The increase in plugin submissions got Marieke and me wondering about the relationship between plugin submission, actual plugin availability, and innovation within the WordPress ecosystem. As a result of all the recent changes, WordPress core releases can be somewhat sporadic. Marieke and I wanted to explore whether plugins are now the primary driver of innovation. Marieke took it upon herself to investigate how much plugin development contributes to WordPress’s evolution by going through heaps of changelogs, while I grabbed and analyzed a lot of data from WordPress.org.&lt;/p&gt;
&lt;p&gt;This is a different angle on a question I’ve explored before: what the &lt;a href=&quot;/unintended-consequences-seo-for-everyone/&quot;&gt;unintended consequences of making SEO accessible&lt;/a&gt; have been, and how WordPress’s &lt;a href=&quot;/transparency-contribution-and-the-future-of-wordpress/&quot;&gt;governance challenges&lt;/a&gt; affect what gets built.&lt;/p&gt;
&lt;h2&gt;Problem statement and research questions&lt;/h2&gt;
&lt;p&gt;While we see an increase in plugin submissions, it’s unclear if this translates to a proportional increase in plugin installations and overall platform innovation. Many plugins in the repository have minimal installations, suggesting a potential disconnect between plugin development and actual user adoption. With this in mind, we’re asking these research questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;RQ1:&lt;/strong&gt; To what extent do new WordPress plugins achieve downloads and active installations, and how do plugins contribute to the innovation of the WordPress platform?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RQ2:&lt;/strong&gt; Do WordPress plugins compensate for the lack of innovation within WordPress Core?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Preliminary data analysis&lt;/h2&gt;
&lt;p&gt;When we started digging into the data we gathered from WordPress.org, we found a more nuanced picture than the report suggested. While 2025 apparently shows a notable increase in plugin &lt;em&gt;submissions&lt;/em&gt;, historical data indicates peak plugin additions in 2015 and 2016 (see Figure 1). Remember that Figure 1 only accounts for plugins that were approved in those years and are still active in the repository; total plugin submissions for those years were likely higher.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/number-of-plugins-added-each-year-still-active.webp&quot; alt=&quot;Number of plugins added each year (which are still active)&quot; /&gt;Figure 1## Analysis of all plugins on WordPress.org&lt;/p&gt;
&lt;p&gt;To answer research question 1, we analyzed the number of active installs for plugins, grouped by their year of addition to WordPress.org. Figure 2 shows that plugins added in more recent years have far fewer active installs than plugins released years ago. Of course, plugins take some time to grow, so it’s logical that plugins released in 2025 have fewer average installs than those released in, say, 2015. But you might &lt;em&gt;also&lt;/em&gt; expect that plugins released &lt;em&gt;now&lt;/em&gt; are adding features that people need and would thus grow faster. That doesn’t seem to be happening though.&lt;/p&gt;
&lt;p&gt;It appears that it takes years for most plugins to reach a significant user base, if they ever reach it. Plugins released earlier might have had an outsized chance of acquiring users, while new plugins are basically never showing up in search results. Only the ones with external factors (like being recommended by a popular theme or plugin) grow quickly.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/average-count-of-active-installs-_-plugin-by-plugin-release-year.webp&quot; alt=&quot;Average count of active installs / plugin by plugin release year&quot; /&gt;Figure 2## Analysis of big plugins&lt;/p&gt;
&lt;p&gt;In order to further research our questions, we did an analysis with only the big plugins. In order to make some kind of impact on WordPress as an ecosystem, plugins need to have at least some users. That’s why we looked at all plugins with over 100,000 active installs. What we saw from that analysis is that only five of the big plugins were released in 2024, and just one in 2025. Again, it makes sense that plugins from recent years have fewer installs, but it does suggest that it is hard for new innovations to reach the WordPress userbase through the release of new plugins.&lt;/p&gt;
&lt;p&gt;At the end of this post, we’ve some more thoughts about why some new plugins do and some plugins don’t attract a substantial user base.&lt;/p&gt;
&lt;h2&gt;Analysis of established plugins&lt;/h2&gt;
&lt;p&gt;It appears that innovation does not come from new plugins. We’ve also established that it does not come from WordPress core. Established plugins maintain a dominant presence in the market, potentially due to longer market tenure, branding efforts, and security hardening.&lt;/p&gt;
&lt;p&gt;We’ve looked at the distribution of large plugins and analyzed to what extent plugin usage is linked to the bigger, more established plugins. In our analysis (results in table 1) we researched to what extent the total number of installs (of all plugins across the ecosystem combined) could be accounted for by the larger plugins.&lt;/p&gt;
&lt;p&gt;Table 1 reads as follows. In the first column, you’ll read the group of plugins (for example plugins with more than 10 million installs). In the second column, you’ll find the number of plugins in that group, in the third column, you’ll find the number of users from that group and the groups above combined. You can see the cumulative sum of the plugins and the % of installs they have (of the total number of installs throughout all WordPress sites).&lt;/p&gt;
&lt;p&gt;Table 1 highlights the concentration of active installations within a few major plugins. This implies that innovation efforts are likely to have the greatest impact within these widely adopted plugins, rather than in the long tail of less installed plugins.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Group&lt;/strong&gt;**#&lt;strong&gt;&lt;strong&gt;Cum. #&lt;strong&gt;&lt;strong&gt;Cum. sum&lt;/strong&gt;&lt;/strong&gt;% installs&lt;/strong&gt;&lt;/strong&gt;% plugins****Plugins with &amp;gt; 10M installs**4440,000,00011.52%0.01%&lt;strong&gt;Plugins with &amp;gt; 5M installs&lt;/strong&gt;71182,000,00023.61%0.02%&lt;strong&gt;Plugins with &amp;gt; 2M installs&lt;/strong&gt;2132137,000,00039.45%0.05%&lt;strong&gt;Plugins with &amp;gt; 1M installs&lt;/strong&gt;3769174,000,00050.10%0.11%&lt;strong&gt;Plugins with &amp;gt; 500k installs&lt;/strong&gt;66135215,900,00062.17%0.21%&lt;strong&gt;Plugins with &amp;gt; 100k installs&lt;/strong&gt;361496276,300,00079.56%0.78%&lt;strong&gt;Plugins with &amp;gt; 10k installs&lt;/strong&gt;1,9712,467327,120,00094.20%3.86%&lt;strong&gt;Plugins with &amp;gt; 1k installs&lt;/strong&gt;5,6208,087343,199,00098.83%12.65%&lt;strong&gt;Other plugins&lt;/strong&gt;52,60382.30%Table 1&lt;/p&gt;
&lt;p&gt;You’re reading that correctly: a group of 4 plugins (less than 0.01% of all plugins) is responsible for about 12 % all plugins installed on WordPress sites worldwide. And, 69 plugins (all having more than a million installs) account for half of the total installs. The usage of plugins in WordPress is highly skewed towards big, well-established plugins.&lt;/p&gt;
&lt;p&gt;This leads to the following conclusion: if the big plugins have the most users, then they are the ones that could (and should) drive WordPress innovation. And not the small plugins that hardly anyone uses (yet). So, we need to investigate how these big WordPress plugins are doing in terms of innovation.&lt;/p&gt;
&lt;h3&gt;Analysis of changelogs&lt;/h3&gt;
&lt;p&gt;We’ve come to the next step of our analysis, which contains a lot of dull research work. This research employs an analysis of plugin changelogs to assess the extent of innovation within big plugins. Marieke spent hours and hours going through changelogs and classifying them. Examining the release notes for plugins included in this study, changes such as “new features” and “enhancements” are documented over time. It is acknowledged that the terminology used in changelogs varies. Therefore, this methodology is primarily for longitudinal intra-plugin comparison (so with themselves) rather than inter-plugin comparison (with each other).&lt;/p&gt;
&lt;p&gt;This is not scientific research (at all). Marieke tried to look at different big plugins while using her background knowledge as a researcher and a WordPress person. We chose plugins that are well-known and that we know well, while trying to get different plugins from different areas. We could have chosen different ones. Marieke wanted to include Learndash too, but couldn’t parse their changelog. Consider this a deep dive into what important brands in the WordPress world do innovation-wise. It’s not complete, but it indicates what’s happening.&lt;/p&gt;
&lt;h2&gt;Plugin innovation&lt;/h2&gt;
&lt;h3&gt;The Big Automattic Plugins&lt;/h3&gt;
&lt;p&gt;Looking at WooCommerce, we see they released many new features from 2021 onwards. However, this year they seem to be behind schedule. Perhaps they’re planning something significant, but it does look a bit concerning for 2025.&lt;/p&gt;
&lt;p&gt;Also, for Jetpack, 2024 was a low point since 2019, and 2025 isn’t looking much better regarding new or enhanced features.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/woocommerce-additions-and-enhancements-1.webp&quot; alt=&quot;WooCommerce additions and enhancements over time.&quot; /&gt;    &lt;img src=&quot;./images/jetpack-major-enhancements-and-enhancements-1.webp&quot; alt=&quot;Jetpack major enhancements per year&quot; /&gt;    ### The SEO plugins&lt;/p&gt;
&lt;p&gt;The three major SEO plugins, Yoast SEO, Rank Math, and All in One SEO (AIOSEO), are substantial projects with large codebases and extensive functionality. They account for over 16 million active installations according to .org’s public numbers (Yoast SEO: 10+ million, Rank Math and AIOSEO: 3+ million each).&lt;/p&gt;
&lt;p&gt;Since 2021, Yoast SEO has introduced fewer enhancements, with a notable decline in 2024, and early indications for 2025 suggest a continued downturn. Rank Math exhibits a similar trend, with a marked slowdown in innovation during 2024 and 2025. In contrast, AIOSEO experienced a surge in new features in 2024, but projections for 2025 (based on changelogs so far) also indicate a significant decline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/all-in-one-seo-pack-new-features.webp&quot; alt=&quot;All in One SEO Pack new features per year&quot; /&gt;    &lt;img src=&quot;./images/rankmath-additions-and-improvements.webp&quot; alt=&quot;Rank Math additions and improvements per year&quot; /&gt;    &lt;img src=&quot;./images/yoast-seo-enhancements-and-yoast-seo-premium-enhancements.webp&quot; alt=&quot;Yoast SEO and Yoast SEO Premium enhancements per year&quot; /&gt;    ## Other notable plugins&lt;/p&gt;
&lt;p&gt;Marieke then further analyzed the innovation trends of five other notable plugins, revealing a mixed picture. WP Rocket and Elementor have demonstrated steady, consistent innovation, although 2025 shows a slight slowdown, possibly indicating an upcoming major release. Contact Form 7 has exhibited a gradual decline in innovation since 2022. Easy Digital Downloads (EDD) experienced a growth in new features during 2024, yet 2025 appears to lag behind. GiveWP, on the other hand, has maintained a relatively stable pace of innovation. While 2025 shows a dip, this pattern mirrors 2022, and innovation rebounded in 2023.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/edd-new-features-and-improvements.webp&quot; alt=&quot;Easy Digital Downloads new features and improvements per year&quot; /&gt;    &lt;img src=&quot;./images/elementor-new-features-and-tweaks-1.webp&quot; alt=&quot;Elementor new features and tweaks per year&quot; /&gt;    &lt;img src=&quot;./images/givewp-new-features-and-enhancements-1.webp&quot; alt=&quot;GiveWP new features and enhancements per year&quot; /&gt;    &lt;img src=&quot;./images/wp-rocket-new-features-and-enhancements-1.webp&quot; alt=&quot;WP Rocket new features and enhancements per year&quot; /&gt;    &lt;img src=&quot;./images/contact-form-7-major-changes.webp&quot; alt=&quot;Contact Form 7 major changes per year&quot; /&gt;    ## What does this mean?&lt;/p&gt;
&lt;p&gt;The current state of innovation within the WordPress ecosystem is concerning. Core updates have slowed, and contributions from key players are either leveling or even declining.&lt;/p&gt;
&lt;p&gt;While new plugins could theoretically drive innovation, they tend to have limited adoption and install bases. Meanwhile, established plugins, particularly in the SEO space, are not showing signs of renewed innovation; in fact, many are introducing fewer updates than in previous years, and the outlook for 2025 is not promising. This stagnation poses a risk to WordPress’s competitive position in the broader market.&lt;/p&gt;
&lt;h3&gt;Why don’t new plugins find new audiences?&lt;/h3&gt;
&lt;p&gt;As said, only 5 plugins in 2024 and 1 plugin in 2025 have crossed the 100,000 installs mark. These plugins are:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Released&lt;strong&gt;&lt;strong&gt;Active installs&lt;/strong&gt;&lt;/strong&gt;Plugin****Built &amp;amp; promoted by&lt;/strong&gt;20241,000,000+&lt;a href=&quot;https://wordpress.org/plugins/image-optimization/&quot;&gt;Image Optimizer&lt;/a&gt;Elementor2024500,000+&lt;a href=&quot;https://wordpress.org/plugins/woocommerce-legacy-rest-api/&quot;&gt;WooCommerce Legacy REST API&lt;/a&gt;WooCommerce2024200,000+&lt;a href=&quot;https://wordpress.org/plugins/omnisend/&quot;&gt;Omnisend&lt;/a&gt;Omnisend2024200,000+&lt;a href=&quot;https://wordpress.org/plugins/sureforms/&quot;&gt;SureForms&lt;/a&gt;Astra / Brainstorm Force2024100,000+&lt;a href=&quot;https://wordpress.org/plugins/site-mailer/&quot;&gt;Site Mailer&lt;/a&gt;Elementor2025100,000+&lt;a href=&quot;https://wordpress.org/plugins/suremails/&quot;&gt;SureMails&lt;/a&gt;Astra / Brainstorm ForceAll of these plugins reached their size because of either an enormous marketing effort (in the case of Omnisend) or because they were promoted by other major plugins or themes.&lt;/p&gt;
&lt;p&gt;The WooCommerce legacy REST API plugin restores a feature that WooCommerce removed. As of this writing, it hasn’t been tested with the last three major releases of WordPress, which means it was released and then not maintained anymore.&lt;/p&gt;
&lt;p&gt;With so many plugins available and many of them not offering excellent quality, how are users supposed to find the truly innovative, useful ones? That’s why we need to do a better job promoting high-quality, innovative plugins in the repository and at events like WordCamps. We should share ideas, show each other how we’re improving WordPress, and celebrate our wins as a community.&lt;/p&gt;
&lt;p&gt;Bigger plugins with large user bases and solid business models should also be encouraged to keep innovating, perhaps even by teaming up with new, promising plugins. Hosting providers could play a key role too, by identifying great plugins and helping their customers discover them.&lt;/p&gt;
&lt;p&gt;Together, we can ensure WordPress keeps growing and stays an excellent platform for everyone.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The unintended consequences of making SEO “for everyone”</title><link>https://joost.blog/unintended-consequences-seo-for-everyone/</link><guid isPermaLink="true">https://joost.blog/unintended-consequences-seo-for-everyone/</guid><description>At Yoast, we had one mission: make SEO easier. For a long time, SEO for everyone was Yoast’s tagline, and we meant it. We helped millions of people optimize the</description><pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At Yoast, we had one mission: make SEO easier. For a long time, &lt;strong&gt;SEO for everyone&lt;/strong&gt; was Yoast’s tagline, and we meant it. We helped millions of people optimize their content. We made technical SEO more accessible. We gave small businesses, bloggers, and creators a real chance to be found online.&lt;/p&gt;
&lt;p&gt;And we were successful. But that success came with side effects. In trying to democratize SEO, we also helped shape habits that led to a web full of “optimized” content, not all of it valuable.&lt;/p&gt;
&lt;h2&gt;What we got right&lt;/h2&gt;
&lt;p&gt;A lot of what we did still matters. The readability checks in Yoast SEO were Marieke’s idea, rooted in her belief that to get as big an audience as possible for your ideas, you need to make them more easily understood. We hired a team of language scientists to research and build them. They were grounded in years of academic work on what makes text understandable, including &lt;a href=&quot;https://yoast.com/the-sentence-length-check/&quot;&gt;shorter sentences&lt;/a&gt;, clear &lt;a href=&quot;https://yoast.com/transition-words-why-and-how-to-use-them/&quot;&gt;transitions&lt;/a&gt;, and &lt;a href=&quot;https://yoast.com/the-passive-voice-what-is-it-and-how-to-avoid-it/&quot;&gt;active voice&lt;/a&gt;. These principles are still incredibly relevant, especially in an AI-powered world.&lt;/p&gt;
&lt;p&gt;For instance, we also &lt;a href=&quot;https://yoast.com/paragraph-length-check/&quot;&gt;talked about paragraphs needing to be about one topic&lt;/a&gt; (and not too long), with a clear use of headings throughout. SEOs now talk about this and then call it “chunking.” They talk about writing for LLMs as much as for people. But the truth is, this was always about helping readers make sense of content. And it still works.&lt;/p&gt;
&lt;p&gt;We also introduced inclusive language checks. That wasn’t about SEO rankings, it was about making the web, and your content, more welcoming. And that still feels like one of the most valuable additions we made.&lt;/p&gt;
&lt;h3&gt;The deeper foundation&lt;/h3&gt;
&lt;p&gt;Some of the best things we built were grounded in ideas older than SEO itself. The structure of a good site comes straight from librarianship, from research on findability and classification, and from the craft of organizing knowledge so others can discover it.&lt;/p&gt;
&lt;p&gt;Those principles guided how we thought about internal linking, content hierarchies, and sitemaps. They’ve lasted because they weren’t based on trends. They were based on how people find and make sense of information.&lt;/p&gt;
&lt;h2&gt;What we didn’t see coming&lt;/h2&gt;
&lt;p&gt;But not everything we did aged well. We turned SEO into a checklist. Our traffic light system, meant to encourage good habits, became a finish line. People wrote to get the green light. They optimized, published, and moved on. Sometimes they were writing with purpose. But sometimes, they were just filling in the blanks.&lt;/p&gt;
&lt;p&gt;We encouraged more content, more optimized content. And we made it easy, maybe too easy. That led to a flood of articles that hit the right technical notes, but didn’t always add real value. In our effort to make SEO accessible, we also helped normalize quantity over quality.&lt;/p&gt;
&lt;p&gt;We did try to combat the over-optimization; we introduced keyword distribution checks to see if you stayed on topic. We warned against having a keyword density that was too high. But, in hindsight, I don’t think we did so strong enough, for which I’m sorry.&lt;/p&gt;
&lt;h2&gt;The myths we helped create&lt;/h2&gt;
&lt;p&gt;Here’s a perfect example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Articles should be at least 300 words, because Google wants that.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Google never said that. &lt;strong&gt;I said that.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There was some logic to it. Most algorithms need a little content and context to understand a topic. But it wasn’t deeply researched. It was a practical rule of thumb that got repeated until it became gospel.&lt;/p&gt;
&lt;p&gt;And suddenly, people were writing to hit a number. Not because their story or idea needed that much space, but because the plugin suggested it.&lt;/p&gt;
&lt;h2&gt;The tide has turned&lt;/h2&gt;
&lt;p&gt;Today, that old playbook no longer works. Publish often and optimize hard? That approach is outdated. At the recent SMX Munich conference, half the talks were about pruning content, removing over-optimized articles, and merging similar articles into fewer, stronger ones.&lt;/p&gt;
&lt;p&gt;It’s now clear to everyone there, it seems, that search engines and AI systems favor clarity, structure, and originality over repetition and keyword stuffing. The focus has shifted from more content to better content.&lt;/p&gt;
&lt;p&gt;And I think that’s the right direction.&lt;/p&gt;
&lt;h2&gt;What I’ve learned&lt;/h2&gt;
&lt;p&gt;The tools we build shape the web. They create incentives, workflows, and habits, some good, some not.&lt;/p&gt;
&lt;p&gt;The parts of our strategy that stood the test of time were the ones grounded in real research: readability, findability, and structure. Not the tricks or shortcuts. The fundamentals. The stuff that just makes people build good websites. That’s where I’m focused now.&lt;/p&gt;
&lt;h2&gt;Where I’m going from here&lt;/h2&gt;
&lt;p&gt;With &lt;a href=&quot;https://progressplanner.com&quot;&gt;Progress Planner&lt;/a&gt;&lt;strong&gt;,&lt;/strong&gt; we’re building tools that support thoughtful website SEO strategies. Tools that don’t just help you publish, but help you manage what you’ve already written. Tools that reflect the full life cycle of content: writing, improving, and pruning.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://progressplanner.com/progress-planners-new-integration-with-yoast/&quot;&gt;previous release&lt;/a&gt;, we added nudges that’ll help you set up Yoast SEO better, configuring things that matter, but that we know people often get wrong. We plan to do this for more plugins.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;https://progressplanner.com/why-writing-more-content-isnt-always-better/&quot;&gt;upcoming release&lt;/a&gt;, we’ll remove features that reward volume for volume’s sake. We’re doubling down on usefulness, clarity, and long-term website performance.&lt;/p&gt;
&lt;p&gt;Because SEO for everyone is still a goal worth pursuing. But it has to be the right kind of SEO.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Build websites like it’s 2005 (and win in 2025)</title><link>https://joost.blog/build-websites-like-2005/</link><guid isPermaLink="true">https://joost.blog/build-websites-like-2005/</guid><description>Last week was a whirlwind, first diving deep into AI and WordPress while working with the WP CLI as MCP host team at Cloudfest, then heading off to SMX Munich f</description><pubDate>Wed, 26 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last week was a whirlwind, first diving deep into AI and WordPress while working with the WP CLI as MCP host team at Cloudfest, then heading off to SMX Munich for non-stop conversations about SEO, AI, and the future of search. Two very different settings, one clear takeaway:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The web has come full circle.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Despite all the advancements in AI, one theme kept surfacing again and again:&lt;br /&gt;
&lt;strong&gt;These systems still struggle to understand the web.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s not just about bad content or spammy sites. Even well-meaning, beautifully designed modern websites are often invisible to AI—and even to traditional search engines—because of how they’re built.&lt;/p&gt;
&lt;p&gt;That might sound surprising in 2025, but it really shouldn’t be.&lt;/p&gt;
&lt;p&gt;See, for example, &lt;a href=&quot;https://x.com/pootlepress/status/1904216642302738586&quot;&gt;this new experiment&lt;/a&gt; by Jamie Marsland. He built a headless site with Lovable using WordPress as a backend. My immediate response to his tweet was loading that URL in ChatGPT and it clearly showed it could not see &lt;em&gt;any&lt;/em&gt; of the contents of that page, which I, of course, &lt;a href=&quot;https://x.com/jdevalk/status/1904231722352316743&quot;&gt;told him too&lt;/a&gt;. Let’s dive into why this doesn’t work.&lt;/p&gt;
&lt;h3&gt;What AIs need (and what they’re not getting)&lt;/h3&gt;
&lt;p&gt;My friend &lt;a href=&quot;https://randomadult.com/&quot;&gt;Jamie Madden&lt;/a&gt; pointed me to &lt;a href=&quot;https://llmstxt.org&quot;&gt;llmstxt.org&lt;/a&gt;. This is a site pushing for standards that help AIs crawl and understand your content better. And the advice there hits hard, because it shows just how bad the situation really is.&lt;/p&gt;
&lt;p&gt;If you want your content to show up in &lt;em&gt;any&lt;/em&gt; kind of search — be it classic search engines like Google and Bing, or newer AI-driven search tools — it needs to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Accessible to bots&lt;/strong&gt; (so: don’t block it with &lt;code&gt;robots.txt&lt;/code&gt; or technical barriers)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy to reach&lt;/strong&gt; (no complex JavaScript rendering or tons of chained requests)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy to parse&lt;/strong&gt; (clear, valid HTML, not div soup)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy to understand&lt;/strong&gt; (simple language, good structure, solid readability)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sure, Google and Bing have gotten better at dealing with #2 and #3 over the years. But the newer crawlers, the ones feeding AI models and tools? They’re still toddlers learning to walk. They do &lt;em&gt;not&lt;/em&gt; execute JavaScript &lt;em&gt;at all&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;And now, instead of them getting smarter, we’re being asked to dumb things down for them through new standards like the above mentioned &lt;code&gt;llms.txt&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But here’s a thought: &lt;strong&gt;why not just make our sites simpler and better for &lt;em&gt;everyone&lt;/em&gt;?&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;What this really means&lt;/h3&gt;
&lt;p&gt;This isn’t just about AI or SEO. It’s a reminder of the principles the web was built on: accessibility, clarity, speed, and user-first design. Somewhere along the way, we traded a lot of that for flashy frameworks and brittle complexity.&lt;/p&gt;
&lt;p&gt;You can have the most beautifully designed website in your favorite JavaScript framework, but if the HTML that comes out of it is garbage and your content is buried behind five layers of client-side rendering… you’re invisible. To AI. Often even to Google. And thus: to users.&lt;/p&gt;
&lt;p&gt;It doesn’t matter how great your content is, how strong your brand is, or how well you target user intent. &lt;strong&gt;If machines can’t read your site, people won’t find it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Funnily enough, if we did this well, accessibility wouldn’t be as much of a challenge and SEO would suddenly be simple.&lt;/p&gt;
&lt;h3&gt;Back to basics, forward to the future&lt;/h3&gt;
&lt;p&gt;This moment feels oddly familiar. Like we’ve been here before. It’s the early 2000s again, but instead of optimizing for search engines, we’re optimizing for LLMs too now. And the fundamentals haven’t changed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;So maybe it’s time to build for &lt;em&gt;clarity&lt;/em&gt; again: for humans first, and machines second, with systems that honor the web’s foundations instead of working against them.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Because in the end, the best content in the world is useless if it’s unreadable by the systems that help people find it.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Redirect-By HTTP headers</title><link>https://joost.blog/redirect-by-http-headers/</link><guid isPermaLink="true">https://joost.blog/redirect-by-http-headers/</guid><description>One of the things one runs into when you’re doing large migrations (between domains, or within a domain) is that you run into redirects that go “wrong”. A syste</description><pubDate>Thu, 13 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the things one runs into when you’re doing large migrations (between domains, or within a domain) is that you run into redirects that go “wrong”. A system somewhere is doing a redirect, and you don’t know which system you need to change that redirect in. A “Redirect-By” header helps you find out which system did the redirect.&lt;/p&gt;
&lt;h2&gt;Origin story&lt;/h2&gt;
&lt;p&gt;I ran into this problem while doing the migration of the Guardian from guardian .co.uk to the theguardian .com. Multiple systems were doing redirects, making it hard to find the source. So, I came up with the X-Redirect-By header, this header was sent along with each redirect. This helped me identify which system was doing the redirect.&lt;/p&gt;
&lt;p&gt;In 2017 I wrote &lt;a href=&quot;https://yoast.com/developer-blog/x-redirect-by-header/&quot;&gt;this blog post on Yoast.com&lt;/a&gt; to propose the &lt;code&gt;X-Redirect-By&lt;/code&gt; header. We then implemented that, first in Yoast SEO, later on also in WordPress, and other systems like Typo3 followed suit.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/yoast-com-redirect-by-png.webp&quot; alt=&quot;Screenshot of the blog post on yoast.com proposing the X-Redirect-By header.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Usage of x-redirect-by&lt;/h2&gt;
&lt;p&gt;Currently, a &lt;a href=&quot;https://webtechsurvey.com/response-header/x-redirect-by&quot;&gt;large portion of sites in the world&lt;/a&gt; uses the X-Redirect-By header to indicate which system created a redirect. The &lt;a href=&quot;https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Common_non-standard_response_fields&quot;&gt;Wikipedia page about HTTP headers&lt;/a&gt; lists it as a “Common non-standard response field”.&lt;/p&gt;
&lt;h2&gt;Should we standardize redirect-by?&lt;/h2&gt;
&lt;p&gt;As I was preparing &lt;a href=&quot;https://smxmuenchen.de/en/session/the-missing-seo-guide-to-domain-migrations-2/&quot;&gt;my presentation&lt;/a&gt; for SMX Munich next week, where I’ll be presenting “The missing guide to SEO domain migrations”, I was discussing this with John Mueller. He kindly informed me that &lt;code&gt;x-&lt;/code&gt; prefixed headers are &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers#:~:text=Custom%20proprietary%20headers,their%20status.&quot;&gt;actually discouraged&lt;/a&gt;. In fact, Google has been trying this for &lt;code&gt;x-robots-tag&lt;/code&gt;, see &lt;a href=&quot;https://datatracker.ietf.org/doc/html/draft-illyes-repext-00&quot;&gt;this IETF draft&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Which got me thinking: should we take the next step, and standardize this properly? I’ve been going over the existing standard HTTP headers, and while I think that you &lt;em&gt;could&lt;/em&gt; use &lt;code&gt;Via&lt;/code&gt; or &lt;code&gt;Server&lt;/code&gt; headers for this same purpose, it’s not the same. A proposal for an official &lt;code&gt;Redirect-By&lt;/code&gt; header would have the benefit of getting more people to notice it and probably implement it, which would potentially help &lt;em&gt;every&lt;/em&gt; SEO and web developer. Lots of them have told me they have spent many hours finding the source of a redirect.&lt;/p&gt;
&lt;p&gt;I’d love feedback on this. What do you think? Should we standardize this as &lt;code&gt;redirect-by&lt;/code&gt;? Am I missing an obvious, already existing, standard header that could be used for this? Let me know in the comments!&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress comments, cookies and caching</title><link>https://joost.blog/wordpress-comment-cookies-and-caching/</link><guid isPermaLink="true">https://joost.blog/wordpress-comment-cookies-and-caching/</guid><description>This post explains how WordPress uses comment cookies and why that is detrimental to your site’s caching. It then shows you how to fix this.</description><pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post explains how WordPress uses comment cookies and why that is detrimental to your site’s caching. It then shows you how to fix this.&lt;/p&gt;
&lt;p&gt;When I wrote my previous post about &lt;a href=&quot;/wordpress-leadership/&quot;&gt;WordPress leadership&lt;/a&gt;, I had anticipated getting a lot of comments. It turned out there were even more than I expected. This led to some issues with my (admittedly rather aggressive) caching settings on my blog. When I approved a comment it didn’t clear my Cloudflare edge cache of my post, so people couldn’t see them.&lt;/p&gt;
&lt;p&gt;A few days after fixing that, I was alerted to the fact that Cloudflare was caching the details of the last commenter on a post. This led me down a rather deep rabbit hole. I was reading code that I’d not seen in quite some time, in the WordPress comment system.&lt;/p&gt;
&lt;p&gt;Now, this is all admittedly… A bit of the stuff that makes my kids call me a boomer. Because who does all this commenting on blogs? Right, not my kids. But still, a problem nonetheless.&lt;/p&gt;
&lt;p&gt;This is rather lengthy, so here’s a table of contents:&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-how-the-wordpress-comment-system-works&quot;&gt;How the WordPress comment system works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-solution-moving-comment-cookies-to-javascript-space&quot;&gt;The solution: moving comment cookies to JavaScript space&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-1-set-cookies-when-you-comment&quot;&gt;1. Set cookies when you comment&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-2-preventing-wordpress-from-setting-comment-cookies&quot;&gt;2. Preventing WordPress from setting comment cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-3-prevent-wordpress-from-reading-comment-cookies&quot;&gt;3. Prevent WordPress from reading comment cookies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-4-fill-the-comment-form-with-javascript&quot;&gt;4. Fill the comment form with JavaScript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-aside-the-seo-impact-of-the-wordpress-comment-system&quot;&gt;Aside: the SEO impact of the WordPress comment system&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;How the WordPress comment system works&lt;/h2&gt;
&lt;p&gt;When you leave a comment on a WordPress site, the process works as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Your comment is submitted;&lt;/li&gt;
&lt;li&gt;WordPress sets a cookie for each of your name, email address, and website URL;&lt;/li&gt;
&lt;li&gt;Your comment is either held for moderation or immediately approved, based on several criteria. Then WordPress redirects you to a new URL and renders the post you commented on.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The URL it redirects you to can be two things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If your comment was immediately approved, it’s the URL you started on, with a &lt;code&gt;#comment-&amp;lt;comment-id&amp;gt;&lt;/code&gt; anchor so that you’re shown your comment on the page immediately.&lt;/li&gt;
&lt;li&gt;If your comment was held for moderation, by default, WordPress sends you to the URL you started on with a &lt;code&gt;unapproved&lt;/code&gt; URL parameter + a &lt;code&gt;moderation-hash&lt;/code&gt; parameter. It uses these to show your &lt;em&gt;unapproved&lt;/em&gt; comment rendered where it &lt;em&gt;would&lt;/em&gt; be rendered. It adds a message around it that your comment is held for moderation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When WordPress renders that page, and &lt;em&gt;every time after that&lt;/em&gt; when it renders &lt;em&gt;any blog post&lt;/em&gt; on the site for you, your browser sends your cookies to the server, and WordPress fills the data from those cookies into the comment form. So when that view, or a later view, is proxy cached (for instance by Cloudflare), your personal data gets cached along with it.&lt;/p&gt;
&lt;p&gt;This is of course highly undesirable on sites that have aggressive caching. On sites like that, you want every page render to be as similar to another one as possible. Even more important you certainly don’t want Personally Identifiable Information in those rendered pages.&lt;/p&gt;
&lt;h2&gt;The solution: moving comment cookies to JavaScript space&lt;/h2&gt;
&lt;p&gt;The solution to this is fairly obvious: there’s no reason why this would need to be done server side anymore, as we can do all of it in JavaScript. To do so, we have to take a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set the appropriate cookies when you leave a comment (this is going to be in JavaScript);&lt;/li&gt;
&lt;li&gt;Prevent WordPress from setting the comment cookies in the first place, as those might get cached;&lt;/li&gt;
&lt;li&gt;Prevent WordPress from adding the content of those cookies to the rendered output on the page, as that would get it cached (this will be a few simple PHP functions);&lt;/li&gt;
&lt;li&gt;Use JavaScript to read those cookies and fill the form fields.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We &lt;em&gt;could&lt;/em&gt; do this in 3 steps if we chose not to be backwards compatible: if we chose different cookie names, we could skip step 3, but I like to keep things backwards compatible.&lt;/p&gt;
&lt;h3&gt;1. Set cookies when you comment&lt;/h3&gt;
&lt;p&gt;We’ll start by setting the comment cookies on form submission. This assumes that you’re using the default WordPress comment forms, and thus have the &lt;code&gt;commentform&lt;/code&gt; &lt;code&gt;id&lt;/code&gt; attribute on your comment forms.&lt;/p&gt;
&lt;p&gt;We’ll update this code a bit later to add the functionality for step 4 as well. What this code does is fairly simple: it sets the 3 different comment cookies, with an expiry date of one year, when you submit the comment form. It sets them for &lt;code&gt;/&lt;/code&gt; on your domain, so that they’re used on all the comment forms throughout a site.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Function to set comment cookies.
function setCommentCookies( name, email, url ) {
    const oneYear = 365 * 24 * 60 * 60 * 1000;
    const expiryDate = new Date( Date.now() + oneYear ).toUTCString();

    // Set cookies for comment author data. This (slightly inefficient) cookie format is used for compatibility with core WordPress.
    document.cookie = `comment_author=${encodeURIComponent(name)}; expires=${expiryDate}; path=/`;
    document.cookie = `comment_author_email=${encodeURIComponent(email)}; expires=${expiryDate}; path=/`;
    document.cookie = `comment_author_url=${encodeURIComponent(url)}; expires=${expiryDate}; path=/`;
}

document.addEventListener( &apos;DOMContentLoaded&apos;, function () {
    const nameField  = document.getElementById( &apos;author&apos; );
    const emailField = document.getElementById( &apos;email&apos; );
    const urlField   = document.getElementById( &apos;url&apos; );

    // Set cookies on form submission.
    const commentForm = document.getElementById( &apos;commentform&apos; );
    if ( commentForm ) {
        commentForm.addEventListener( &apos;submit&apos;, function() {
            if ( nameField &amp;amp;&amp;amp; emailField &amp;amp;&amp;amp; urlField ) {
                setCommentCookies( nameField.value, emailField.value, urlField.value );
            }
        });
    }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. Preventing WordPress from &lt;em&gt;setting&lt;/em&gt; comment cookies&lt;/h3&gt;
&lt;p&gt;To prevent WordPress from setting comment cookies, we should just unhook &lt;a href=&quot;https://github.com/WordPress/WordPress/blob/275d202ae3baac4e674538fd5bc3bc660825d0e0/wp-includes/comment.php#L530-L572&quot;&gt;the function&lt;/a&gt; that sets these:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;remove_action( &apos;set_comment_cookies&apos;, &apos;wp_set_comment_cookies&apos; );
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. Prevent WordPress from &lt;em&gt;reading&lt;/em&gt; comment cookies&lt;/h3&gt;
&lt;p&gt;Next we want to prevent WordPress from adding the content from the comment cookies on the rendered pages. Since there is no way to prevent this functionality from running entirely, we just have to filter it to be empty:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * To prevent this data from being cached, we don&apos;t want to show it server side.
 *
 * @param array $commenter Ignored.
 *
 * @return array With empty values for all three keys.
 */
function ignore_comment_cookies_serverside( $commenter ) {
    return [
        &apos;comment_author&apos;       =&amp;gt; &apos;&apos;,
        &apos;comment_author_email&apos; =&amp;gt; &apos;&apos;,
        &apos;comment_author_url&apos;   =&amp;gt; &apos;&apos;,
    ];
}

add_filter( &apos;wp_get_current_commenter&apos;, &apos;ignore_comment_cookies_serverside&apos; );
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4. Fill the comment form with JavaScript&lt;/h3&gt;
&lt;p&gt;To fill the comment form with JavaScript, we have to read the values from the cookies:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Function to read cookies.
function getCommentCookies() {
    const cookies = document.cookie.split( &apos;; &apos; ).reduce(( acc, cookie ) =&amp;gt; {
        const [key, value] = cookie.split( &apos;=&apos; );
        acc[key]           = decodeURIComponent(value);

        return acc;
    }, {} );

    return {
        name:  cookies[&apos;comment_author&apos;] || &apos;&apos;,
        email: cookies[&apos;comment_author_email&apos;] || &apos;&apos;,
        url:   cookies[&apos;comment_author_url&apos;] || &apos;&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we update the &lt;code&gt;DOMContentLoaded&lt;/code&gt; function we created in step 1 to add this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.addEventListener( &apos;DOMContentLoaded&apos;, function () {
    const nameField  = document.getElementById( &apos;author&apos; );
    const emailField = document.getElementById( &apos;email&apos; );
    const urlField   = document.getElementById( &apos;url&apos; );

    // Get the data from the comments.
    const cookies = getCommentCookies();

    // Populate comment form fields with cookie data.
    if (nameField)  nameField.value  = cookies.name;
    if (emailField) emailField.value = cookies.email;
    if (urlField)   urlField.value   = cookies.url;

    // Set cookies on form submission.
    const commentForm = document.getElementById( &apos;commentform&apos; );
    if ( commentForm ) {
        commentForm.addEventListener( &apos;submit&apos;, function() {
            if ( nameField &amp;amp;&amp;amp; emailField &amp;amp;&amp;amp; urlField ) {
                setCommentCookies( nameField.value, emailField.value, urlField.value );
            }
        });
    }
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have combined all of these in &lt;a href=&quot;https://gist.github.com/jdevalk/2d4d4f6d055ceede40a68eeb21800ef4&quot;&gt;this gist&lt;/a&gt;, for easy copy pasting.&lt;/p&gt;
&lt;h2&gt;Aside: the SEO impact of the WordPress comment system&lt;/h2&gt;
&lt;p&gt;The fact that WordPress redirects to a URL with an &lt;code&gt;unapproved&lt;/code&gt; parameter is &lt;em&gt;incredibly&lt;/em&gt; tricky in my eyes; I’m never a fan of URL parameters being added anywhere (as it usually busts the cache, and bloats the URL space), but here it also leads to potential SEO issues if people link to these parameterized URLs. I was able to find quite a few of these with &lt;a href=&quot;https://www.google.com/search?q=inurl:unapproved+inurl:moderation-hash&quot;&gt;relatively simple Google search&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://poty-demolition.fr/bonjour-tout-le-monde/?unapproved=24737&amp;amp;moderation-hash=91fb997849f7f870c5abd153fb99a326&quot;&gt;https://poty-demolition.fr/bonjour-tout-le-monde/?unapproved=24737&amp;amp;moderation-hash=91fb997849f7f870c5abd153fb99a326&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sekainoowari.jp/discography/2011/08/17/125/inori/?unapproved=11&amp;amp;moderation-hash=9a1bc2e1de3f9708df8a394c836bcc76&quot;&gt;https://sekainoowari.jp/discography/2011/08/17/125/inori/?unapproved=11&amp;amp;moderation-hash=9a1bc2e1de3f9708df8a394c836bcc76&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since WordPress does not &lt;code&gt;noindex&lt;/code&gt; these URLs by default, this is a perfectly sensible way of injecting links into a page and getting (usually &lt;code&gt;nofollow&lt;/code&gt;, but still) links from domains that have no idea they’re linking to you, thinking they’ve not approved your comment.&lt;/p&gt;
&lt;p&gt;SEO plugins should take note and add &lt;code&gt;noindex&lt;/code&gt; to every page that has these URL parameters on it, something I think WordPress core should do too. On my site, I have decided to just not let WordPress redirect to this parameterized URL entirely, something I have included in the gist as well.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Breaking the Status Quo</title><link>https://joost.blog/wordpress-leadership/</link><guid isPermaLink="true">https://joost.blog/wordpress-leadership/</guid><description>WordPress is at a crossroads, now even more clearly then when I wrote my previous post on WordPress’s roadmap. I had very much intended to leave this topic alon</description><pubDate>Fri, 20 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;A vision for a new WordPress era&lt;/h2&gt;
&lt;p&gt;WordPress is at a crossroads, now even more clearly then when I wrote my &lt;a href=&quot;/wordpress-roadmap/&quot;&gt;previous post on WordPress’s roadmap&lt;/a&gt;. I had very much intended to leave this topic alone for a bit until after the holiday break, until, last night, Matt &lt;a href=&quot;https://wordpress.org/news/2024/12/holiday-break/&quot;&gt;imposed a holiday break on us all&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This holiday break &lt;a href=&quot;https://github.com/WordPress/wordcamp.org/issues/1450&quot;&gt;caused issues&lt;/a&gt; that needed immediate fixing, but also makes more clear that we don’t have the luxury of waiting.&lt;/p&gt;
&lt;p&gt;We, the WordPress community, need to decide if we’re ok being led by a single person who controls everything, and might do things we disagree with, or if we want something else. For a project whose tagline is “Democratizing publishing”, we’ve been very low on exactly that: democracy.&lt;/p&gt;
&lt;p&gt;I read the recent open letter by &lt;a href=&quot;https://www.therepository.email/wordpress-contributors-and-community-leaders-call-for-governance-reform-in-rare-open-letter&quot;&gt;20 signatories on the Repository&lt;/a&gt; who say “we stand with you” and make very clear that they object to the current status quo. I agree with most everything they say, and I want us all to take a step further: let’s get to solutions.&lt;/p&gt;
&lt;p&gt;You’ll have heard the proverb “it takes a village to raise a child” and that’s &lt;em&gt;very&lt;/em&gt; much how I think about developing CMSs too. You need many voices, many ideas, many backgrounds. You need to embrace diversity. Unfortunately, those with ideas that don’t follow the same direction as our current leader, are being shut down, quite a few even banned, hence the reason that those 19 signatories mentioned above chose anonymity. I get it and I respect it.&lt;/p&gt;
&lt;p&gt;Our &lt;a href=&quot;https://en.wikipedia.org/wiki/Benevolent_Dictator_for_Life&quot;&gt;BDFL&lt;/a&gt; is no longer Benevolent, and because of that, speaking up in public is a risk. In &lt;a href=&quot;https://www.inc.com/#:~:text=Is%20Matt%20Mullenweg%20the%20Mad%20King%20of%20WordPress%3F&quot;&gt;an interview with Inc&lt;/a&gt; (which to be fair Matt called a “&lt;a href=&quot;https://ma.tt/2024/12/inc-hit-piece/&quot;&gt;hit piece&lt;/a&gt;” himself) Matt said he preferred the term “enlightened leader” over “dictator”. I asked ChatGPT what would be qualities of an enlightened leader, it gave me the following:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An enlightened leader embodies qualities that inspire trust, foster growth, and guide others with wisdom, compassion, and integrity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I think it’s fair to say that most in the community would disagree with that being an apt description of Matt’s current behavior.&lt;/p&gt;
&lt;h2&gt;On contributing &amp;amp; influence&lt;/h2&gt;
&lt;p&gt;While at Yoast, we always contributed “big”. Mostly because we &lt;em&gt;wanted&lt;/em&gt; to, but quite often also because we &lt;em&gt;had to&lt;/em&gt;: if we didn’t build the APIs to integrate into the things we wanted to integrate with (for example: Gutenberg), we simply couldn’t. As owners of Yoast we’ve spent &lt;em&gt;well&lt;/em&gt; over 5% of revenue over the course of my time there on WordPress core development and that tradition seems to mostly continue there today.&lt;/p&gt;
&lt;p&gt;In exchange for our contributions, I did get some say in where WordPress went, though never officially, and never when it went in directions that Matt disagreed with. Over time, that influence became less as Matt tightened his grip on the project. I think that tightening was in part a cramp. Wanting to control more what people were working on, because the project wasn’t progressing fast enough in the direction he wanted it to go in.&lt;/p&gt;
&lt;p&gt;A lot has happened over the last few months, that I think all comes down to the above. I’ve often considered how the WordPress world “worked” unhealthy. I’ve spoken to many slightly outside of our industry over the past months about what was happening and several people, independent of each other, described WordPress as “a cult” to me. And I understand why.&lt;/p&gt;
&lt;p&gt;I think it’s time to let go of the cult and change project leadership. &lt;a href=&quot;/transparency-contribution-and-the-future-of-wordpress/&quot;&gt;I’ve said it before&lt;/a&gt;: we need a “board”. We can’t wait with doing that for the years it will take for Automattic and WP Engine to fight out this lawsuit. As was &lt;a href=&quot;https://www.404media.co/wordpress-wp-engine-preliminary-injunction/&quot;&gt;already reported&lt;/a&gt;, Matt said recently in Post Status that “it’s hard to imagine wanting to continue working on WordPress after this”. A few days later, he gave a &lt;a href=&quot;https://x.com/WordPress/status/1868605324229263840&quot;&gt;completely conflicting message&lt;/a&gt; in the State of the Word. Yet he never came back on that first statement or clarified that he’d changed his mind. He also didn’t come back to talk to the community he turned his back on.&lt;/p&gt;
&lt;p&gt;Last night, he disabled the registration of new accounts on WordPress.org and said this in his post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I hope to find the time, energy, and money to reopen all of this sometime in the new year.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What if Matt &lt;em&gt;doesn’t&lt;/em&gt; reopen registration? Or only does so for people with an &lt;code&gt;@automattic.com&lt;/code&gt; email address? Let’s be clear: he &lt;em&gt;could&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I’m still, to this day, &lt;em&gt;very&lt;/em&gt; thankful for what Matt has created. I would love to work &lt;em&gt;with&lt;/em&gt; him to fix all this. But it’s clear now, that we can no longer have him be our sole leader, although I’d love it if we could get him to be &lt;em&gt;among&lt;/em&gt; the leaders.&lt;/p&gt;
&lt;h2&gt;What should happen now?&lt;/h2&gt;
&lt;p&gt;In my ideal world, the following things happen:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A WordPress foundation like entity becomes the lead of the project, and gets a board of industry people, from diverse backgrounds.&lt;/li&gt;
&lt;li&gt;WordPress.org and all other important community assets that are currently “Matt’s private property” are handed over to that foundation.&lt;/li&gt;
&lt;li&gt;The WordPress trademark is given to the public domain or otherwise dealt with in such a way that every company can freely say that they do “WordPress hosting”, “WordPress support” etc. Not just because that’s the right thing to do in my mind, but because doing so means we allow growth of the terms and the concepts.&lt;/li&gt;
&lt;li&gt;People and companies can become sponsors of this WordPress foundation like entity, and we give some perks to those sponsors (for instance, listing them on a hosting page) and we disclose all of that transparently.&lt;/li&gt;
&lt;li&gt;We create several small teams responsible for Architecture, Product, Events, etc. and create a proper governance structure around this.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There’s lots more to figure out, but these are steps that must be taken fast. On the code side we need to take steps too, and we can most quickly act on one that has become very clear that we need immediately:&lt;/p&gt;
&lt;h2&gt;Federated And Independent Repositories&lt;/h2&gt;
&lt;p&gt;We need to supplement WordPress.org updates with other sources, so that what happened to Advanced Custom Fields, can’t happen again. Lots of hosts are currently experimenting with or already putting in place mirrors of WordPress.org. This creates issues: download and active install statistics are no longer reliable, for instance.&lt;/p&gt;
&lt;p&gt;Just having mirrors of WordPress.org also doesn’t &lt;em&gt;really&lt;/em&gt; solve the problem of a single party controlling our single update server. For that, we need to make sure that those mirrors federate with each other, and share each others data and, &lt;a href=&quot;https://marucchi.com/wordpress-leadership-continued/&quot;&gt;as Karim suggested&lt;/a&gt;, allow for independent plugins and themes to be hosted there, outside of the wordpress.org repository. I call this: Federated and Independent Repositories, in short: FAIR.&lt;/p&gt;
&lt;p&gt;I’m already talking to several hosts about this, and would welcome anyone who wants to join these conversations, so we’re not duplicating work.&lt;/p&gt;
&lt;p&gt;Matt might not agree to my first five points above. However: we can still work on the Federated and Independent Repositories without his permission, because frankly, we don’t need it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We take back the commons.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;What is my role in this?&lt;/h2&gt;
&lt;p&gt;I’ve already been working and talking to lots of people over the last few months trying to find ways out of this. I want to hold this community together. This resulted in several conversations, starting with my conversations with Matt and how to reconcile, to several conversations I’ve initiated with key community people &amp;amp; hosting leaders. This led to a deeper conversation with Karim Marucchi, CEO of &lt;a href=&quot;https://crowdfavorite.com/&quot;&gt;Crowd Favorite&lt;/a&gt;, one of the WordPress world’s first enterprise-grade agencies, about breaking away from the status quo and focusing on the future. I feel this gives me a base for moving the conversation forward with people that I can fall back on and who can help to transition us to what’s next.&lt;/p&gt;
&lt;p&gt;Taking back the commons means that we try to hear every voice, be considerate of all the different use cases of that commons and bring us all forward. We may not all agree along the way, but we’ll talk about that openly, without fear. We may make mistakes, and then we’ll set them right, and if needed, apologize.&lt;/p&gt;
&lt;p&gt;I’m here, and willing to lead through this transition. I &lt;em&gt;do&lt;/em&gt; have the time, the energy &lt;em&gt;and&lt;/em&gt; the money needed to fund myself doing it. I’ve worked in this industry and this community for close to 20 years and it’s very dear to me. Thanks in large part &lt;em&gt;to&lt;/em&gt; the WordPress project, I have the privileged position to be able to drop and/or delegate some of the stuff I’m working on and start working on this.&lt;/p&gt;
&lt;p&gt;Let me be clear though: we should not replace one BDFL with another. This is a moment of transition. I’m also very willing to work with other leadership if it turns out the community wants someone else.&lt;/p&gt;
&lt;p&gt;If you haven’t already, go read &lt;a href=&quot;https://marucchi.com/wordpress-leadership-continued/&quot;&gt;Karim’s post&lt;/a&gt; too and let me know all your thoughts. In the comments here, on &lt;a href=&quot;https://bsky.app/profile/joost.blog&quot;&gt;Bluesky&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/jdevalk&quot;&gt;X&lt;/a&gt;, through DMs somewhere or through my &lt;a href=&quot;/contact-me/&quot;&gt;contact form here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;What happens &lt;em&gt;now&lt;/em&gt;?&lt;/h2&gt;
&lt;p&gt;Right now, in the next few weeks: nothing. We do, truly, all deserve this holiday break (for those it applies to). I’m going to celebrate the holidays with my wife, children and wider family. I hope everyone can reflect about this throughout the holiday season (or you block it out entirely, and think about it later, fine too!).&lt;/p&gt;
&lt;p&gt;We (being Karim and myself) will start 2025 by getting into a (probably virtual) room half way through January with lots of the leaders of this community &amp;amp; industry and decide where we go from here. So, enjoy, rest, and get ready!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress, and what should be on its roadmap</title><link>https://joost.blog/wordpress-roadmap/</link><guid isPermaLink="true">https://joost.blog/wordpress-roadmap/</guid><description>I was reading Hendrik Luehrsen’s excellent post “WordPress isn’t WordPress anymore“, and I decided I had to write more about this. I recently spoke at WordCamp</description><pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Since writing this, I &lt;a href=&quot;/path-forward-for-wordpress/&quot;&gt;proposed FAIR&lt;/a&gt; as a path forward for WordPress governance, and later wrote about &lt;a href=&quot;/fair-wordpress-and-knowing-when-to-stop/&quot;&gt;why we stopped pursuing it&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was reading Hendrik Luehrsen’s excellent post “&lt;a href=&quot;https://kraut.press/2024/wordpress-isnt-wordpress-anymore/&quot;&gt;WordPress isn’t WordPress anymore&lt;/a&gt;“, and I decided I had to write more about this. I recently spoke at WordCamp NL about “The missing features of WordPress”, and these two things “touch”, in an important way. I love WordPress. I love WordPress plugins. I don’t love some of the recent developments in WordPress and that’s not &lt;em&gt;just&lt;/em&gt; talking about the recent Automattic – WP Engine drama.&lt;/p&gt;
&lt;p&gt;When I started writing this, there had been no decision on the injunction yet. Now there is, and as a community we have to figure out how we deal with all this. I don’t think we can separate that entirely from looking at what we all want from (and for) WordPress. Good discussions about where WordPress should be headed have been lacking from the community for a while.&lt;/p&gt;
&lt;p&gt;So: let me share my thoughts, first by looking at WordPress’s market share and the trends in the wider market, then by discussing what WordPress is, or should be.&lt;/p&gt;
&lt;h2&gt;Looking at WordPress’s market share&lt;/h2&gt;
&lt;p&gt;If we look at WordPress’s market share, we have to admit that it’s stable at best. WordPress, which has been growing for the longest time since it was created, is now stagnant. The current market share (as of November 2024) is slightly below what we started the year at:&lt;/p&gt;
&lt;p&gt;This means there’s also hardly any growth since early 2023, which should be (and probably &lt;em&gt;is&lt;/em&gt; to everyone who’s aware) concerning to everyone in the WordPress industry.&lt;/p&gt;
&lt;p&gt;To make things worse, &lt;em&gt;all&lt;/em&gt; other open source, self-hosted technologies are losing:&lt;/p&gt;
&lt;p&gt;If you’re now thinking “but then other projects must be winning”, you’re 100% right: SaaS tools are winning. Just look at the numbers:&lt;/p&gt;
&lt;p&gt;I’ll give my sharper analysis: if WordPress hadn’t had &lt;a href=&quot;https://elementor.com/&quot;&gt;Elementor&lt;/a&gt;, it would have shrunk (&lt;a href=&quot;/elementor-wordpress-secret-growth-driver/&quot;&gt;something I’ve been saying for a while&lt;/a&gt;). This is definitely &lt;em&gt;also&lt;/em&gt; true, though with less big of an impact, for &lt;a href=&quot;https://woocommerce.com/&quot;&gt;WooCommerce&lt;/a&gt;, the other driver for WordPress growth. Which shows that it’s not that people don’t want Open Source. Let me show this with a graph too:&lt;/p&gt;
&lt;p&gt;So, it’s not that people dislike or don’t want Open Source. I honestly think people don’t care that much about this &lt;em&gt;at all&lt;/em&gt;. I think we have other problems. Let’s dive deeper.&lt;/p&gt;
&lt;h2&gt;Why is this happening?&lt;/h2&gt;
&lt;p&gt;The question we have to ask ourselves of course is: why is this happening? What are people and companies looking for that WordPress is not providing? I think there are several important factors to discuss around this. Many of the arguments that people use against WordPress, we are very often quick to dismiss: WordPress is old, not modern. WordPress is insecure. WordPress is slow. There are certainly more pain points, but let’s try to cover these for now.&lt;/p&gt;
&lt;h3&gt;WordPress is slow &amp;amp; technically stagnant&lt;/h3&gt;
&lt;p&gt;WordPress is often ridiculed by developers in the wider web ecosystem. It’s considered backward, slow and bloated. And the truth is harsh but simple: this is &lt;em&gt;true&lt;/em&gt;. We have failed to modernize large parts of our codebase over the last decade, especially on the PHP side. Many projects &lt;a href=&quot;https://make.wordpress.org/core/2016/05/03/proposal-next-generation-rewrites/&quot;&gt;and&lt;/a&gt; &lt;a href=&quot;https://make.wordpress.org/core/2024/02/01/proposal-implement-a-php-autoloader-in-wordpress-core/&quot;&gt;potential&lt;/a&gt; &lt;a href=&quot;https://make.wordpress.org/core/2019/08/05/feature-project-proposal-wp-notify/&quot;&gt;improvements&lt;/a&gt; have been discussed, but we focused instead on building features (like the duotone filters Hendrik mentioned). Fantastic work has been done to keep WordPress working with modern versions of PHP, but we’ve failed to truly deal with our technical debt and with that also failed to make WordPress that much faster.&lt;/p&gt;
&lt;p&gt;On the JavaScript side, we’re doing the opposite: we’re churning through code harder than we should. The speed of development is outpacing what even some of the best developers can follow if they’re not on the project full time. We bring new JavaScript APIs very often, developing &lt;a href=&quot;https://make.wordpress.org/core/2024/03/04/interactivity-api-dev-note/&quot;&gt;our own&lt;/a&gt; instead of integrating &lt;a href=&quot;https://alpinejs.dev/&quot;&gt;existing projects&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;WordPress is also at the absolute forefront of some of the web’s development though. The Performance team includes some of the world’s very best frontend performance engineers, who jointly made WordPress &lt;a href=&quot;https://lookerstudio.google.com/u/0/reporting/55bc8fad-44c2-4280-aa0b-5f3f0cd3d2be/page/M6ZPC&quot;&gt;much, much faster over the last few years&lt;/a&gt;. The problem is: our competitors are doing the same. Shopify improved more than we did. Part of the problem here is that we’re simply not merging the fantastic work the Performance team is doing into WordPress core fast enough.&lt;/p&gt;
&lt;h3&gt;WordPress is insecure&lt;/h3&gt;
&lt;p&gt;WordPress is very often dubbed insecure by its competitors. The standard response to this is “WordPress core has hardly had any security issues the last few years”. We have to be honest here: WordPress is often considered insecure because it gets hacked a lot. It doesn’t get hacked through WordPress core, it gets hacked through plugins most of the time. But we don’t have the luxury of saying that those plugins are not WordPress. They &lt;em&gt;very much&lt;/em&gt; are. Plugins are, as Hendrik so poignantly discussed in his post too, the reason for WordPress’s popularity, this easy extensibility is also its challenge. You can’t look away from their shortcomings yet benefit from what they do.&lt;/p&gt;
&lt;p&gt;Recently PatchStack (disclosure: we’re an investor as &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital&lt;/a&gt; and I’m on their board) held a special event in their Bug Bounty program for Cyber Security Month and they &lt;a href=&quot;https://patchstack.com/articles/nearly-1000-plugins-closed-during-wordpress-security-cleanup/&quot;&gt;closed 1,000 plugins&lt;/a&gt; on WordPress.org as a result. Now that is a herculean effort, but it also leaves everyone wondering: what would happen if we looked at &lt;em&gt;all&lt;/em&gt; plugins a bit more deeply?&lt;/p&gt;
&lt;p&gt;It also makes you wonder: why can’t we see in our WordPress admins that those plugins are closed on WordPress.org? Why is WordPress, the software, not telling us that plugins we’re using that we installed from WordPress.org, have been closed for security reasons? &lt;a href=&quot;https://core.trac.wordpress.org/ticket/30465&quot;&gt;The ticket requesting this&lt;/a&gt; is now officially over a decade old.&lt;/p&gt;
&lt;p&gt;A step further would even be to say: this set of plugins hasn’t been updated in the last year. The &lt;a href=&quot;https://wordpress.org/plugins/&quot;&gt;plugin directory&lt;/a&gt; talks about 59,000 plugins. The &lt;a href=&quot;https://wordpress.org/plugins/sitemap-index-1.xml&quot;&gt;XML sitemap for the plugin directory&lt;/a&gt; tells me that only 14,648 of them have been updated in the last year or so.&lt;/p&gt;
&lt;h3&gt;WordPress is too hard to use&lt;/h3&gt;
&lt;p&gt;The last, most painful argument that I want to cover is that WordPress is too hard to use. Currently, WordPress forces users to learn several different admin UIs, with different design languages. WordPress has for years tried to get people to use the Block Editor and more recently the Full Site Editor, and the harsh truth is that the editor that has grown the most in that time is a third: Elementor. See for yourself:&lt;/p&gt;
&lt;p&gt;Note that the Block Editor was released December 6th, 2018, now 6 years ago, but wasn’t tracked in this data until earlier this year, and that the drop in Elementor’s growth is the result of a measurement change.&lt;/p&gt;
&lt;p&gt;One of the best additions to WordPress’s marketing recently has been the flurry of activity on the WordPress Youtube channel mostly by Jamie Marsland. I have to admit to have been utterly dismayed when I saw &lt;a href=&quot;https://www.youtube.com/watch?v=EqY5bje8D2o&quot;&gt;Matt Mullenweg battle Jessica Lyschik recently&lt;/a&gt;, because it showed so clearly what many people have been saying for a while now: the block editor is too hard to use. Matt resorted to use AI to help him create patterns but that didn’t help him all that much. If Matt, who leads this project and &lt;em&gt;should&lt;/em&gt; be pretty well versed in it, can’t figure this stuff out faster, shouldn’t we take that as a sign?&lt;/p&gt;
&lt;h3&gt;So what to do?&lt;/h3&gt;
&lt;p&gt;Instead of dismissing this feedback, we should take it head on. If the WordPress performance team has shown us one thing, it’s that we &lt;em&gt;can&lt;/em&gt; improve. Now, we must. But to do that, we must solve our existential crisis first:&lt;/p&gt;
&lt;h2&gt;What &lt;em&gt;is&lt;/em&gt; WordPress?&lt;/h2&gt;
&lt;p&gt;Hendrik in his post, underneath a heading asking “&lt;a href=&quot;https://kraut.press/2024/wordpress-isnt-wordpress-anymore/#bigger-picture&quot;&gt;Who is WordPress for&lt;/a&gt;” says this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Historically, WordPress didn’t try to be everything for everyone. Its lean core and robust ecosystem allowed it to meet a wide range of needs without dictating how it should be used. Developers could tailor it to clients. Hobbyists could start small and scale up. Agencies could rely on its flexibility to create bespoke solutions. WordPress provided the tools, but the user always defined the experience.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The “user” over the years have ranged from individuals, small &amp;amp; large businesses up to large enterprises. The power of WordPress is exactly that: it is what you want (and dare to dream) it to be. The problem is that the last few years, new features that went into core didn’t always seem to fit that “lean core” idea.&lt;/p&gt;
&lt;p&gt;Of course there are features the enterprise would want, like personalization, logging, A/B testing, etc. There are features that not just enterprise but more sites would like to see, like multilingual support, custom fields, better admin UIs and many, many things. Most of those things do &lt;em&gt;not&lt;/em&gt; need to be in core. Plugins have &lt;em&gt;always&lt;/em&gt; been a source of innovation and they should continue to be just that.&lt;/p&gt;
&lt;p&gt;WordPress core needs to provide the underlying APIs that allow for extension and force some regularity in how we do these things. I was a big proponent of the REST API when it was developed. In the same way, I’m now a big proponent of merging the fantastic work Felix has done on the &lt;a href=&quot;https://wordpress.org/plugins/ai-services/&quot;&gt;AI services plugin&lt;/a&gt; into core, or at least make that a canonical plugin. I think multilingual support should definitely be built into core, at the very least the underlying APIs.&lt;/p&gt;
&lt;p&gt;We also need a new admin UI and a design language that we force on plugins (and themes) a bit harder. Plugins simply should not have (to develop) their own admin UI, a &lt;a href=&quot;/wordpress-admin-ui-needs-to-be-better/&quot;&gt;point I’ve been stressing for years&lt;/a&gt;. More broadly speaking, we need to work on UX &lt;em&gt;everywhere&lt;/em&gt; and make more logical decisions. For example: “Edit site” does not need to be a button in my admin bar. Nobody edits their site’s templates every day, and nor should they.&lt;/p&gt;
&lt;p&gt;I’d love to have more of these thoughtful conversations, where we all openly and freely discuss where WordPress should go. So: let’s see your blog posts, outlining &lt;em&gt;your&lt;/em&gt; wishes for the project!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update Dec 20th: I followed up this post with another one with &lt;a href=&quot;/wordpress-leadership/&quot;&gt;a vision for a new WordPress era&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Can I safely use AVIF or WebP share images?</title><link>https://joost.blog/use-avif-webp-share-images/</link><guid isPermaLink="true">https://joost.blog/use-avif-webp-share-images/</guid><description>AVIF and WebP are efficient image storage formats. They are smaller than their predecessors, PNG, JPG, and GIF. This leads to smaller images, which means faster</description><pubDate>Wed, 04 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AVIF and WebP are efficient image storage formats. They are smaller than their predecessors, PNG, JPG, and GIF. This leads to smaller images, which means faster page loads, which is what we all want. So, you should use them everywhere. Ideally, we’d also be able to use these image formats for our OpenGraph image tags (&lt;code&gt;og:image&lt;/code&gt;) and Twitter image tags (&lt;code&gt;twitter:image&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;In fact, some platforms, like WhatsApp, limit the size of a share image, so smaller would be &lt;em&gt;much&lt;/em&gt; better.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update December 2024&lt;/strong&gt;&lt;br /&gt;
LinkedIn now supports WebP, making all the major platforms support it. I’ve also added Bluesky, it too supports WebP.&lt;/p&gt;
&lt;h2&gt;TL;DR&lt;/h2&gt;
&lt;p&gt;You should use both AVIF and WebP on your website. They’re smaller, they’re better. All browsers support them. As of December 2024, you &lt;em&gt;can&lt;/em&gt; use WebP share images, but you &lt;em&gt;can not&lt;/em&gt; use AVIF on social platforms yet. We still have to get quite a few more platforms to support AVIF before it’s safe to use them as share images. However, changed from June 2024, you &lt;em&gt;can&lt;/em&gt; now safely use WebP everywhere.&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-test-results&quot;&gt;Test results&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-methodology-how-i-tested-avif-and-webp&quot;&gt;Methodology: how I tested AVIF and WebP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-how-to-use-avif-and-webp-on-your-wordpress-site&quot;&gt;How to use AVIF and WebP on your WordPress site&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-modern-images-in-the-lt-head&quot;&gt;Modern images in the &amp;lt;head&amp;gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Test results&lt;/h2&gt;
&lt;p&gt;I’ve run a quick test across the platforms below, and as you can see, we still have to get multiple platforms to support AVIF:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;AVIF&lt;/th&gt;
&lt;th&gt;WebP&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Bluesky&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Discord&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facebook&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iMessage&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pinterest&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinkedIn&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mastodon&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slack&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Twitter / X&lt;/td&gt;
&lt;td&gt;❌️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WhatsApp&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;td&gt;✅️&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Methodology: how I tested AVIF and WebP&lt;/h2&gt;
&lt;p&gt;I’ve used these two test URLs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;/research/images/avif-test.html&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;/research/images/webp-test.html&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With these URLs, I checked whether the platform would load the AVIF / WebP &lt;code&gt;og:image&lt;/code&gt;, or show the fallback.&lt;/p&gt;
&lt;p&gt;Below are some examples of what that looks like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/linkedin-avif-webp-test-png.avif&quot; alt=&quot;AVIF (negative) and WebP (positive) test results on LinkedIn.&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;./images/whatsapp-avif-webp-test-791x1200.avif&quot; alt=&quot;AVIF (positive) and WebP (positive) test results on WhatsApp.&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;./images/discord-avif-webp-test-png.avif&quot; alt=&quot;AVIF (negative) and WebP (positive) test results on Discord.&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;./images/slack-avif-webp-test-1-png.avif&quot; alt=&quot;WebP (positive) and AVIF (negative) test results on Slack.&quot; /&gt;
&lt;img src=&quot;./images/imessage-avif-webp-test-png.avif&quot; alt=&quot;AVIF (negative) and WebP (positive) test results on iMessage.&quot; /&gt;
&lt;img src=&quot;./images/bluesky-avif-webp-test-845x1200.avif&quot; alt=&quot;AVIF (negative) and WebP (positive) test results on Bluesky.&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;How to use AVIF and WebP on your WordPress site&lt;/h2&gt;
&lt;p&gt;To use these modern image formats on your site you can install the &lt;a href=&quot;https://wordpress.org/plugins/webp-uploads/&quot;&gt;Modern Image Formats plugin&lt;/a&gt;. This plugin has been built and is maintained by the WordPress Performance team, which includes some of the very best performance engineers on the web.&lt;/p&gt;
&lt;p&gt;When you install this plugin, go into its settings, and set them like this if you want to make sure your images are always recognized by social platforms:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/modern-image-formats-setting-webp-1600x596.avif&quot; alt=&quot;Modern Image Formats plugin settings configured to output WebP for social compatibility&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;Modern images in the &amp;lt;head&amp;gt;&lt;/h3&gt;
&lt;p&gt;Alternatively, if you set this to AVIF, you have to make sure that the output in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; section of your site always uses the “classic” format, usually PNG or JPG. The Modern Image Formats plugin actually already tries to do this by default, but it unfortunately fails to do that when using Yoast SEO.&lt;/p&gt;
&lt;p&gt;The following tiny plugin makes sure that your &lt;code&gt;og:image&lt;/code&gt; and &lt;code&gt;twitter:image&lt;/code&gt; tags &lt;em&gt;do&lt;/em&gt; output GIF, PNG or JPG files:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
/*
Plugin Name: Fix modern image formats for social
Plugin URI: /use-avif-webp-share-images/
Description: Replaces og:image and twitter:image URLs in Yoast SEO output ending in 
             .webp or .avif with the original in the page output, so they work.
Author: Joost de Valk
Version: 1.0
*/

function joost_filter_modern_social_image_url( $image_url ) {
    return preg_replace( &apos;/^(.*)-(gif|jpeg|jpg|png)\.(avif|webp)$/i&apos;, 
                         &apos;$1.$2&apos;, $image_url );
}
add_filter( &apos;wpseo_twitter_image&apos;, &apos;joost_filter_modern_social_image_url&apos; );
add_filter( &apos;wpseo_opengraph_image&apos;, &apos;joost_filter_modern_social_image_url&apos; );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This small plugin also replaces &lt;code&gt;WebP&lt;/code&gt; files, just to make sure it works everywhere.&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>The People Behind the Platform: How WordPress’s Community Drives its Success</title><link>https://joost.blog/people-behind-the-platform/</link><guid isPermaLink="true">https://joost.blog/people-behind-the-platform/</guid><description>By Marieke van de Rakt and Joost de Valk</description><pubDate>Thu, 31 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;&lt;strong&gt;By &lt;a href=&quot;https://marieke.com/&quot;&gt;Marieke van de Rakt&lt;/a&gt; and Joost de Valk&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;WordPress marketing is a collaborative effort. WPBeginner, Yoast, BobWP, Siteground, Post Status, and the Repository are just a few of the many, many brands and individuals advocating for WordPress.&lt;/p&gt;
&lt;p&gt;We’re incredibly proud of the WordPress community. Amid the recent drama, we feel the community’s role and importance are severely overlooked. Our community comprises fantastic people who have also excelled in marketing WordPress, helping it grow into the world’s largest CMS. In this blog post, we’ll discuss our view on the impact of the WordPress community on the growth of WordPress.&lt;/p&gt;
&lt;h2&gt;Make the Pie Bigger&lt;/h2&gt;
&lt;p&gt;During his traditional Q&amp;amp;A session at WordCamp Europe (Paris, Vienna, one of these), Matt Mullenweg spoke about WordPress’s growth. He encouraged us to “make the pie bigger,” something he’s said &lt;a href=&quot;https://youtu.be/gfbT2dlhiFk?t=2044&quot;&gt;since at least 2014&lt;/a&gt;, emphasizing that as WordPress grew, everyone in the community would benefit from our collective efforts. That phrase, “making the pie bigger,” became our guiding principle.&lt;/p&gt;
&lt;p&gt;At Yoast, we fully embraced this idea. We shared updates about WordPress on our blog, newsletter, and social channels. At every SEO conference we attended, going back to 2006, we advocated for WordPress, promoting its benefits to a broader audience. We also contributed to WordPress core, developed free plugins, translated strings, and helped organize WordPress events. All of these actions supported the growth of WordPress.&lt;/p&gt;
&lt;p&gt;Automattic and Matt Mullenweg have undeniably had a tremendous impact on WordPress software. Everyone — hosts, plugin developers, agencies — benefitted from their contributions to WordPress. In return, Automattic also thrived thanks to the marketing power of the WordPress community. WordPress (and, with it, WordPress.com) wouldn’t be as widespread without the passionate promotion by the countless WordPress brands and community members. The strength of the community’s marketing has been pivotal in our collective success.&lt;/p&gt;
&lt;h2&gt;How Marketing Works&lt;/h2&gt;
&lt;p&gt;To truly appreciate the role of the WordPress community in driving the growth of the world’s largest CMS, it’s essential to understand the science of marketing that backs it up.&lt;/p&gt;
&lt;p&gt;Studies show that marketing messages are much more effective when they come from independent sources or trusted opinion leaders rather than directly from the brand (e.g. &lt;a href=&quot;https://www.sciencedirect.com/science/article/abs/pii/S037843712200019X&quot;&gt;Hou, 2022&lt;/a&gt;, &lt;a href=&quot;https://www.igi-global.com/article/content/72309&quot;&gt;Momtaz et al., 2011&lt;/a&gt;). This indirect approach, called social proof, makes audiences more likely to trust the message as they see it endorsed by others &lt;a href=&quot;https://www.researchgate.net/profile/Robert-Cialdini/publication/247746744_Compliance_with_a_Request_in_Two_Cultures_The_Differential_Influence_of_Social_Proof_and_CommitmentConsistency_on_Collectivists_and_Individualists/links/0a85e53b2ea7abeb1d000000/Compliance-with-a-Request-in-Two-Cultures-The-Differential-Influence-of-Social-Proof-and-Commitment-Consistency-on-Collectivists-and-Individualists.pdf&quot;&gt;(Cialdini et al, 1999)&lt;/a&gt;. When a recommendation comes from a figure they respect, like an expert or a community leader, it carries even more weight, increasing credibility and influence within the audience’s social circles (&lt;a href=&quot;https://d1wqtxts1xzle7.cloudfront.net/106486947/00913367.2015-libre.pdf?1697022261=&amp;amp;response-content-disposition=inline%3B+filename%3DGoing_Native_Effects_of_Disclosure_Posit.pdf&amp;amp;Expires=1730213178&amp;amp;Signature=Z~yHfjNFY8GSZTDkZZEITK-6yT4xLy4qT97aCYBa4d8EiY9OQRMuZhUV5FwsKE5wsrEuh5sYyIHVo9v~h36LzjIjtvKENSjp3QdEAiNTtHglYsEGpnlmKtUzvXkeR6XFJt7iPLu33K2sVA25Axc1LskCdxvJs8DloKbvk5f0qKT6xi-8asWamsMT5HDoTiph8BV0tB1ldICCc2CjruRMhiAW5-qmvEhpgxt9agm0qHEBETG0b8hBQ1zzh6WBK5LMAf0WzxKPa6CpyZLrhcxoL3aVbl21-rO2fzHt7nByw~YfL44KY1OpHpcxDrKvr9colRkM6KZNlTimRU8uYKxirg__&amp;amp;Key-Pair-Id=APKAJLOHF5GGSLRBV4ZA&quot;&gt;Wojdynski &amp;amp; Evans, 2016&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Direct brand messaging often has a limited impact on changing consumer behavior immediately. Instead, it shapes opinions over time through key figures in social networks (&lt;a href=&quot;https://link.springer.com/chapter/10.1007/978-3-658-09923-7_6&quot;&gt;Katz &amp;amp; Lazarsfeld, 1955&lt;/a&gt;). According to the “two-step flow” model, messages reach the so-called opinion leaders first, who then interpret and share this information with their communities. People are more likely to trust and act on the recommendations of these figures, whose endorsements feel more personal and relevant to their needs (&lt;a href=&quot;https://myscp.onlinelibrary.wiley.com/doi/abs/10.1016/j.jcps.2014.05.002&quot;&gt;Berger, 2014&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Marketing messages hardly ever reach people directly from the main brand. To successfully grow a brand, one needs to convince the opinion leaders. Let’s look at how the WordPress brand grew, shall we?&lt;/p&gt;
&lt;h2&gt;Bloggers, Believers, and Brand Builders: The Early Advocates of WordPress&lt;/h2&gt;
&lt;p&gt;WordPress grew mainly because a vibrant community of early bloggers acted as influential voices, what researchers call opinion leaders or influencers (e.g., &lt;a href=&quot;https://dl.acm.org/doi/abs/10.1145/1146598.1146628&quot;&gt;Kavanaugh, 2006&lt;/a&gt;; &lt;a href=&quot;https://elar.urfu.ru/handle/10995/82963&quot;&gt;Yefanow &amp;amp; Tomin, 2020&lt;/a&gt;). Initially, WordPress was a platform for bloggers: a hub for influential voices to connect and create. Many users shared their enthusiasm for the software, writing posts and spreading the word. Notable figures like Matt Mullenweg himself, Lorelle VanFossen, Liz Strauss, Chris Lema, and Syed Balkhi were among these early advocates. Their blogs reached thousands and inspired others to start their own WordPress blogs, gradually creating a vast network of niche blogs, each speaking to a unique audience but all promoting WordPress.&lt;/p&gt;
&lt;p&gt;This early community advantage set WordPress apart from other CMS platforms by fostering a loyal, decentralized network of opinion leaders. Each community leader shared WordPress’s benefits in their own way: Joost and Jono Alderson spoke at numerous SEO conferences, introducing WordPress to new audiences, while agency owners from companies like HumanMade and 10up spread the word to clients. Hosting companies like WP Engine and Pagely organized events like Decode and Pressnomics, which were dedicated to educating and inspiring their communities about (the business of) WordPress.&lt;/p&gt;
&lt;p&gt;The strength of WordPress’s growth strategy came from its network of bloggers, speakers, and opinion leaders, whose enthusiasm made promoting WordPress feel almost effortless. Their collective support was WordPress’s marketing machine, showcasing how “many hands make light work.”&lt;/p&gt;
&lt;h2&gt;Influencer Marketing: How WordPress Built a Movement&lt;/h2&gt;
&lt;p&gt;Today, influencer marketing is enormous. We often think of celebrities endorsing fashion brands or luxury goods getting commissions in return. However, WordPress had influencers long before it became a trend. Bloggers, speakers, and community leaders championed WordPress without needing pay—they were passionate about the platform. This genuine belief in WordPress as the best CMS made their message far more powerful and persuasive.&lt;/p&gt;
&lt;h2&gt;WordPress YouTubers: Reaching a New Generation&lt;/h2&gt;
&lt;p&gt;Today’s WordPress influencers reach new audiences, especially on YouTube, appealing to a younger, often less tech-focused crowd. Creators like &lt;a href=&quot;https://www.youtube.com/@ferdycom/community&quot;&gt;Ferdy Korpershoek&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/@darrelwilson&quot;&gt;Darrel Wilson&lt;/a&gt;, and &lt;a href=&quot;https://www.youtube.com/@WPCrafter&quot;&gt;WPcrafter&lt;/a&gt; have hundreds of thousands of subscribers, gaining viewership far beyond the official WordPress channel.&lt;/p&gt;
&lt;p&gt;These YouTubers are compensated for their content through advertising and affiliate partnerships, yet WordPress doesn’t pay them. Instead, they’re supported by companies and brands in the WordPress ecosystem, such as Elementor, GoDaddy, Yoast, and Awesome Motive. This diverse sponsorship shows how WordPress has inspired a community-driven network of old and new advocates essential to its growth.&lt;/p&gt;
&lt;h2&gt;WP Community and Automattic – symbiotic relationship&lt;/h2&gt;
&lt;p&gt;Many WordPress companies would never have grown without Automattic’s unrelenting efforts to improve WordPress, the software. However, Automattic, WordPress.com, Woocommerce, and Jetpack would not have grown so much without the unrelenting marketing efforts of the WordPress community.&lt;/p&gt;
&lt;p&gt;The growth of WordPress is a testament to what a passionate community can achieve together. Automattic’s ongoing software innovations and the community’s commitment to spreading the word have created a symbiotic ecosystem that benefits all. WordPress’s success story shows how community and collaboration can make an impact far beyond what any organization could accomplish alone.&lt;/p&gt;
&lt;h2&gt;In Conclusion: A Community-Driven Legacy&lt;/h2&gt;
&lt;p&gt;The WordPress journey from a simple blogging tool to the world’s leading CMS has been made possible by the passion and dedication of its community. From early bloggers to today’s YouTube influencers, countless voices have spread the WordPress mission. This collaboration — where Automattic’s innovations and the community’s marketing meet —demonstrates that true growth comes from empowering others. Together, we’ve created something much greater than any one brand or platform alone could achieve.&lt;/p&gt;
&lt;p&gt;Let’s hope that WordPress remains a tool by the people, for the people.&lt;/p&gt;
&lt;p&gt;For more on how we approached WordPress marketing, see the posts on &lt;a href=&quot;/leading-marketing-communication-for-wordpress/&quot;&gt;leading marketing for WordPress&lt;/a&gt; and the &lt;a href=&quot;/marketing-wordpress-first-steps/&quot;&gt;first steps we took&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Open Source</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Transparency, Contribution, and the Future of WordPress</title><link>https://joost.blog/transparency-contribution-and-the-future-of-wordpress/</link><guid isPermaLink="true">https://joost.blog/transparency-contribution-and-the-future-of-wordpress/</guid><description>When we sold Yoast to Newfold in 2021, I quickly learned we had been incredibly naive. While at Yoast, I hadn’t realized how many deals were made between big co</description><pubDate>Thu, 26 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; This post was the start of a longer series. I followed it with posts on &lt;a href=&quot;/wordpress-leadership/&quot;&gt;WordPress leadership&lt;/a&gt;, &lt;a href=&quot;/wordpress-roadmap/&quot;&gt;what should be on the roadmap&lt;/a&gt;, and eventually &lt;a href=&quot;/path-forward-for-wordpress/&quot;&gt;a path forward through FAIR&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;When we sold Yoast to Newfold in 2021, I quickly learned we had been incredibly naive. While at Yoast, I hadn’t realized how many deals were made between big companies to promote their products. This was my first realization that not all companies thought and worked like we had been doing. I did learn a lot from the way Newfold Digital runs its businesses. Positive things!&lt;/p&gt;
&lt;p&gt;But I remember the way I felt back in 2021 so well. I was so disappointed in the community, these large WP businesses, and even the world. And I don’t want to presume that everyone is as naive as I was, but I think there’s a big gap between the community and the ‘big WordPress companies’. It is very hard for community members to understand why these companies are making so much money and are still fighting. So why is that? And is there a solution?&lt;/p&gt;
&lt;h2&gt;Inequality in information&lt;/h2&gt;
&lt;p&gt;There’s nothing wrong with making business deals to promote each other’s stuff. However, most members of our community (including me up until 2021) are not aware of those deals. There’s inequality in information, which makes it hard for community members to understand the decisions that big companies make — decisions concerning contributing to WordPress, for example. If we were more transparent about how money flows, it would be a little bit easier to understand.&lt;/p&gt;
&lt;h2&gt;Different objectives&lt;/h2&gt;
&lt;p&gt;Corporations that make hundreds of millions of dollars have different objectives than individuals and small businesses working in the community. These corporations have shareholders and other stakeholders, and those stakeholders want to make money. Again, there’s nothing wrong with that.&lt;/p&gt;
&lt;p&gt;However, many people in the WordPress community are driven by completely different objectives. They tend to be excited about open source, they want to make a better product or they want to organize an event for our amazing community. When we became part of Newfold Digital, I noticed the difference in objectives. All of a sudden, it became so very clear to us. I know now that I am just driven by different objectives: both Marieke and I don’t get excited about only making more money.&lt;/p&gt;
&lt;h2&gt;Inequality in compensation&lt;/h2&gt;
&lt;p&gt;Differences in objectives and inequality in information lead to very big differences in the amount of money that people make from WordPress. On the one hand, there are people who are in the community, volunteering their help and organizing events. In many cases, these people aren’t even compensated for their hours. On the other hand, there are people who are working on the boards of very large companies with very large salaries.&lt;/p&gt;
&lt;p&gt;The people who make the most money in WordPress are not the people who contribute the most (Matt / Automattic really is one of the exceptions here, as I think we are). And this is a problem. It’s a moral problem. It’s just not equitable.&lt;/p&gt;
&lt;p&gt;I agree with Matt about his opinion that a big hosting company such as WPEngine should contribute more. It is the right thing to do. It’s fair. It will make the WordPress community more egalitarian. Otherwise, it will lead to resentment. I’ve experienced that too.&lt;/p&gt;
&lt;p&gt;At Yoast, we contributed many many hours. During the first years of the Gutenberg project, our revenue growth dropped because we couldn’t develop new features for our product due to contributing so much to core. But we still made a lot of money. So, I didn’t really have a reason to complain, did I?&lt;/p&gt;
&lt;p&gt;At the same time, we were annoyed that other companies – much bigger than we were at the time – did not participate as much. We felt we were helping them get bigger while our growth stayed behind. I truly do understand where Matt is coming from.&lt;/p&gt;
&lt;h2&gt;Solution: transparency &amp;amp; governance&lt;/h2&gt;
&lt;p&gt;So I agree with Matt that big WordPress companies should have to contribute — preferably a lot, just like Automattic and some others do. But I would like to do that more openly: let everybody see how the money flows.&lt;/p&gt;
&lt;p&gt;Currently the way it works is that the money for trademark deals flows to Automattic, but we don’t know how much of &lt;a href=&quot;https://wordpress.org/five-for-the-future/pledge/automattic/&quot;&gt;the contributions Automattic does&lt;/a&gt; are paid for by Newfold, whom we now all know &lt;a href=&quot;https://wordpressfoundation.org/trademark-policy/&quot;&gt;are paying for the use of the trademark&lt;/a&gt;. Maybe the money should go directly into the foundation? If not, I think we should at least see how many of the hours contributed by Automattic are actually contributed by Newfold.&lt;/p&gt;
&lt;h3&gt;No taxation without representation&lt;/h3&gt;
&lt;p&gt;If we require everyone to contribute, we can call that taxation. It’s not as bad a word to me as to some other people, I think, but I do know that with taxation should also come representation.&lt;/p&gt;
&lt;p&gt;In my opinion, we all should get a say in how we spend those contributions. I understand that core contributors are very important, but so are the organizers of our (flagship) events, the leadership of hosting companies, etc. We need to find a way to have a group of people who represent the community and the contributing corporations.&lt;/p&gt;
&lt;p&gt;Just like in a democracy. Because, after all, isn’t WordPress all about democratizing?&lt;/p&gt;
&lt;p&gt;Now I don’t mean to say that Matt should no longer be project leader. I just think that we should more transparently discuss with a “board” of some sorts, about the roadmap and the future of WordPress as many people and companies depend on it. I think this could actually help Matt, as I do understand that it’s very lonely at the top.&lt;/p&gt;
&lt;p&gt;With such a group, we could also discuss how to better highlight companies that are contributing and how to encourage others to do so.&lt;/p&gt;
&lt;h2&gt;Parting thoughts&lt;/h2&gt;
&lt;p&gt;This is quite a long post. This week has been rough. But as I blog this, I also realize that I’m very thankful for what WordPress has brought me, and all the friendships I’ve been able to create because of it. I want to thank everyone whom I’ve been discussing this with the last few days for all their insights and for taking the time to talk to me and help me find my bearings on this rough sea. I also want to thank my wife Marieke, who is just the very best, and helped me put these words together.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>CMS market share June 2024 commentary</title><link>https://joost.blog/cms-market-share-june-2024-commentary/</link><guid isPermaLink="true">https://joost.blog/cms-market-share-june-2024-commentary/</guid><description>This commentary focuses on the data found in my monthly CMS market share report, which is generated automatically every month. This is the first time I’m writin</description><pubDate>Tue, 02 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This commentary focuses on the data found in my monthly &lt;a href=&quot;/cms-market-share/&quot;&gt;CMS market share report&lt;/a&gt;, which is generated automatically every month. This is the first time I’m writing the commentary separately from the data, so I’m still figuring things out, but as I was building this, I had so many interesting tidbits that I wanted to share immediately, but I also &lt;em&gt;really&lt;/em&gt; had to finish the code for the report. So, here it is, all these things I noticed:&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-squarespace-s-advertising-works&quot;&gt;Squarespace’s advertising works?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-tistory-a-new-kid-on-the-block&quot;&gt;Tistory: a new kid on the block?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-woo-vs-shopify-vs-more&quot;&gt;Woo vs. Shopify vs. more?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-elementor-keeps-on-growing&quot;&gt;Elementor keeps on growing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-adobe-is-not-so-lucky&quot;&gt;Adobe is not so lucky&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Squarespace’s advertising works?&lt;/h2&gt;
&lt;p&gt;Anecdotally, I heard about Squarespace advertising more, so their growth &lt;em&gt;could&lt;/em&gt; be due to their advertising. But it could also be something else. Their growth chart shows that whatever it is, it’s working. Note that these effects can rarely be immediate; a site needs to have reasonable traffic to start showing up in this dataset, which normally doesn’t happen a month after launch:&lt;/p&gt;
&lt;h2&gt;Tistory: a new kid on the block?&lt;/h2&gt;
&lt;p&gt;I had personally never heard of Tistory before, but it &lt;a href=&quot;https://en.wikipedia.org/wiki/Tistory&quot;&gt;turns out&lt;/a&gt; it’s been around for about 18 years, it just only showed up in our measurements. This is a good example of the gaps in all of the data sources. I hope we’ll be able to make this report more comprehensive over time and not run into gaps like these any more.&lt;/p&gt;
&lt;h2&gt;Woo vs. Shopify vs. more?&lt;/h2&gt;
&lt;p&gt;For a while, it seemed like the only two real eCommerce contenders were WooCommerce and Shopify. But looking at this data, Wix’s eCommerce offering is actually also growing &lt;em&gt;very&lt;/em&gt; rapidly and Squarespace’s Commerce solution is also not to be scoffed at, both only entered the market in 2021:&lt;/p&gt;
&lt;p&gt;The individual year-on-year growth rates aren’t too obvious from this chart. But if you calculate them for June 2024 vs. June 2023, they were showing quite a stark difference:&lt;/p&gt;
&lt;p&gt;WooCommerce5.6%Shopify15.5%Squarespace Commerce11.7%Wix eCommerce26.9%## Elementor keeps on growing&lt;/p&gt;
&lt;p&gt;One company in the WordPress space feels like it has no limits to it in terms of their growth: Elementor. Their growth chart over the last years can be described as “up and to the right”, with their most recent YoY growth rate at 23.3%:&lt;/p&gt;
&lt;p&gt;However, if you look at the number of installs for Yoast and Elementor, it looks like Elementor has surpassed Yoast judging by some of the numbers outed by both companies. However, Yoast is still much bigger in terms of market share judging by this dataset (note we only have data for Yoast SEO as of January 2020):&lt;/p&gt;
&lt;h2&gt;Adobe is not so lucky&lt;/h2&gt;
&lt;p&gt;Adobe acquired Magento &lt;a href=&quot;https://news.adobe.com/news/news-details/2018/Adobe-to-Acquire-Magento-Commerce/default.aspx&quot;&gt;about 6 years ago&lt;/a&gt;, and it seems it’s not been able to make it “work”. Similarly, Adobe Experience Manager, which in my personal opinion is an overpriced solution that lacks some of the most basic functionality to manage a site (particularly on the SEO front), was on a path downwards up until May, although it seems to have recovered a &lt;em&gt;tiny&lt;/em&gt; bit in June:&lt;/p&gt;
&lt;p&gt;That’s it for this month; I’d love to hear your thoughts and opinions on the data, the new format and questions you may have!&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>CMS Market share measured “right”</title><link>https://joost.blog/cms-market-share-measured-right/</link><guid isPermaLink="true">https://joost.blog/cms-market-share-measured-right/</guid><description>Today, I’m introducing a new CMS market share report on this site based on the HTTP Archive’s dataset. This report will be updated – automatically – every month</description><pubDate>Tue, 02 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, I’m introducing a new &lt;a href=&quot;/cms-market-share/&quot;&gt;CMS market share report&lt;/a&gt; on this site based on the &lt;a href=&quot;https://httparchive.org/&quot;&gt;HTTP Archive’s dataset&lt;/a&gt;. This report will be updated – automatically – every month. It doesn’t just contain CMSes; it also contains the most popular eCommerce platforms, WordPress page builders, and SEO plugins. Over time, we might expand this to more; suggestions are most certainly welcome.&lt;/p&gt;
&lt;p&gt;I had been slacking for more than a year in publishing a new version of the CMS market share statistics, partly because of time but mostly because I was unhappy with the data I was dealing with. In the last few weeks, I sat down and thought about it and discussed it with &lt;a href=&quot;https://www.jonoalderson.com/&quot;&gt;Jono Alderson&lt;/a&gt; (whom I owe massive thanks for his help as he was always the one helping me with my previous CMS market share reports). The conclusion was that we need to build this better. So we did.&lt;/p&gt;
&lt;p&gt;The report now also contains a list of all the technologies we track, allowing you to click on pages for each.&lt;/p&gt;
&lt;p&gt;This replaces the analysis in my earlier posts on &lt;a href=&quot;/wordpress-market-share-shrinking/&quot;&gt;WordPress&apos;s shrinking market share&lt;/a&gt; and &lt;a href=&quot;/elementor-wordpress-secret-growth-driver/&quot;&gt;Elementor as a growth driver&lt;/a&gt;, both of which relied on W3Techs data.&lt;/p&gt;
&lt;h2&gt;Separating data from commentary&lt;/h2&gt;
&lt;p&gt;In my previous reports, I added my commentary throughout the report. However, as this report will now be updated automatically every month, that’s no longer possible. So, instead, I will post more regularly on this blog in this category.&lt;/p&gt;
&lt;p&gt;The first post is straight after this one: &lt;a href=&quot;/cms-market-share-june-2024-commentary/&quot;&gt;CMS market share June 2024 commentary&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;On measuring market share&lt;/h2&gt;
&lt;p&gt;Measuring market share on the web is incredibly difficult: you need to define what “the market” is, and even the definition of a “CMS” is being discussed in some places. This report now assumes that the Chrome UX dataset, which is the basis for the HTTP Archive’s dataset, makes the right decisions to get a statistically reasonable “view” of the web. This does mean, however, that very small sites (in terms of traffic) are &lt;em&gt;not&lt;/em&gt; included in this dataset, which in itself leads to some bias.&lt;/p&gt;
&lt;h3&gt;WordPress&lt;/h3&gt;
&lt;p&gt;The biggest visible change in all of this is that WordPress has a lower market share than the numbers we’ve previously shared, which were based on &lt;a href=&quot;https://w3techs.com/&quot;&gt;W3Techs dataset&lt;/a&gt;. The difference is due to many things, including choosing whether to count subdomains (W3Techs counts all subdomains of a domain as 1 site).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;As always, with data like this, you should watch the trend and its direction.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the data we have for WordPress in this dataset:&lt;/p&gt;
&lt;h3&gt;Measurement fixes&lt;/h3&gt;
&lt;p&gt;While rebuilding this report, I made several pull requests on the &lt;a href=&quot;https://github.com/HTTPArchive/wappalyzer/&quot;&gt;HTTP Archive’s clone of the Wappalyzer&lt;/a&gt; project to improve measurements and fix categorization.&lt;/p&gt;
&lt;p&gt;For instance, the SEO plugins report shows that All in One SEO is incorrectly measured. It’s been consistently going down, and I know from other sources (including talking to Syed) that while it did go down for a while, it’s been going up again. It turns out that its measurement was broken, so I &lt;a href=&quot;https://github.com/HTTPArchive/wappalyzer/pull/35&quot;&gt;made a pull request&lt;/a&gt; to fix that.&lt;/p&gt;
&lt;p&gt;Enjoy the new report, my first analysis post and let me know in the comments or on Twitter or elsewhere what you think!&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Introducing a new plugin: Progress Planner</title><link>https://joost.blog/introducing-a-new-plugin-progress-planner/</link><guid isPermaLink="true">https://joost.blog/introducing-a-new-plugin-progress-planner/</guid><description>Ever since Marieke and I left Yoast, we’ve considered building a new WordPress product. We love working on something that people can use—something that will imp</description><pubDate>Fri, 07 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ever since Marieke and I &lt;a href=&quot;/yoast-joins-newfold/&quot;&gt;left Yoast&lt;/a&gt;, we’ve considered building a new WordPress product. We love working on something that people can use—something that will improve their website or make working on their website easier. About a year ago, we came up with something, and we’ve been playing with that idea ever since. We’ve been creating and developing in the past few months, and today, the first version is done! Let me tell you everything about Progress Planner!&lt;/p&gt;
&lt;h2&gt;Planning and organizing&lt;/h2&gt;
&lt;p&gt;Both Marieke and I aren’t particularly good at planning and organizing. &lt;a href=&quot;https://marieke.com/hyperfocusing-on-procrastination/&quot;&gt;Marieke noticed that when leaving Yoast&lt;/a&gt;. Over the years, we have built-in strategies and tactics for getting our own work organized. Managing a website consists of all these tasks that people easily forget or postpone. Could we build something that would make working on your website easier and more fun? Something like Duolingo or Fitbit for your website. That’ll keep you motivated to update and add stuff.&lt;/p&gt;
&lt;p&gt;So, that’s what we did. We built a plugin that motivates you to keep up the work on your website. We give you all kinds of stats on your activity, and you can unlock badges and keep streaks. Your activity score will go up if you update plugins or add content. Every Monday, we’ll email you with your progress and motivate you to keep going.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/screenshot-2.webp&quot; alt=&quot;Progress Planner&apos;s first set of badges shown in the WordPress admin&quot; /&gt;A screenshot of Progress Planner’s first badges# Pro version: helping people even more&lt;/p&gt;
&lt;p&gt;There will be a Pro version at some point later this year. We’re already developing features for that. Our idea is that in addition to motivating people to keep going with their websites, we should also help them with the work. We’re building several mini-courses that will be accessible from the WordPress backend, right in the edit screen. In those courses, we’ll help people how to draft or improve certain pages. How do you write a proper ‘about’ page? What does a good FAQ page contain?&lt;/p&gt;
&lt;p&gt;In addition to these courses, we’ll give people direction and help them decide what task to start next. We’ll also help them set goals and come up with proper to-do lists. The whole website maintenance thing can be rather overwhelming. We want to direct people to what tasks to take on next.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/screenshot-1.webp&quot; alt=&quot;Progress Planner&apos;s Website activity score view in the WordPress admin&quot; /&gt;A screenshot of Progress Planner’s Website activity score# Small features – significant improvements&lt;/p&gt;
&lt;p&gt;We’ve made a nice dashboard in the WordPress backend that allows for a small to-do list and will tell you when to update your plugins. Marieke updated the plugins on her site for the first time (I usually take care of it because she keeps forgetting). You’ll collect points for updating, which motivated her to do it herself. This is going to save me a lot of work!&lt;/p&gt;
&lt;p&gt;We’ve also added a to-do widget to the WordPress dashboards, allowing you to write down the tasks you want to work on and see them every time you log into your site. This is a small feature, but it will certainly help &lt;em&gt;me&lt;/em&gt; to remind myself of the things I need to get done on my site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/screenshot-3.webp&quot; alt=&quot;Progress Planner&apos;s to-do list widget on the WordPress dashboard&quot; /&gt;A screenshot of Progress Planner’s to-do list dashboard widget# Try out our beta!&lt;/p&gt;
&lt;p&gt;We’ve &lt;em&gt;just&lt;/em&gt; released the very first version of Progress Planner &lt;a href=&quot;https://wordpress.org/plugins/progress-planner/&quot;&gt;on WordPress.org&lt;/a&gt;. We’re very excited because it looks amazing and already helps us keep working on our websites. We even get a little competitive! Please try out the beta version of Progress Planner and let us know how to improve it further.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>A right-click / contextmenu logo popover</title><link>https://joost.blog/popover-contextmenu/</link><guid isPermaLink="true">https://joost.blog/popover-contextmenu/</guid><description>I always like it when I right-click a logo on a website because I need to get the logo, and the site’s designer has thought about this. If you right-click on th</description><pubDate>Mon, 27 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I always like it when I right-click a logo on a website because I need to get the logo, and the site’s designer has thought about this. If you right-click on the logo on our &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital site&lt;/a&gt;, you’ll get a popup that sends you to our logo page. As of today, that’s not a popup, but technically a popover, using the new popover API in HTML.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/cleanshot-2024-05-27-at-15.23.48@2x-1600x1044.webp&quot; alt=&quot;Custom right-click popover context menu on the Emilia Capital logo&quot; /&gt;An example of what we’re going to build in this post.## Step 1: the HTML&lt;/p&gt;
&lt;p&gt;The HTML for this is extremely simple, and the magic is in line 1:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div popover=&quot;manual&quot; id=&quot;logo_popover&quot;&amp;gt;
	&amp;lt;h3&amp;gt;Looking for our logo?&amp;lt;/h3&amp;gt;
	&amp;lt;p&amp;gt;You&apos;re in the right place.&amp;lt;br/&amp;gt;Check out &amp;lt;a href=&quot;/logo/&quot;&amp;gt;our logos &amp;amp; style page&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
	&amp;lt;button popovertarget=&quot;logo_popover&quot; popovertargetaction=&quot;hide&quot;&amp;gt;Close&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a popover; if you haven’t heard about it yet, popover is the new, browser-native way of doing modals, which &lt;em&gt;hugely&lt;/em&gt; simplifies building modals. Just how much it simplifies it? Well, if I wanted to open the above popover on click, all I’d need to do is add this somewhere:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button popovertarget=&quot;logo_popover&quot; popovertargetaction=&quot;show&quot;&amp;gt;
  Open popover
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, we’re not going to open it with a button in the HTML, but rather on *right-*clicking the logo, so we need the tiniest bit of JavaScript.&lt;/p&gt;
&lt;h2&gt;Step 2: the JavaScript&lt;/h2&gt;
&lt;p&gt;With JavaScript, we’re going to catch the &lt;code&gt;contextmenu&lt;/code&gt; event of the logo. This means we’re going to grab right clicks on that element. The JavaScript is actually super simple, too. We find the logo with &lt;code&gt;document.querySelector&lt;/code&gt;. We attach an event listener to the &lt;code&gt;contextmenu&lt;/code&gt; event of that logo. Then, we prevent the default (which is showing the context menu). Lastly, we find the above popover by its &lt;code&gt;id&lt;/code&gt; attribute and make it show up.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
document.querySelector(&quot;.is-logo-image&quot;).addEventListener(&apos;contextmenu&apos;, function(event) {
	event.preventDefault();
	document.querySelector(&quot;#logo_popover&quot;).showPopover();
	}, false);
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you do not set the popover above to close manually, so instead of &lt;code&gt;popover=&quot;manual&lt;/code&gt;, you just put &lt;code&gt;popover&lt;/code&gt;, you may find that the &lt;code&gt;popover&lt;/code&gt; closes immediately.&lt;/p&gt;
&lt;h2&gt;Step 3: the CSS&lt;/h2&gt;
&lt;p&gt;There’s a &lt;em&gt;tiny&lt;/em&gt; bit of styling needed to determine the color of the backdrop for your popover. Simply do this for a black backdrop, using the &lt;code&gt;::backdrop&lt;/code&gt; pseudo-element:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#logo_popover::backdrop {
  background: rgb(0 0 0 / 75%);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Step 4: doing this in WordPress&lt;/h2&gt;
&lt;p&gt;If you want to do this in WordPress, you can add it with a simple bit of code, for instance, like this, to your &lt;code&gt;wp_footer&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Output a popover for the logo.
 *
 * @link /popover-contextmenu/
 */
function joost_logo_popover_footer() {
?&amp;gt;
	&amp;lt;div popover=&quot;manual&quot; id=&quot;logo_popover&quot;&amp;gt;
		&amp;lt;h3&amp;gt;Looking for our logo?&amp;lt;/h3&amp;gt;
		&amp;lt;p&amp;gt;You are in the right place.&amp;lt;br/&amp;gt;Check out &amp;lt;a href=&quot;/logo/&quot;&amp;gt;our logos &amp;amp; style page&amp;lt;/a&amp;gt;.&amp;lt;/p&amp;gt;
		&amp;lt;button popovertarget=&quot;logo_popover&quot; popovertargetaction=&quot;hide&quot;&amp;gt;Close&amp;lt;/button&amp;gt;
	&amp;lt;/div&amp;gt;

	&amp;lt;script&amp;gt;
	document.querySelector(&quot;.is-logo-image&quot;).addEventListener(&apos;contextmenu&apos;, function(event) {
		event.preventDefault();
		document.querySelector(&quot;#logo_popover&quot;).showPopover();
	}, false);
	&amp;lt;/script&amp;gt;
&amp;lt;?php
}

add_action( &apos;wp_footer&apos;, &apos;joost_logo_popover_footer&apos; );
&lt;/code&gt;&lt;/pre&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Docusign to Documenso</title><link>https://joost.blog/docusign-to-documenso/</link><guid isPermaLink="true">https://joost.blog/docusign-to-documenso/</guid><description>In my quest to move most of my SaaS services to self-hosted open-source software, I’ve taken on the next step: our document signing infrastructure. Everyone see</description><pubDate>Fri, 15 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Migrating from SaaS to open-source&lt;/h2&gt;
&lt;p&gt;In my quest to move most of my SaaS services to self-hosted open-source software, I’ve taken on the next step: our document signing infrastructure. Everyone seems to be using Docusign, but as with all SaaS tools, that can get really expensive. While we don’t do a ton of contracts at Emilia Capital, I did already spend $25 / month, just to get some PDFs signed. &lt;a href=&quot;https://documenso.com/&quot;&gt;Documenso&lt;/a&gt; is a great solution!&lt;/p&gt;
&lt;p&gt;Documenso has basically all the features Docusign has with one exception so far: I’ve not been able to get it to do initials yet, which might get annoying for some document types. But otherwise it already looks pretty feature complete to me for a document signing solution.&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-migrating-from-saas-to-open-source&quot;&gt;Migrating from SaaS to open-source&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-software&quot;&gt;The software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-benefits&quot;&gt;The benefits&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#the-economics&quot;&gt;The economics&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-cost-of-docusign&quot;&gt;The cost of Docusign&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-the-cost-of-documenso&quot;&gt;The cost of Documenso&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The software&lt;/h2&gt;
&lt;p&gt;Installing Documenso is relatively trivial if you know how reverse proxies work, but let’s walk through the steps;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;You follow the install steps for &lt;a href=&quot;https://github.com/documenso/documenso/?tab=readme-ov-file#self-hosting&quot;&gt;self-hosting in their readme&lt;/a&gt;; it requires &lt;code&gt;npm&lt;/code&gt; and a bunch of other libraries on your system and you need to have a Postgres database running.&lt;/p&gt;
&lt;p&gt;A few things I found: I didn’t need the marketing site, so I removed &lt;code&gt;--filter=@documenso/marketing&lt;/code&gt; from the &lt;code&gt;start&lt;/code&gt; command in &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Also, make sure to not add trailing slashes to the URLs in your &lt;code&gt;.env&lt;/code&gt; file, that caused me a few minutes of debugging.&lt;/p&gt;
&lt;p&gt;You’ll probably want to disable registration, so set &lt;code&gt;NEXT_PUBLIC_DISABLE_SIGNUP&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Make sure to install the &lt;code&gt;next&lt;/code&gt; cli on your server, like so:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;npm install --global next
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Make sure to run Documenso as a service. On my Debian install that means copying their &lt;a href=&quot;https://github.com/documenso/documenso/?tab=readme-ov-file#run-as-a-service&quot;&gt;example file&lt;/a&gt; to &lt;code&gt;/etc/systemd/system/documenso.service&lt;/code&gt;, modifying the values to be correct for my system, and then enabling the service like so:&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;systemctl enable documenso.service
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Assuming you’re using Apache, create a virtual host file for the (sub-)domain you’re installing Documenso on, and make it look something like this, assuming you’re running Documenso on port 3000 (if not, just change it in your service file that we created in the previous step and here). This assumes you’re doing SSL with Let’s encrypt / certbot.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:443&amp;gt;
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/sign.example.com
	ServerName sign.example.com

	ErrorLog ${APACHE_LOG_DIR}/sign.example.com-error.log
	CustomLog ${APACHE_LOG_DIR}/sign.example.com-access.log combined

	SSLEngine on
	SSLCertificateFile /etc/letsencrypt/live/sign.example.com/fullchain.pem
	SSLCertificateKeyFile /etc/letsencrypt/live/sign.example.com/privkey.pem

	ProxyRequests Off
	ProxyPreserveHost On
	ProxyVia Full
	ProxyPass / http://localhost:3000/
	ProxyPassReverse / http://localhost:3000/
&amp;lt;/VirtualHost&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Email sending&lt;/h3&gt;
&lt;p&gt;For the email sending part, Documenso allows you to put several different types of services into your &lt;code&gt;.env&lt;/code&gt; file. I use Postmark for everything, so I used it here too, with just its SMTP sending service.&lt;/p&gt;
&lt;p&gt;Once you’ve done all that, you should have something that looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/documenso-screenshot.webp&quot; alt=&quot;Screenshot of the Documenso interface&quot; /&gt;## The benefits&lt;/p&gt;
&lt;p&gt;Next to the cost benefits, more on that below, there are a few other benefits:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You know where your data is and where it’s hosted. If you’re a European company, you should probably set this up on a European server and save yourself a lot of hassle with legal.&lt;/li&gt;
&lt;li&gt;You can more easily, without cost restraints, add team members.&lt;/li&gt;
&lt;li&gt;If you’re building another app, the idea for Documenso is that you should be able to integrate it into your apps. Check out their site, it’s nice stuff!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The economics&lt;/h2&gt;
&lt;p&gt;Setting this up took me about 30 minutes, so from a time perspective it wasn’t a deep investment.&lt;/p&gt;
&lt;h3&gt;The cost of Docusign&lt;/h3&gt;
&lt;p&gt;Docusign was costing me $25 / month, which I’ve always found annoying for just getting some PDFs signed. It was also the reason to have only one account, which meant that I was doing some manual work myself because I owned the Docusign account that I didn’t hand off to a colleague. I probably should’ve never done that and just paid for another account, but the cost right now was $300 / year.&lt;/p&gt;
&lt;h3&gt;The cost of Documenso&lt;/h3&gt;
&lt;p&gt;Zero. Hosting cost: if I’d taken on a separate server for this: ~$5 a month. I didn’t though, so for us it’s no extra cost.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Documenso is a great tool to have for your company. If you have someone who can set it up for you, it’s a great addition to your companies toolbelt without having to pay for Docusign.&lt;/p&gt;
&lt;p&gt;Read my previous from SaaS to open-source guide: from &lt;a href=&quot;/helpscout-to-freescout/&quot;&gt;HelpScout to Freescout&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>HelpScout to Freescout</title><link>https://joost.blog/helpscout-to-freescout/</link><guid isPermaLink="true">https://joost.blog/helpscout-to-freescout/</guid><description>Everyone uses a lot of SaaS applications to manage their day-to-day business. So much that the cost of it becomes prohibitive. I’ve decided that I’m going to tr</description><pubDate>Thu, 07 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Migrating from SaaS to open-source&lt;/h2&gt;
&lt;p&gt;Everyone uses a lot of SaaS applications to manage their day-to-day business. So much that the cost of it becomes prohibitive. I’ve decided that I’m going to try to move as much of the software we use to run &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital&lt;/a&gt; from SaaS to open-source and self-hosted solutions as possible. I will document every step because this might help others do the same.&lt;/p&gt;
&lt;p&gt;The first step I took was moving from &lt;a href=&quot;https://www.helpscout.com/&quot;&gt;HelpScout&lt;/a&gt; to &lt;a href=&quot;https://freescout.net/&quot;&gt;Freescout&lt;/a&gt;. Email is still a very important process for us, as both the legal processes around investing and the invoicing processes of all our businesses are still tied to email. So, we were using HelpScout to manage that. When I started looking for an open-source alternative, Freescout immediately turned out to be a very solid system that was also incredibly easy to install. Since it looks incredibly similar, the switch was easy on our team.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/freescout-image.webp&quot; alt=&quot;Freescout screenshot&quot; /&gt;## The software&lt;/p&gt;
&lt;p&gt;Freescout is based on a traditional PHP + MySQL stack, with either NGINX or Apache in front of it. The &lt;a href=&quot;https://github.com/freescout-helpdesk/freescout/wiki/Installation-Guide&quot;&gt;install instructions&lt;/a&gt; are simple, because installing it really isn’t hard if you’ve ever done some work on a server. There are also packages for installers like Softaculous and Fantastico. You’ll need to ensure you have all their required PHP modules, but the installer makes it easy to see which modules you still need.&lt;/p&gt;
&lt;p&gt;I set Freescout up using &lt;a href=&quot;https://postmarkapp.com/&quot;&gt;Postmarkapp&lt;/a&gt; as an SMTP server for the outgoing email because I don’t want to use Sendmail or something like that on my server and consider email reliability super important. In reality, you can probably set that up fairly easily too.&lt;/p&gt;
&lt;p&gt;For the incoming mail, I used simple free Gmail accounts. Here there’s an important trick to know: first, create those accounts. Then, enable 2-factor authentication and create an application password in your Google settings (go to 2-factor authentication in your Google account and scroll down). Then go into Gmail for that account and enable IMAP. Then use that application password to log in to your Gmail account from Freescout, and everything should work seamlessly.&lt;/p&gt;
&lt;h3&gt;Modules&lt;/h3&gt;
&lt;p&gt;I added the following modules to our setup for now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/api-webhooks/&quot;&gt;API &amp;amp; Webhooks&lt;/a&gt; (needed for the migration)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/extended-attachments/&quot;&gt;Extended Attachments&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/reports/&quot;&gt;Reports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/saved-replies/&quot;&gt;Saved replies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/tags/&quot;&gt;Tags&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://freescout.net/module/workflows/&quot;&gt;Workflows&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;The benefits&lt;/h2&gt;
&lt;p&gt;There are a few benefits to self-hosting Freescout that might not be immediately obvious. Of course, there are economic advantages; more on that below. There are also legal advantages: all the data is on your premises. No need to tell users that you’re using a third-party service for support interactions.&lt;/p&gt;
&lt;p&gt;I think the biggest benefit for us as a company is the liberty to create more users and inboxes when we need them and not be bound by very restrictive account limitations or prohibitive costs. Which brings us to the economics:&lt;/p&gt;
&lt;h2&gt;The economics&lt;/h2&gt;
&lt;h3&gt;The cost of HelpScout&lt;/h3&gt;
&lt;p&gt;I’ve been a HelpScout customer for over a decade, we were big users at Yoast, but their pricing started annoying me more and more. We were paying $80 per month for 4 standard user accounts. We needed more than two inboxes, but we decided against using more and changed our process a bit because the cost would otherwise have gone up to $160 a month.&lt;/p&gt;
&lt;p&gt;So, over a year, the TCO (Total Cost of Ownership) of HelpScout would be 12 * $80 = $960. As our company grows a bit, that cost would go up. Had I used it the way I’m now using Freescout, I’d have 2 more users and more inboxes; the total cost per year would have been $2,880.&lt;/p&gt;
&lt;h3&gt;Cost of Freescout&lt;/h3&gt;
&lt;p&gt;For Freescout, since we host it on a server I already had running for other things, it’s free. But realistically, the monthly cost is like $5 for a &lt;a href=&quot;https://www.vultr.com/?ref=9597032-8H&quot;&gt;Vultr server&lt;/a&gt; that can run this (that link gets you $100 credit to test Vultr and gives me some credit, too). On top of that, you need an email account to receive the emails. As I mentioned, I just used free Gmail accounts for that.&lt;/p&gt;
&lt;p&gt;I did spend some money on modules for Freescout. They’re one-time costs, as they have lifetime licenses. The total cost of that was $67.94. The most expensive step was the data migration. You could probably do that yourself if you wanted to, but I used &lt;a href=&quot;https://help-desk-migration.com/&quot;&gt;help-desk-migration.com&lt;/a&gt; to get it done in under an hour. Cost of that: $375.&lt;/p&gt;
&lt;p&gt;ItemCostServer cost per year$60.00Modules – one-time fee$67.94Migration$375.00&lt;strong&gt;Total cost of the first year****$502.94&lt;strong&gt;&lt;strong&gt;Total cost second year&lt;/strong&gt;&lt;/strong&gt;$60.00&lt;/strong&gt;As you can see, the ROI of changing to Freescout will be positive in the first year. In the 2nd year, this is a very big cost reduction.&lt;/p&gt;
&lt;h2&gt;Conclusion: success! What’s next?&lt;/h2&gt;
&lt;p&gt;I’ll call this one a win. I hope it inspires companies to do the same. I’ve got quite a few other services I’m considering for the next step, but I’m open to your suggestions: what should be next?&lt;/p&gt;
</content:encoded><category>Open Source</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Playground Blueprint Builder</title><link>https://joost.blog/playground-blueprint-builder/</link><guid isPermaLink="true">https://joost.blog/playground-blueprint-builder/</guid><description>I have been playing with the WordPress Playground a lot. As I did, I wanted a way to quickly make a copy of an existing site to a new Playground. It seems an id</description><pubDate>Wed, 06 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been playing with the &lt;a href=&quot;https://wordpress.org/playground/&quot;&gt;WordPress Playground&lt;/a&gt; a lot. As I did, I wanted a way to quickly make a copy of an existing site to a new Playground. It seems an ideal way to test new plugins, themes, and other things you’d want to change.&lt;/p&gt;
&lt;p&gt;You do that by making a &lt;a href=&quot;https://wordpress.github.io/wordpress-playground/blueprints-api/index&quot;&gt;Blueprint&lt;/a&gt; with all the data for the site you want to mimic. So, I started working on a &lt;a href=&quot;https://github.com/emilia-Capital/blueprint-builder&quot;&gt;Blueprint Builder&lt;/a&gt;. It’s far from done and shouldn’t be run on a live site yet. But it has all the concepts of something that could be very useful. It makes a blueprint of the current site’s plugins, themes, and options. It even makes a WXR URL available through the REST API to download a site’s content into your Playground.&lt;/p&gt;
&lt;p&gt;If your site is relatively simple, it works (if I haven’t just broken it with my last commit). If you have a custom theme, it doesn’t play nicely yet, because it doesn’t download that (yet). It also ignores any plugins it doesn’t recognize from WordPress.org.&lt;/p&gt;
&lt;p&gt;I told &lt;a href=&quot;https://github.com/adamziel&quot;&gt;Adam Zielinski&lt;/a&gt; about it (he created Playground). He asked me to open-source it, as it could perhaps be used within the project. &lt;a href=&quot;https://github.com/WordPress/wordpress-playground/issues/539&quot;&gt;Similar ideas existed&lt;/a&gt; there as well, which I think is exciting. So I did, and the code is now &lt;a href=&quot;https://github.com/emilia-Capital/blueprint-builder&quot;&gt;available to all&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Cleaning up WordPress option clutter</title><link>https://joost.blog/cleaning-up-wordpress-option-clutter/</link><guid isPermaLink="true">https://joost.blog/cleaning-up-wordpress-option-clutter/</guid><description>Note: this plugin has received a ton of updates since its inception. It’s now been moved to Progress Planner, where the team there (including myself) are develo</description><pubDate>Thu, 29 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Note: this plugin has received a ton of updates since its inception. It’s now been moved to Progress Planner, where the team there (including myself) are developing it further. So please check out &lt;a href=&quot;https://progressplanner.com/plugins/aaa-option-optimizer/&quot;&gt;AAA Option Optimizer on Progress Planner’s website&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you run a WordPress site for a while, it’ll become cluttered. You’ve installed and uninstalled dozens of plugins over the course of your site being alive, and many of those plugins have left their footprint (their options) in your database. I’ve come up with a solution for this, which is the first step of something that I think can be even nicer.&lt;/p&gt;
&lt;h2&gt;Why is this a problem?&lt;/h2&gt;
&lt;p&gt;This wouldn’t be so bad if WordPress didn’t load all the options that are set to “autoload=yes” into its memory in one big load action. Since stray options stay behind in the database when you uninstall a plugin, those options are still loaded into the memory on every pageview. Every. Single. Time.&lt;/p&gt;
&lt;h2&gt;How can we solve this?&lt;/h2&gt;
&lt;p&gt;To solve this, we have to follow a few steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Determine how bad the problem is by seeing how many autoloaded options we have and how much memory is being used by those options right now.&lt;/li&gt;
&lt;li&gt;For a while, keep track of all the autoloaded options and whether they are being used. Simply put: every time an autoloaded option is used during page load, we store it. Then we compare the list of options that have been used with those that have been autoloaded. The difference between the two are all the options that are autoloaded but never used.&lt;/li&gt;
&lt;li&gt;Now we have a list that we can go through and decide to either delete the option entirely or, if you’re unsure, simply remove the autoload=yes from it. If you do the latter, everything keeps on working; the option just isn’t loaded on every page load.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You’re now asking yourself: that sounds quite simple, but… How do I do that?? Well. I’ve built a plugin for it 🙂&lt;/p&gt;
&lt;h2&gt;Meet AAA Option Optimizer&lt;/h2&gt;
&lt;p&gt;This plugin is called &lt;a href=&quot;https://progressplanner.com/plugins/aaa-option-optimizer/&quot;&gt;&lt;strong&gt;AAA Option Optimizer&lt;/strong&gt;&lt;/a&gt;. What’s with the funny name? Well, WordPress loads plugins alphabetically. To best measure which options are being loaded, it’s best for the plugin to be loaded &lt;em&gt;early&lt;/em&gt;. So… I called it AAA Option Optimizer!&lt;/p&gt;
&lt;p&gt;When you install the plugin, it measures the current standings immediately and stores them. No need for you to worry about that. Then you should go and click around your site. Be sure to hit every page, both in the front end and in the admin. You could use something like &lt;a href=&quot;https://www.screamingfrog.co.uk/seo-spider/&quot;&gt;Screaming Frog&lt;/a&gt; on the front end.&lt;/p&gt;
&lt;p&gt;Then you go to the plugin’s page. It’s under Tools. It’ll look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/aaa-option-optimizer-screenshot-1.webp&quot; alt=&quot;AAA Option Optimizer plugin screen listing unused WordPress options with remove actions&quot; /&gt;On this page, you remove the options that you no longer need, and you can optimize away. Be sure to make a backup before you do this, because you might of course break things. This is a power tool, don’t give it to people who don’t know what to do with it.&lt;/p&gt;
&lt;h2&gt;Future versions: auto optimization?&lt;/h2&gt;
&lt;p&gt;I actually think this could be turned into something that auto-optimizes. Removes autoload from options automatically when they’re never used, add autoload &lt;em&gt;to&lt;/em&gt; options that get called a lot but aren’t auto-loaded.&lt;/p&gt;
&lt;p&gt;I’d love to hear your thoughts!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update 15th April 2024: &lt;a href=&quot;https://wordpress.org/plugins/aaa-option-optimizer/&quot;&gt;the plugin made it to WordPress.org today&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>Development</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Designing plugins to be uninstalled</title><link>https://joost.blog/designing-plugins-to-be-uninstalled/</link><guid isPermaLink="true">https://joost.blog/designing-plugins-to-be-uninstalled/</guid><description>I feel a particular class of plugins in the WordPress world should be more prominent. Plugins that help you do something, which you then delete when you’re done</description><pubDate>Fri, 02 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I feel a particular class of plugins in the WordPress world should be more prominent. Plugins that help you do something, which you then delete when you’re done. There are a lot of these, and they’re super useful. I’d probably call them &lt;em&gt;utility plugins&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let me explain with an example: say you’ve been bitten by the Tags bug and have too many tags. (This is something I’ve been &lt;a href=&quot;https://fewertags.com/research/&quot;&gt;publishing about on the Fewer Tags site&lt;/a&gt;). You have too many tags; you need to do an audit, merge some of them, delete some, and you’re done. Fewer Tags Pro can help you do that. And then, because of how it’s designed, you can remove it! This is a superb thing for site owners because it means you don’t clutter your site. Not yet another plugin you have to update every other week, but it does help you perform your task faster.&lt;/p&gt;
&lt;h2&gt;Designing for uninstallation&lt;/h2&gt;
&lt;p&gt;It’s not something plugin developers do often: designing their plugins to be uninstalled. In the case of Fewer Tags Pro, it was clear that this plugin needed to create redirects. But that doesn’t mean I want it to &lt;em&gt;do&lt;/em&gt; redirects. That would mean building a redirect maintenance system and other annoying things they already have. I don’t want this plugin to add yet another one of those things. So, instead, it integrates with Yoast SEO Premium and Redirection. If you use either one, it will create the redirects in their redirect system. This means the plugin can be safely uninstalled.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Aside: this did mean those plugins needed to have APIs that allow integration. To be fair, both could have been better in that case, but I managed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There are quite a few plugins like this, and we’ve probably all used some of them:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Migration plugins like &lt;a href=&quot;https://wordpress.org/plugins/all-in-one-wp-migration/&quot;&gt;All in one WP migration&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;The famous &lt;a href=&quot;https://wordpress.org/plugins/regenerate-thumbnails/&quot;&gt;regenerate thumbnails&lt;/a&gt; plugin,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wordpress.org/plugins/find-my-blocks/&quot;&gt;Find my Blocks&lt;/a&gt;, awesome to find where you’re using blocks you’re trying to get rid of&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But you know what’s funny? Two of the above plugins have more than a million installs. Why are these plugins staying installed on sites? Because we don’t have an active system saying: “Hey, this is a type of plugin that should maybe not be on your site for months.” Maybe we should change that. Maybe we should add a flag in the plugin header or the readme that means: “This plugin is meant to be used for a short period.” We could then warn a site owner if a plugin like that has been installed on their site for more than a month and urge them to uninstall it.&lt;/p&gt;
&lt;h2&gt;Other metrics&lt;/h2&gt;
&lt;p&gt;We could give the developers of these plugins something in return if they set that flag! We could give them stats on the number of activations and de-activations they had on WordPress.org. They’d get this data instead of (or maybe next to) data on the number of active installations. That data is not normally available to plugin developers and would actually show these developers how often their plugin is being used.&lt;/p&gt;
&lt;p&gt;What do you think? Good idea? Suggestions for improvements?&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Research: WordPress publications misuse tags</title><link>https://joost.blog/research-wordpress-publications-misuse-tags/</link><guid isPermaLink="true">https://joost.blog/research-wordpress-publications-misuse-tags/</guid><description>This post was migrated to this site in January 2025 when we took down the Fewer Tags project site.</description><pubDate>Tue, 30 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;This post was migrated to this site in January 2025 when we took down the Fewer Tags project site.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Tags are not used correctly in WordPress. Approximately two-thirds of WordPress websites using tags are using (way) too many tags. This has significant consequences for a site’s chances in the search engines – especially if the site is large. WordPress websites use too many tags, often forget to display them on their site, and the tag pages do not contain any unique content. In this post, I’ll discuss these three main tag problems I’ve diagnosed WordPress websites with and propose solutions.&lt;/p&gt;
&lt;h2&gt;About the research&lt;/h2&gt;
&lt;p&gt;As an &lt;a href=&quot;/about-me/&quot;&gt;SEO consultant&lt;/a&gt;, I have repeatedly seen the problems that tags and tag pages can create. To better understand the extent of the problem, I’ve conducted a small research project. I’ve investigated 30 sites and analyzed the way these sites dealt with tags and tagpages. My analysis included 10 popular WordPress-based mom blogs, 10 WordPress news blogs (based on WordPress and writing about WordPress), and 10 randomly chosen WordPress-based publications from the &lt;a href=&quot;https://wordpress.org/showcase/&quot;&gt;WordPress showcase&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The results shocked me. Mostly because I was expecting quite a few people to do it wrong; I wasn’t prepared for &lt;em&gt;how&lt;/em&gt; wrong it would be. One site had 3-4,000 posts and 30,000 tags. Another had 1,000+ tag pages that &lt;em&gt;all&lt;/em&gt; didn’t work. 87.5% of the sites that used tags had one or multiple tags with just one post in them.&lt;/p&gt;
&lt;p&gt;For each of these sites, I checked the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If they used tags.&lt;/li&gt;
&lt;li&gt;If they linked these tags from their posts.&lt;/li&gt;
&lt;li&gt;If they had a sensible number of tags compared to the size of their content library.&lt;/li&gt;
&lt;li&gt;If they had tag pages with just one post in them.&lt;/li&gt;
&lt;li&gt;If their tag pages actually &lt;em&gt;worked&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If their tag pages had a description/intro copy.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;First problem: way too many tags&lt;/h2&gt;
&lt;p&gt;People often add tags to their posts because WordPress suggests they do so. Perhaps people also add tags because they’re used to adding tags on other platforms, such as Instagram (&lt;a href=&quot;https://fewertags.com/dont-use-wordpress-tags-as-instagram/&quot;&gt;you shouldn’t do that&lt;/a&gt;). Sometimes (in about 10% of the websites in our research), websites even end up with more tags than posts. And that does not make &lt;em&gt;any&lt;/em&gt; sense. Tags are supposed to structure your content. A good use of tags means that the number of tags on a website is significantly smaller than the number of posts. In our research, we’ve treated sites with 5 posts per tag on average as having a good use of tags. In Figure 1, you can see that the majority of the sites that we analyzed do not smartly use tags.&lt;/p&gt;
&lt;p&gt;![Pie chart showing that:&lt;/p&gt;
&lt;p&gt;37.5% of sites have a good use of tags
20.8% of sites have 3-4 posts / tag
25% of sites have 1-2 posts per tag
16.7% of sites have more tags than posts](./images/use-of-tags-on-sites-1-png.webp)Figure 1: The (over-)use of tags on sites.People are often unaware that WordPress creates a specific new URL (a tag archive page) for each new tag you add to a page. WordPress lists all the articles with that specific tag on that tag page. But we see a lot of cases in which writers add a tag only once or twice. Of the sites we’ve analyzed, 87,5 % have tags containing only one post. Such a tag page will only list that one post. This page is very empty, and that’s bad for your chances of ranking in the search engines. It’s also a horrible user experience: you click from the article to the tag archive, because you want to read more content like that, and you’re presented with one choice: the article you just read. That’s a disappointment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/one-post-tags-png.webp&quot; alt=&quot;87.5% of sites have tags with just one post.&quot; /&gt;Figure 2: Percentage of sites with tags with just one post.## Second problem: Tags are not displayed&lt;/p&gt;
&lt;p&gt;The second problem with tags is that the tags aren’t even displayed on websites. I’ve encountered this problem in about one-third of the sites I analyzed. People add tags to a post before publishing, but their website’s theme (design) doesn’t show these tags. In many themes or designs, there is just no place for the tags.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/showing-tags-png.webp&quot; alt=&quot;pie chart showing that 37.5% of sites using tags do not show those tags to the user.&quot; /&gt;Figure 3: Percentage of sites using tags that don’t show those tags to the user.Having tags and not displaying them means you’re users are not using those tags at all. You’ll create a bunch of empty tag pages that nobody can visit. But since the tag pages &lt;em&gt;are&lt;/em&gt; created and listed in your site’s XML sitemap, search engines &lt;em&gt;will&lt;/em&gt; visit them, index them, etc. This takes resources away from your site pages that you want them to visit and index.&lt;/p&gt;
&lt;h2&gt;Third problem: Tag pages do not have descriptions&lt;/h2&gt;
&lt;p&gt;The third problem with tags has to do with thin tag pages. Each new tag that people add to a post on their site will create a new tag page. This tag page could be where visitors to your website will find more information about a certain topic. It should at least have a description or an introduction. However, my research shows that only one WordPress site has done some optimization for its tag pages.&lt;/p&gt;
&lt;p&gt;If you create a tag page, it would make sense to spend some time optimizing it. A tag page could be much more than just a list of links pointing toward articles with a tag. Ideally, A tag page should be a kind of homepage for a specific topic. It should help your visitors figure out and navigate to the content they want. However, the standard WordPress backend only allows for simple text editing on the tag pages. It provides an HTML field, which requires you to write code even if you want to add a simple thing like an internal link. This could be a reason why tag pages on most WordPress sites are so very thin.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/tag-archives-without-description-png.webp&quot; alt=&quot;pie chart showing that 95.8% of sites have tag archives without a description.&quot; /&gt;Figure 4: Percentage of sites that don’t have a description on their tag archives.## How do we solve these problems?&lt;/p&gt;
&lt;p&gt;We should use fewer tags, we should always display the tags we are using, and we should draft engaging introductions to our tag pages. That’s the solution. But that can be a lot of work.&lt;/p&gt;
&lt;p&gt;Only recently have I developed a quick solution with the Fewer Tags plugin. The Fewer Tags plugin (free) will help you automatically remove all tag pages with fewer than 10 posts. You can also alter that number to something else. It’s a straightforward and quick way to handle the problem immediately. However, installing the Fewer Tags plugin does not offer a sustainable solution for dealing with tags.&lt;/p&gt;
&lt;p&gt;Fewer Tags Pro, the premium version of the plugin I’ve built, will help you to work towards a more sustainable solution. The Pro plugin helps you to merge and redirect tags. Next to that, buying Fewer Tags Pro gives users access to an online course, explaining in-depth everything people need to know and understand about tags. We’ll help you display tags on your site and draft engaging introductions to those tag archive pages. The premise of Fewer Tags Pro is that we help you solve all the problems with tags and use tags to your advantage.&lt;/p&gt;
&lt;p&gt;Several sites (6 of the 30) in our analysis do not use tags. Within the category of WordPress websites, this percentage was especially high. We suspect these sites are aware of the problem with tags and decided to use only categories as a taxonomy. That makes sense and would also be a solution to the tag problem.&lt;/p&gt;
&lt;h2&gt;How did I do my research?&lt;/h2&gt;
&lt;p&gt;Conveniently, because all these sites run WordPress, they have pretty recognizable patterns. If they used an SEO plugin, I checked their XML sitemaps. If they didn’t, or I couldn’t find tags in those, I used the REST API to get the number of posts and tags on their site using these scripts. If that didn’t work, I ran a crawl, which I luckily only had to do on one site. After that, I randomly opened some articles on each site to see if those had tags displayed in them.&lt;/p&gt;
&lt;p&gt;When I found tag pages, I clicked on them and checked what they looked like, and I also clicked on articles on that tag page to see if they linked back to the tag page I just came from.&lt;/p&gt;
&lt;p&gt;To determine whether a site had too many tags, I looked at their XML sitemaps again or the REST API using the scripts linked before. Mostly because those are the easiest to check. I then divided their number of posts by their number of tags to determine a posts-to-tags ratio.&lt;/p&gt;
&lt;h2&gt;Why these checks?&lt;/h2&gt;
&lt;p&gt;Now, let me explain why I checked these particular things one by one:&lt;/p&gt;
&lt;h3&gt;Why check if they use tags?&lt;/h3&gt;
&lt;p&gt;Mostly because I wanted to see how many sites use tags and didn’t want to exclude sites that didn’t use tags. There’s nothing wrong with &lt;em&gt;not&lt;/em&gt; using tags.&lt;/p&gt;
&lt;h3&gt;What’s bad about not linking to tags?&lt;/h3&gt;
&lt;p&gt;If you’re not linking your posts to your tags, then what are you using them for? If you’re just using them for internal reasons, the best thing you can do is &lt;em&gt;not show them to visitors anywhere.&lt;/em&gt; If they’d done that, I wouldn’t have been able to find their tag pages.&lt;/p&gt;
&lt;h3&gt;When does a site have too many tags?&lt;/h3&gt;
&lt;p&gt;Tags should bring content together. They should allow you to go from one piece of content to another and meaningfully connect pages. If you have too many tags, this doesn’t help the user’s navigation, and you actually also add tons of URLs on your site that search engines have to index, so that’s not helpful.&lt;/p&gt;
&lt;p&gt;I determined a site had too many tags when the ratio of the number of posts divided by the number of tags was bigger than or equal to 5. So if you have 200 tags and 1,000 posts, I’d still deem that “acceptable”, even though I personally think that’s a bit much already.&lt;/p&gt;
&lt;h3&gt;What’s bad about one post in a tag?&lt;/h3&gt;
&lt;p&gt;You might be thinking: what’s bad about having one post in a tag? Well, let me explain. You get to a post. You liked that post so much that at the end of the post, you click on a tag underneath it, because you want to read more of that type of content. You are what we call an engaged reader. Then, the site presents you with a tag page that has just one post in it. The post you’ve just read. That’s a disappointment, right? That’s what we’d like to prevent.&lt;/p&gt;
&lt;h3&gt;What do you mean “if their tag pages work?”&lt;/h3&gt;
&lt;p&gt;On a few of the sites, the tag pages didn’t work. They were either empty (not listing the articles with the tag) or had big missing items, like missing a main heading on the page, or had clear errors. Tag pages are not pages people look at every day. That became clear.&lt;/p&gt;
&lt;h3&gt;Why does a tag need a description?&lt;/h3&gt;
&lt;p&gt;If someone clicks on a tag, they want to read more about a certain topic. With a description, you could help them. We explain how you could do this in &lt;a href=&quot;https://fewertags.com/too-many-tags-2-possible-solutions/&quot;&gt;our course about tags&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Multi-currency awesomeness with EDD &amp; Cloudflare</title><link>https://joost.blog/multi-currency-awesomeness-with-edd-cloudflare/</link><guid isPermaLink="true">https://joost.blog/multi-currency-awesomeness-with-edd-cloudflare/</guid><description>We’re getting ready at Emilia Projects (which is the “run our own projects part” of Emilia Capital) to sell our first plugin, the Pro version of Fewer Tags. We</description><pubDate>Fri, 26 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We’re getting ready at Emilia Projects (which is the “run our own projects part” of &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital&lt;/a&gt;) to sell our first plugin, the Pro version of &lt;a href=&quot;https://progressplanner.com/plugins/fewer-tags/&quot;&gt;Fewer Tags&lt;/a&gt;. We will sell that using &lt;a href=&quot;https://easydigitaldownloads.com/&quot;&gt;Easy Digital Downloads&lt;/a&gt; (EDD), which I have not used for &lt;em&gt;years,&lt;/em&gt; so I’ve been re-learning how to work with it. One of the things I’d found they had added since I started using it is a very nice &lt;a href=&quot;https://easydigitaldownloads.com/downloads/multi-currency/&quot;&gt;multi-currency plugin&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The plugin works mostly by adding &lt;code&gt;?currency=&amp;lt;currency&amp;gt;&lt;/code&gt; to the URL and allowing people to switch using that, but… I don’t necessarily want people to switch. We have two currencies, Euros (&lt;code&gt;EUR&lt;/code&gt;) and US dollars (&lt;code&gt;USD&lt;/code&gt;) and I want to give everyone in Europe &lt;code&gt;EUR&lt;/code&gt; and everyone else &lt;code&gt;USD&lt;/code&gt;. So, the default currency of the site is &lt;code&gt;USD&lt;/code&gt;, and we needed to find a way to make people in the EU default to &lt;code&gt;EUR&lt;/code&gt;. That’s where Cloudflare comes in!&lt;/p&gt;
&lt;p&gt;I started with an approach using &lt;a href=&quot;https://gist.github.com/jdevalk/bb3efd2932bb45bec885149017d278e0&quot;&gt;cookies and a worker&lt;/a&gt;, but this has the downside of not allowing Cloudflare caching to work properly, as the page differs depending on where you come from, but the URL doesn’t change. Thankfully Jono pointed this out to me when I was discussing it with him, and he pointed me towards transform rules.&lt;/p&gt;
&lt;h2&gt;Cloudflare transform rules&lt;/h2&gt;
&lt;p&gt;Cloudflare has added a &lt;em&gt;very&lt;/em&gt; nifty feature called transform rules that allow you to change URLs based on a number of variables. One of them is the country that people are visiting your site from. So, I created a transform rule that adds &lt;code&gt;?currency=EUR&lt;/code&gt; to every URL on the site for visitors from the EU (and GB). Here’s the “request match” part of that rule:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/edd-transform-rules.webp&quot; alt=&quot;Cloudflare Transform Rule &apos;request match&apos; configuration for EU visitors&quot; /&gt;Clicking this together in Cloudflare can be some work, so here’s the full expression. You can just put that in by clicking “Edit expression”:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(
  ip.geoip.country in { &quot;AT&quot; &quot;BE&quot; &quot;BG&quot; &quot;CY&quot; &quot;CZ&quot; &quot;DE&quot; &quot;DK&quot; &quot;EE&quot; 
   &quot;ES&quot; &quot;FI&quot; &quot;FR&quot; &quot;GR&quot; &quot;HR&quot; &quot;HU&quot; &quot;IE&quot; &quot;IT&quot; &quot;LT&quot; &quot;LU&quot; &quot;LV&quot; &quot;MT&quot; &quot;NL&quot; 
   &quot;PL&quot; &quot;PT&quot; &quot;RO&quot; &quot;SE&quot; &quot;SI&quot; &quot;SK&quot; &quot;GB&quot; }
  and not starts_with( http.request.uri.path, &quot;/wp-&quot; ) 
  and not http.request.uri.query contains &quot;edd_action&quot;
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first bit is simple: target everyone in these countries. The second “And” ensures you can still use your WordPress admin properly, and Cloudflare doesn’t add the query string we’re about to add to JavaScript that might be loaded, images, etc. The third “And” ensures you can still add and remove items from your cart and do other things. You might need to adjust this a bit for your site if you have other query parameters specific to your site.&lt;/p&gt;
&lt;p&gt;And here’s the “Then” part of this rule:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/cloudflare-transform-url.webp&quot; alt=&quot;Cloudflare Transform Rule &apos;then&apos; action rewriting the URL with ?currency=EUR&quot; /&gt;What’s probably not immediately clear to you but is super valuable: &lt;strong&gt;the user does not see that &lt;code&gt;?currency=EUR&lt;/code&gt; is secretly being added to the URL&lt;/strong&gt;! It only gets added in the communication between CloudFlare and your server and not towards the user. So, the cache is different because the URL that CloudFlare is requesting is different, but the user sees the exact same URL as his friend in the US! This isn’t just great for the user but also for your SEO &lt;em&gt;and&lt;/em&gt; your caching.&lt;/p&gt;
&lt;h2&gt;Displaying the right price&lt;/h2&gt;
&lt;p&gt;Now, the next challenge is displaying the right price and the right currency! Luckily, EDD has a shortcode for that: &lt;code&gt;[edd_price id=&amp;lt;ID of download&amp;gt;]&lt;/code&gt;; this will output the price correctly. So, you need to drop the habit of mentioning price straight in your copy and instead use that shortcode everywhere. And yes, you can use that shortcode within blocks, for instance, buttons. Doing this in the editor:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2024-01-26-at-11.43.41.webp&quot; alt=&quot;WordPress block editor using the edd_price shortcode inside a button block&quot; /&gt;Will show this on the front of the site:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2024-01-26-at-11.44.01.webp&quot; alt=&quot;Button rendered with price in euros for EU visitors&quot; /&gt;Or this if you’re from the US:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2024-01-26-at-11.45.09.webp&quot; alt=&quot;Button rendered with price in US dollars for US visitors&quot; /&gt;## You don’t need a currency switcher!&lt;/p&gt;
&lt;p&gt;By doing this, I would argue you do not need a currency switcher dropdown or buttons on your EDD site, and you can give people the correct currency, depending on where they come from, without any coding!&lt;/p&gt;
&lt;p&gt;Props: thanks to my colleague &lt;a href=&quot;https://github.com/aristath&quot;&gt;Ari Stathopoulos&lt;/a&gt; for helping me with some of these bits and to &lt;a href=&quot;https://www.jonoalderson.com/&quot;&gt;Jono Alderson&lt;/a&gt; for his advice!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>GitHub Actions to keep your WordPress plugin healthy</title><link>https://joost.blog/github-actions-wordpress/</link><guid isPermaLink="true">https://joost.blog/github-actions-wordpress/</guid><description>I absolutely love GitHub Actions and have been using them more and more for several purposes. As I’ve seen on some of the GitHub repositories I’ve been looking</description><pubDate>Tue, 23 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I absolutely love GitHub Actions and have been using them more and more for several purposes. As I’ve seen on some of the GitHub repositories I’ve been looking through, not everybody has as many nice use cases as we have in our repositories, so I thought I’d share some of my personal favorites.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;not-prose rounded-lg border-2 border-primary/20 bg-tertiary px-6 py-5 dark:border-accent/20 dark:bg-stone-900&quot;&amp;gt;
&amp;lt;p class=&quot;mb-2 text-lg font-semibold text-primary dark:text-accent&quot;&amp;gt;Want your AI agent to do this for you?&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;text-base leading-relaxed text-stone-700 dark:text-stone-300&quot;&amp;gt;Install my &amp;lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-wordpress-github-actions&quot; class=&quot;text-primary underline decoration-1 underline-offset-2 hover:no-underline dark:text-accent&quot;&amp;gt;WordPress GitHub Actions skill&amp;lt;/a&amp;gt;, or point your AI coding agent at this article. The skill analyzes your plugin&apos;s structure and drops in the workflows it needs.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;PHP&lt;/h2&gt;
&lt;h3&gt;Composer diff&lt;/h3&gt;
&lt;p&gt;It can be quite hard to see what has been changed in Composer files, especially the lock file, which makes reviewing changes to them in pull requests hard. When added to your repository, this &lt;a href=&quot;https://github.com/marketplace/actions/composer-diff&quot;&gt;composer diff action&lt;/a&gt; adds a comment to every pull request that touches the &lt;code&gt;composer.lock&lt;/code&gt; file and gives a human-readable table of changes to the composer file, as in the screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/composer-diff-example.webp&quot; alt=&quot;Example of a comment left by the composer diff package, showing a table with changes to the composer.lock file.&quot; /&gt;### Composer security check&lt;/p&gt;
&lt;p&gt;Another &lt;code&gt;composer.lock&lt;/code&gt; scanning action, this (quite popular) Github action aptly called “&lt;a href=&quot;https://github.com/marketplace/actions/the-php-security-checker&quot;&gt;The PHP Security checker&lt;/a&gt;” scans your &lt;code&gt;composer.lock&lt;/code&gt; file for security issues and tells you when you should be fixing them. You can see how we’ve implemented it, for instance, &lt;a href=&quot;https://github.com/Emilia-Capital/fewer-tags/blob/develop/.github/workflows/security.yml&quot;&gt;in our Fewer Tags plugin here&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Code style &amp;amp; linting&lt;/h3&gt;
&lt;p&gt;If you’re coding, you’re making mistakes. Everyone does. But if you’re coding and you’re not doing everything you can to prevent those mistakes, you’re doing it wrong. That’s where code style &amp;amp; linting, and a few more tools come in. The first thing you should be running against your plugin is the &lt;a href=&quot;https://github.com/WordPress/WordPress-Coding-Standards&quot;&gt;WordPress Coding Standards&lt;/a&gt;. The name can lead you to easily misjudge this package. It doesn’t “just” judge that you’ve used the right number of spaces and the right type of naming conventions. It also tells you when you’re using functions that are discouraged, when you’re forgetting to add nonce checks even though you’re dealing with request data, when you’re not sanitizing or escaping strings before output, etc. WPCS is the first thing I run against every plugin I need to evaluate, whether for investment reasons or because I want to run it in one of our own projects.&lt;/p&gt;
&lt;p&gt;To run WPCS on every pull request and commit is actually fairly simple. Here’s a &lt;a href=&quot;https://github.com/jdevalk/comment-hacks/blob/trunk/.github/workflows/cs.yml&quot;&gt;good example implementation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another thing you should be running on every commit and pull request is a linter, both for your JavaScript and your PHP. Examples &lt;a href=&quot;https://github.com/Emilia-Capital/fewer-tags/blob/develop/.github/workflows/lint.yml&quot;&gt;here (PHP)&lt;/a&gt; and &lt;a href=&quot;https://github.com/Yoast/wordpress-seo/blob/trunk/.github/workflows/jslint.yml&quot;&gt;here (JS)&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;JavaScript&lt;/h2&gt;
&lt;h3&gt;Package-lock.json &amp;amp; Yarn.lock diff&lt;/h3&gt;
&lt;p&gt;These actions will make a human-readable version of changes to your &lt;code&gt;package-lock.json&lt;/code&gt; or &lt;code&gt;yarn.lock&lt;/code&gt; file, which can otherwise be quite opaque. For Yarn, you can use &lt;a href=&quot;https://github.com/marketplace/actions/yarn-lock-changes&quot;&gt;this action&lt;/a&gt;. I would recommend &lt;a href=&quot;https://gist.github.com/jdevalk/da9ac8f2acc6eada6dd10a8b114bc6cf&quot;&gt;a setup like this&lt;/a&gt;, where it only runs if your &lt;code&gt;yarn.lock&lt;/code&gt; actually changes. Similarly, you can use &lt;a href=&quot;https://github.com/marketplace/actions/npm-lockfile-changes&quot;&gt;this action&lt;/a&gt; to create a human-readable comment for your &lt;code&gt;package-lock.json&lt;/code&gt; file, with a setup &lt;a href=&quot;https://gist.github.com/jdevalk/daa721c3f7af75255f8562820cbaab9a&quot;&gt;like this example&lt;/a&gt; to only run when the &lt;code&gt;package-lock.json&lt;/code&gt; file has actually been changed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/yarn-lock-changes.webp&quot; alt=&quot;Yarn lockfile diff GitHub Action comment on a pull request&quot; /&gt;## Testing&lt;/p&gt;
&lt;h3&gt;PHPUnit&lt;/h3&gt;
&lt;p&gt;If you have unit tests (you should!!), you can, of course, run them. Feel free to copy &lt;a href=&quot;https://github.com/Emilia-Capital/fewer-tags/blob/develop/.github/workflows/phpunit.yml&quot;&gt;the implementation from our Fewer Tags&lt;/a&gt; plugin on how we run these.&lt;/p&gt;
&lt;h3&gt;Manual testing&lt;/h3&gt;
&lt;p&gt;For very simple manual testing, I recommend using the WordPress playground (which &lt;a href=&quot;/plugin-demos-with-the-wordpress-playground/&quot;&gt;I recently wrote about&lt;/a&gt;). I’ve created a &lt;a href=&quot;https://gist.github.com/jdevalk/62d523be19743fd495144ade4c65e54c&quot;&gt;very simple workflow&lt;/a&gt; action that leaves a comment on every pull request, with a link that opens the zip of that pull request in the Playground. This only works for plugins without a build process, so if you have an autoloader, it should work without that build, or you should adapt the workflow. But it’s proven very useful for quick testing of changes without having to spin up local environments or do &lt;em&gt;anything&lt;/em&gt; else, really. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/wordpress-playground-zip.webp&quot; alt=&quot;GitHub Actions pull request comment with a link to open the PR zip in WordPress Playground&quot; /&gt;&lt;/p&gt;
&lt;p&gt;It automatically updates the comment after each commit on a pull request, to the latest commit.&lt;/p&gt;
&lt;h2&gt;WordPress.org deploy&lt;/h2&gt;
&lt;p&gt;I’m too lazy to do all the manual Subversion work needed to release plugins; with &lt;a href=&quot;https://github.com/10up/action-wordpress-plugin-deploy&quot;&gt;10up’s WordPress deploy action&lt;/a&gt;, you don’t need to do that ever again. Combine it with a &lt;code&gt;.distignore&lt;/code&gt; file (&lt;a href=&quot;https://github.com/Emilia-Capital/fewer-tags/blob/develop/.distignore&quot;&gt;example&lt;/a&gt;) to really create clean deploys to WordPress.org while also having all your WordPress.org assets (headers, icons, screenshots) in a nice &lt;code&gt;.wordpress-org&lt;/code&gt; directory. In some of the implementations we have, like &lt;a href=&quot;https://github.com/jdevalk/comment-hacks/blob/trunk/.github/workflows/deploy.yml&quot;&gt;this Comment Hacks one&lt;/a&gt;, it even creates a release on GitHub automatically. All I have to do is merge a branch into my &lt;code&gt;main&lt;/code&gt; branch, tag it with the right version number and commit and push that tag to GitHub, and it does &lt;em&gt;all&lt;/em&gt; the work from there.&lt;/p&gt;
&lt;h2&gt;Share! What are your favorite actions?&lt;/h2&gt;
&lt;p&gt;I’d love to hear in the comments what your favorite GitHub actions are.&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress needs more dev-rel</title><link>https://joost.blog/wordpress-needs-more-dev-rel/</link><guid isPermaLink="true">https://joost.blog/wordpress-needs-more-dev-rel/</guid><description>The WordPress ecosystem is very good at talking to each other. We do so on platforms like the official WordPress Slack, Post Status, Twitter, and many other pla</description><pubDate>Mon, 01 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The WordPress ecosystem is very good at talking to each other. We do so on platforms like &lt;a href=&quot;https://make.wordpress.org/chat/&quot;&gt;the official WordPress Slack, Post Status, Twitter,&lt;/a&gt; and many other places. We are, however, not as good at keeping up with the rest of the open-source world – something &lt;a href=&quot;https://poststatus.com/open-source-beyond-wordpress/&quot;&gt;Marieke recently mentioned in her column too&lt;/a&gt; – and I think this is where we are missing many opportunities. In this post, I explain why WordPress needs more developer relations (dev-rel) and what we could do to fix it.&lt;/p&gt;
&lt;p&gt;I’ve recently been diving into a fascinating ecosystem of new open-source startups. I started by looking at the investment portfolio of &lt;a href=&quot;https://oss.capital/portfolio&quot;&gt;OSS Capital&lt;/a&gt;, which &lt;a href=&quot;https://ma.tt/&quot;&gt;Matt&lt;/a&gt; pointed me to. Automattic is an investor in them / partners with them; I don’t know the exact details. It has many interesting startups: apps like &lt;a href=&quot;https://cal.com/&quot;&gt;Cal.com&lt;/a&gt;, a Calendly alternative, which also allows you to self-host. Or &lt;a href=&quot;https://www.openstatus.dev/&quot;&gt;OpenStatus&lt;/a&gt;, an open-source monitoring &amp;amp; status page system. Or &lt;a href=&quot;https://formbricks.com/&quot;&gt;Formbricks&lt;/a&gt;, an open-source surveying platform. All these companies link to each other, talk about each other, they have “&lt;a href=&quot;https://formbricks.com/oss-friends&quot;&gt;OSS friends&lt;/a&gt;” pages on those sites telling their users about each other. But what bothers me: none of them talk about WordPress.&lt;/p&gt;
&lt;p&gt;Some of them &lt;em&gt;do&lt;/em&gt; WordPress a bit. For instance, &lt;a href=&quot;https://wordpress.org/plugins/cal-com/&quot;&gt;Cal.com made a WordPress plugin&lt;/a&gt; and then promptly forgot about it and never updated it again. And honestly: that’s a shame. It’s a shame not just for them but also for the WordPress ecosystem. And it happens because we have no one actively maintaining those relations and telling them: “Hey, you could have a ton of users for your platform if you made it a bit more easily accessible to WordPress users.” (While we’re at it, we could also help them set up better blogs. It’s apparently not fancy to use WordPress for those blogs (they mostly use Next.js), but the result is that most of their blogs are horrible.)&lt;/p&gt;
&lt;h2&gt;So let’s fix WordPress developer relations!&lt;/h2&gt;
&lt;p&gt;The fix to this problem is actually incredibly simple. We don’t need one individual to do this. We need to &lt;em&gt;all&lt;/em&gt; do this together. The power of WordPress is in the number of people we have! If you look at new open-source projects (and you should, I think a lot of these have the potential to save you thousands of dollars a year on SaaS fees), check whether they have WordPress integrations. If they don’t, create an issue on their GitHub. These people think and talk like “us” in many ways, and they also spend a lot of time on GitHub like every WordPress contributor does, so use that to your advantage!&lt;/p&gt;
&lt;p&gt;I’ll do this myself; I’ve already made an issue on Cal.com’s plugin repository to &lt;a href=&quot;https://github.com/calcom/wp-plugin/issues/7&quot;&gt;update their plugin&lt;/a&gt; and &lt;a href=&quot;https://github.com/calcom/wp-plugin/issues/8&quot;&gt;create a Gutenberg block&lt;/a&gt;. There are many of us; together, we could really reach out to all those new open-source tools and help them &lt;em&gt;and&lt;/em&gt; WordPress thrive!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Clickable WhatsApp links</title><link>https://joost.blog/clickable-whatsapp-links/</link><guid isPermaLink="true">https://joost.blog/clickable-whatsapp-links/</guid><description>The simple things make life easy. One of them is WhatsApp. And more specifically: customer service through WhatsApp. I love it when companies offer WhatsApp sup</description><pubDate>Sat, 30 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The simple things make life easy. One of them is WhatsApp. And more specifically: customer service through WhatsApp. I love it when companies offer WhatsApp support. What I love a little bit less is when they offer it, but they make me jump through hoops to get to it. I recently ordered some shirts from SuitSupply and ended up needing their support. This is what I ran into:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/whatsapp-email-links.webp&quot; alt=&quot;Screenshot of SuitSupply&apos;s FAQ page, where their WhatsApp and email addresses both aren&apos;t linked.&quot; /&gt;The email address wasn’t linked and thus not clickable, nor were the WhatsApp numbers. Talking about making me work for it… Now, I don’t even need to &lt;em&gt;know&lt;/em&gt; your WhatsApp number. I just need you to give me a link that works. And this stuff ain’t hard, so here comes the explanation:&lt;/p&gt;
&lt;h2&gt;How to create Clickable WhatsApp links&lt;/h2&gt;
&lt;p&gt;WhatsApp makes linking to a chat on WhatsApp super simple by allowing you to link to their &lt;code&gt;wa.me&lt;/code&gt; short domain. So, instead of showing that number in full, making it all look super complicated, just do the following:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;https://wa.me/31655110516&quot;&amp;gt;Talk to us on WhatsApp&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Leave out all the spaces, dashes, and pluses. You can even add a text to start with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;https://wa.me/31655110516?text=I%20need%20help%20with&quot;&amp;gt;Talk to us on WhatsApp&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On &lt;a href=&quot;https://faq.whatsapp.com/5913398998672934&quot;&gt;their official support page&lt;/a&gt;, WhatsApp also provide official “chat on WhatsApp” buttons, but I’d probably just use a text link myself. That’s it! Easy does it.&lt;/p&gt;
&lt;p&gt;And then it could look something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/whatsapp-email-links-after.webp&quot; alt=&quot;SuitSupply FAQ page with WhatsApp numbers and email address rendered as clickable links&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now, of course, they really should underline their links too, but that’s another matter.&lt;/p&gt;
&lt;h2&gt;Post Scriptum: other links&lt;/h2&gt;
&lt;p&gt;So, you really also should link all email addresses with &lt;code&gt;mailto:&lt;/code&gt; links and phone numbers with &lt;code&gt;tel:&lt;/code&gt; links, so I can click them and start writing that email or start calling you:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;tel:0031655110516&quot;&amp;gt;Call us&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;mailto:service@suitsupply.com&quot;&amp;gt;Mail us&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s not just WhatsApp links, everything that you expect me to do on a phone, you should be making it easier for me, and adding links like this is super simple.&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Good-looking GitHub profile pages</title><link>https://joost.blog/good-looking-github-profile-pages/</link><guid isPermaLink="true">https://joost.blog/good-looking-github-profile-pages/</guid><description>After my blog post about healthy GitHub repositories, I learned that many people didn’t know how to create .github repositories and what you can do with those.</description><pubDate>Wed, 20 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After my &lt;a href=&quot;/healthy-github-repository/&quot;&gt;blog post about healthy GitHub repositories&lt;/a&gt;, I learned that many people didn’t know how to create &lt;code&gt;.github&lt;/code&gt; repositories and what you can do with those. That automatically also means that you don’t know about organization profile pages, so I wrote a quick post about those, too, which you’re reading now. I’ll start by explaining how to make a good-looking organization page, and then we’ll talk about your &lt;a href=&quot;#h-personal-github-profile&quot;&gt;&lt;em&gt;personal&lt;/em&gt; GitHub profile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;not-prose rounded-lg border-2 border-primary/20 bg-tertiary px-6 py-5 dark:border-accent/20 dark:bg-stone-900&quot;&amp;gt;
&amp;lt;p class=&quot;mb-2 text-lg font-semibold text-primary dark:text-accent&quot;&amp;gt;Want your AI agent to do this for you?&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;text-base leading-relaxed text-stone-700 dark:text-stone-300&quot;&amp;gt;Install my &amp;lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-github-profile-optimizer&quot; class=&quot;text-primary underline decoration-1 underline-offset-2 hover:no-underline dark:text-accent&quot;&amp;gt;GitHub Profile Optimizer skill&amp;lt;/a&amp;gt;, or point your AI coding agent at this article. The skill reviews your personal or organization profile and generates an optimized profile README.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;A good-looking GitHub organization page&lt;/h2&gt;
&lt;p&gt;The Emilia Capital GitHub organization profile is very new and a bit empty, but it already looks quite good:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2023-12-20-at-20.56.17.webp&quot; alt=&quot;Screenshot of the Emilia Capital GitHub profile, showing a README file.&quot; /&gt;### 1. Add a profile readme&lt;/p&gt;
&lt;p&gt;The fact that this looks good is the result of a few things. First, and most importantly, it’s the &lt;code&gt;profile/README.md&lt;/code&gt; in our &lt;code&gt;.github&lt;/code&gt; repository, which renders above our pinned repositories. This allows you to explain who you, as an organization, are and point to interesting links. It’s really as simple as this: create a &lt;code&gt;.github&lt;/code&gt; repository, which you should for reasons I outlined in my previous post anyway, add a &lt;code&gt;profile&lt;/code&gt; directory and create a &lt;code&gt;README.md&lt;/code&gt; file in that directory.&lt;/p&gt;
&lt;h3&gt;2. Pin your most interesting repositories&lt;/h3&gt;
&lt;p&gt;Of course, ensure those repositories have good descriptions, so people know why they should check them out. In practice, a simple first step is pinning your most starred repositories.&lt;/p&gt;
&lt;h3&gt;3. Add your company URL and social profiles&lt;/h3&gt;
&lt;p&gt;Go to your organization settings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click your Avatar&lt;/li&gt;
&lt;li&gt;Go to “Your Organizations”&lt;/li&gt;
&lt;li&gt;Click “Settings” for your organization.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Add your company’s URL, a description, and a few social profiles like your organization’s X/Twitter and LinkedIn. Then, make sure to &lt;em&gt;verify&lt;/em&gt; that organization URL by going to “Verified &amp;amp; approved domains” in the left-hand menu and following the steps outlined there.&lt;/p&gt;
&lt;h2&gt;A good-looking &lt;em&gt;personal&lt;/em&gt; GitHub profile&lt;/h2&gt;
&lt;p&gt;You can quite simply have a good-looking personal GitHub profile &lt;a href=&quot;https://github.com/jdevalk&quot;&gt;like mine&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2023-12-20-at-21.12.33.webp&quot; alt=&quot;Screenshot of Joost&apos;s personal GitHub profile page.&quot; /&gt;### 1. Create a profile readme&lt;/p&gt;
&lt;p&gt;Just like you can create a &lt;code&gt;.github&lt;/code&gt; repository for organizations, you can create a “magic” repository for your personal profile too. But this repository has a different name: it should be identical to your username. So for me, it’s &lt;code&gt;jdevalk&lt;/code&gt;, as &lt;a href=&quot;https://github.com/jdevalk/jdevalk&quot;&gt;you can see here&lt;/a&gt;. If you add a &lt;code&gt;README.md&lt;/code&gt; file to that personal repository, you get the same benefits as the &lt;code&gt;profile/README.md&lt;/code&gt; does for organization profiles. And then it renders above your pinned profiles as you can see in the screenshot.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: you can have a &lt;code&gt;.github&lt;/code&gt; repository for your personal GitHub too. It has &lt;em&gt;all&lt;/em&gt; the benefits of a &lt;code&gt;.github&lt;/code&gt; repository for your personal repositories; the only thing that’s different is the method for the profile page.&lt;/p&gt;
&lt;h3&gt;2. Pin some repositories&lt;/h3&gt;
&lt;p&gt;Here too, you should pin some favorite repositories, and make sure those repositories have proper descriptions. Of course, those repositories should be &lt;a href=&quot;/healthy-github-repository/&quot;&gt;healthy GitHub repositories&lt;/a&gt; themselves too.&lt;/p&gt;
&lt;h3&gt;3. Set your profile data&lt;/h3&gt;
&lt;p&gt;Go to your &lt;a href=&quot;https://github.com/settings/profile&quot;&gt;profile settings&lt;/a&gt; and add your social URLs, website, description, etc. Make sure to do this as it “connects” this repository to your important social profiles and then, create a link back from those social profiles and your website to your GitHub profile too. This makes people trust your profile a lot more.&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>How to create a healthy GitHub repository</title><link>https://joost.blog/healthy-github-repository/</link><guid isPermaLink="true">https://joost.blog/healthy-github-repository/</guid><description>As a result of investing in a fair few companies and getting a lot more requests to invest, I look at GitHub repositories a lot. I often run into GitHub reposit</description><pubDate>Tue, 19 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;As a result of &lt;a href=&quot;https://emilia.capital/&quot;&gt;investing in a fair few companies&lt;/a&gt; and getting a lot more requests to invest, I look at GitHub repositories a lot. I often run into GitHub repositories that don’t look well maintained. So, I thought I’d write a small article on what a healthy GitHub repository should look like.&lt;/p&gt;
&lt;p&gt;&amp;lt;div class=&quot;not-prose rounded-lg border-2 border-primary/20 bg-tertiary px-6 py-5 dark:border-accent/20 dark:bg-stone-900&quot;&amp;gt;
&amp;lt;p class=&quot;mb-2 text-lg font-semibold text-primary dark:text-accent&quot;&amp;gt;Want your AI agent to do this for you?&amp;lt;/p&amp;gt;
&amp;lt;p class=&quot;text-base leading-relaxed text-stone-700 dark:text-stone-300&quot;&amp;gt;Install my &amp;lt;a href=&quot;https://github.com/jdevalk/skills?tab=readme-ov-file#-github-repo-optimizer&quot; class=&quot;text-primary underline decoration-1 underline-offset-2 hover:no-underline dark:text-accent&quot;&amp;gt;GitHub Repo Optimizer skill&amp;lt;/a&amp;gt;, or point your AI coding agent at this article. The skill audits your repo against the checklist below and generates drop-in files for anything missing.&amp;lt;/p&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;In this post&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-repository-description-and-url&quot;&gt;Repository description and URL&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-readme-file&quot;&gt;README file&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-action-badges&quot;&gt;Action badges&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-what-does-this-thing-do&quot;&gt;What does this thing do?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-installation-instructions&quot;&gt;Installation instructions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#how-do-i-contribute&quot;&gt;How do I contribute?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-your-readme-txt-is-not-your-readme-md&quot;&gt;Your readme.txt is not your README.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#community-health-files&quot;&gt;Community health files&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-hack-a-github-repository&quot;&gt;Hack: a .github repository&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#tags-and-releases&quot;&gt;Tags and releases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-branches&quot;&gt;Branches&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Repository description and URL&lt;/h2&gt;
&lt;p&gt;The repository description should not be empty. If you go to your repository and see “No description provided”, it looks like you didn’t care. Click the cog, add a line of text describing the repository, and you’re done.&lt;/p&gt;
&lt;p&gt;If you haven’t set a URL for your project, add it now, too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2023-12-19-at-12.41.14@2x.webp&quot; alt=&quot;GitHub repository description and website URL settings panel&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;README file&lt;/h2&gt;
&lt;p&gt;A good README should, in my opinion, cover a few things. Let me discuss them in the order I believe they should appear in.&lt;/p&gt;
&lt;h3&gt;Action badges&lt;/h3&gt;
&lt;p&gt;Showing your default actions, like Code Style, Linting, Unit tests, etc., is a good idea. It shows me you care. GitHub &lt;a href=&quot;https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/adding-a-workflow-status-badge&quot;&gt;makes this incredibly easy&lt;/a&gt;, so not doing it is a no-go. If you don’t have any actions and this repository is for code, you should wonder why that is. Do you really think you don’t need linting or a code style? Think again.&lt;/p&gt;
&lt;h3&gt;What does this thing do?&lt;/h3&gt;
&lt;p&gt;No long prose is needed here, but one or two paragraphs that explain what the repository I’m looking at is for. It could be similar or the same as the description, but probably slightly longer.&lt;/p&gt;
&lt;h3&gt;Installation instructions&lt;/h3&gt;
&lt;p&gt;If your GitHub repository is public, you say, “Look at my/our code and contribute!” Of course, as a lover of open-source software, I love that. However, how to install your software, plugin, theme, or whatever it might be is often unclear. Your &lt;code&gt;README.md&lt;/code&gt; should start with, or very quickly, point me to clear and complete installation instructions. What do I do after I check out your code? Do I need to build anything? Please don’t make me read your code to determine your build steps.&lt;/p&gt;
&lt;h3&gt;How do I contribute?&lt;/h3&gt;
&lt;p&gt;The “How do I contribute” section can be tiny, as it should point to your &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; file, which we discuss below. A special note, either here or under its own heading, should be made about security issues. This should probably point to your security policy, which should live in your &lt;code&gt;SECURITY.md&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;Your readme.txt is not your README.md&lt;/h3&gt;
&lt;p&gt;WordPress plugins and themes come with a &lt;code&gt;readme.txt&lt;/code&gt;, that has &lt;a href=&quot;https://wordpress.org/plugins/readme.txt&quot;&gt;specific requirements&lt;/a&gt;. Your &lt;code&gt;README.md&lt;/code&gt; file should be different. And yes, that means you need both, but I think your &lt;code&gt;README.md&lt;/code&gt; doesn’t need to be shipped with your plugin or theme; it should describe (how to use/interact with) your repository, which is subtly but distinctly different from your built plugin or theme.&lt;/p&gt;
&lt;h2&gt;Community health files&lt;/h2&gt;
&lt;p&gt;GitHub allows for a fair few different &lt;a href=&quot;https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/creating-a-default-community-health-file&quot;&gt;community health files&lt;/a&gt;. I consider having these the absolute minimum:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CONTRIBUTING.md&lt;/code&gt;&lt;br /&gt;
This file should specify how to contribute, which code style to follow, and what to do and not do when doing pull requests or creating issues. Of course, you should have &lt;a href=&quot;https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/about-issue-and-pull-request-templates&quot;&gt;pull request templates and issue templates&lt;/a&gt; to guide these processes as well.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SECURITY.md&lt;/code&gt;&lt;br /&gt;
This file should first clarify where to report security issues and how quickly you expect to respond. It may contain more info, but this is the most basic requirement.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CODE_OF_CONDUCT.md&lt;/code&gt;&lt;br /&gt;
It’s smart to have a code of conduct. It’s easy to point to when you get unwanted behavior and also provides you with a guideline on how to handle problems. You don’t have to develop one yourself; I’d suggest using the amazing &lt;a href=&quot;https://www.contributor-covenant.org/&quot;&gt;contributor covenant&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Hack: a .github repository&lt;/h3&gt;
&lt;p&gt;If you create a &lt;code&gt;.github&lt;/code&gt; repository for your organization, you get to set up “default” health files for all your repositories in one go. See &lt;a href=&quot;https://github.com/Emilia-Capital/.github/&quot;&gt;the Emilia Capital .github repo&lt;/a&gt; as an example. This lets you set up default issue and pull request templates for all your repositories as well, which is super convenient.&lt;/p&gt;
&lt;h2&gt;Tags and releases&lt;/h2&gt;
&lt;p&gt;If you release software, I expect you to tag those releases in your repository, so it’s easy to find them, preferably with downloadable zips and a proper changelog. Far too often, I run into WordPress plugins that don’t do this and make life more complicated than it needs to be.&lt;/p&gt;
&lt;p&gt;It’s smart to add a &lt;code&gt;.gitattributes&lt;/code&gt; file to your repository, one of the things you can do with that file is define which files should be &lt;code&gt;export-ignore&lt;/code&gt;, so they’re not in your zip files.&lt;/p&gt;
&lt;h2&gt;Branches&lt;/h2&gt;
&lt;p&gt;If you have dozens or more stale branches on your GitHub repository, I will know (or assume) that you’re not in control. Clean them up. Delete no longer necessary branches, set a reminder for yourself to do this every so often, and make sure that you’re in control of all of them.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Clean up! This is your workspace, but most importantly, the space where you either foster collaboration or shun it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be sure to check out the sequel to this post: &lt;a href=&quot;/good-looking-github-profile-pages/&quot;&gt;good-looking GitHub profile pages&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>Development</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Plugin demos with the WordPress playground</title><link>https://joost.blog/plugin-demos-with-the-wordpress-playground/</link><guid isPermaLink="true">https://joost.blog/plugin-demos-with-the-wordpress-playground/</guid><description>For years, plugin developers have been looking for ways to demo their plugins to users. Users don’t read readme.txt files or plugin pages on WordPress.org. They</description><pubDate>Thu, 14 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For years, plugin developers have been looking for ways to demo their plugins to users. Users don’t read &lt;code&gt;readme.txt&lt;/code&gt; files or plugin pages on WordPress.org. They want to try and see a plugin in action for a bit, which is a much better way of seeing whether a plugin solves your problem. The new &lt;a href=&quot;https://developer.wordpress.org/playground/&quot;&gt;WordPress playground&lt;/a&gt; is a perfect way of doing that. So, the meta team at WordPress.org started building a preview button. While the first iteration was not ideal, a few weeks ago, @Tellyworth posted on Make WordPress about the &lt;a href=&quot;https://make.wordpress.org/meta/2023/11/22/plugin-directory-preview-button-revisited/&quot;&gt;revisited Playground experiment&lt;/a&gt;. I, of course, started playing with this feature, and now I can’t wait for it to go live for all users.&lt;/p&gt;
&lt;p&gt;First, let me show you what I’ve created. &lt;a href=&quot;https://playground.wordpress.net/?plugin=fewer-tags&amp;amp;blueprint-url=https%3A%2F%2Fwordpress.org%2Fplugins%2Fwp-json%2Fplugins%2Fv1%2Fplugin%2Ffewer-tags%2Fblueprint.json%3Frev%3D3010033&quot;&gt;Click this link to open the Fewer Tags playground&lt;/a&gt; in a new tab.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/CleanShot-2023-12-14-at-14.58.48.webp&quot; alt=&quot;Screenshot of the WordPress playground with the Fewer Tags plugin loaded and demo data loaded.&quot; /&gt;As you’ll see, this opens a WordPress Playground, with &lt;a href=&quot;https://progressplanner.com/plugins/fewer-tags/&quot;&gt;Fewer Tags&lt;/a&gt; installed, like in the screenshot above. But it does more. It brings you to the correct page for you to see what Fewer Tags does (the edit tags page), &lt;em&gt;and&lt;/em&gt; it loads a notification that explains what you can do there. This notification &lt;em&gt;only loads and shows on Playground sites&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let me explain to you how to do this:&lt;/p&gt;
&lt;h2&gt;Define a blueprint file&lt;/h2&gt;
&lt;p&gt;In the &lt;code&gt;assets&lt;/code&gt; directory of your WordPress plugin’s subversion checkout, create a subfolder called &lt;code&gt;blueprints&lt;/code&gt;. In that folder, create a file called &lt;code&gt;blueprint.json&lt;/code&gt;. You can keep this file incredibly simple. The file for Fewer Tags is here, and it reads just this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
	&quot;landingPage&quot;: &quot;\/wp-admin\/edit-tags.php?taxonomy=post_tag&quot;,
	&quot;steps&quot;: [
		{
			&quot;step&quot;: &quot;login&quot;,
			&quot;username&quot;: &quot;admin&quot;,
			&quot;password&quot;: &quot;password&quot;
		},
        {
			&quot;step&quot;: &quot;defineWpConfigConsts&quot;,
			&quot;consts&quot;: {
			  &quot;IS_PLAYGROUND_PREVIEW&quot;: true
			}
		}
	]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, all it does is define our landing page, define a constant we’ll use below, and log the user in so they can skip that step. Everything else is done with another bit of magic.&lt;/p&gt;
&lt;h2&gt;Loading a Playground specific file&lt;/h2&gt;
&lt;p&gt;In the main file of the plugin, we load a class specifically for the Playground, like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Detect if we&apos;re running on the playground, if so, load our playground specific class.
if ( defined( &apos;IS_PLAYGROUND_PREVIEW&apos; ) &amp;amp;&amp;amp; IS_PLAYGROUND_PREVIEW ) {
	$playground = new FewerTags\Playground();
	$playground-&amp;gt;register_hooks();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we have a &lt;a href=&quot;https://github.com/Emilia-Capital/fewer-tags/blob/develop/src/class-playground.php&quot;&gt;specific class&lt;/a&gt; that’s loaded, which takes care of a few things:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It generates some sample data, in this case, 2 tags and 20 posts.&lt;/li&gt;
&lt;li&gt;It creates a notice on the page we’re demoing on, with a tiny bit of explanation of what you can do here.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Of course, this is a relatively simple plugin and, thus, also a fairly simple demo setup, but you can make this demo setup as complex as you want. Because this file is part of your plugin, you can have all the text translated on WordPress.org. If you use autoloading, as you should, the file never loads if you’re not on the Playground, so it’s not a burden to performance.&lt;/p&gt;
&lt;p&gt;I think you’ll now agree with me that this is a game-changer for the WordPress plugin repository.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Saying goodbye to Marieke at Yoast</title><link>https://joost.blog/saying-goodbye-to-marieke-at-yoast/</link><guid isPermaLink="true">https://joost.blog/saying-goodbye-to-marieke-at-yoast/</guid><description>Last Friday (June 23rd 2023) we had Marieke‘s goodbye party from Yoast. With that, we’ve both completely stepped out at both Yoast and Newfold Digital, and can</description><pubDate>Mon, 26 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Last Friday (June 23rd 2023) we had &lt;a href=&quot;https://marieke.blog/&quot;&gt;Marieke&lt;/a&gt;‘s goodbye party from Yoast. With that, we’ve both completely stepped out at both Yoast and Newfold Digital, and can start to focus on new stuff. I wrote a speech for the occasion and thought it’d be a shame to not share it with the wider world:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When Marieke says goodbye to Yoast, I of course can not stay silent and so I have prepared this speech. To you all, but especially, to you, Marieke. As you and I, Marieke, &lt;a href=&quot;https://emilia.capital/&quot;&gt;started investing in other businesses&lt;/a&gt; and helping them along their road, I often reflect back on what we did at Yoast, and &lt;em&gt;how&lt;/em&gt; we did things. And every time I’m amazed by the incredible impact so many of your achievements, or maybe we can even call them inventions, have had.&lt;/p&gt;
&lt;p&gt;Of course, Marieke came up with the readability feature, and she created Yoast Academy. What was typical is how she did it: she had a clear vision of what she wanted to reach, and then started doing it: she did research, hiring some of the best people I’ve ever worked with in the process to help bring her ideas to life. It’s insane to realize now that when we started talking about readability, no one in the SEO space was talking about it. Now &lt;em&gt;every&lt;/em&gt; SEO tool worth their salt measures readability to some extent. There were more features of course, for instance Marieke’s continuous focus on internal linking and site structure was also super important for those features.&lt;/p&gt;
&lt;p&gt;She also created much of the branding, not the logo and colors, but the ideas around YoastCon, the magazines, everything around how she wanted people to perceive Yoast. She masterminded so much of that, that I don’t think I can ever do it all justice in a short speech here.&lt;/p&gt;
&lt;p&gt;Equally as important as those features and the branding of Yoast was, was what she did in creating our company culture. From the very beginning, Marieke was involved in the hiring process. While she usually stays away from the paperwork, something she happily leaves to me or someone else, she had very specific ideas about how she wanted our company to work. Some of those ideas she brought from her own previous work experience, but others were entirely new and I think true innovations.&lt;/p&gt;
&lt;p&gt;The premise of her HR policy was extremely simple and effective: be good to your employees and they’ll be good to you. She made it a point to know not just everyone’s name (which became hard enough already), but also know their partner’s names &lt;em&gt;and&lt;/em&gt; their kids’ names.&lt;/p&gt;
&lt;p&gt;She empowered women at Yoast, with her &lt;a href=&quot;https://empowerwoment.academy/&quot;&gt;Empowerwoment project&lt;/a&gt;, which I think we can at least partly credit for Yoast having such a good &amp;amp; diverse leadership team for so long. The core values project we did, which had the entire company align on “why do we do what we do?”, something that seems obvious when you’re a startup but becomes increasingly hard when a company grows. She was also so clearly the more subtle one: instead of, like me, just asking “is it done already?” all the time, she got everyone to “hup hup”, or: move faster.&lt;/p&gt;
&lt;p&gt;And then during COVID, Marieke started doing daily vlogs for the entire company. Pouring energy into the company in a way that I can still not really understand, with so much energy being required at home too. That time was tough and really brought to light the enormous amount of pressure that comes with being responsible for so many people, both users of our software and families relying on their income from us. That’s when we decided: this is no longer “it” for us, we need to sell.&lt;/p&gt;
&lt;p&gt;So: we did. We sold Yoast, that process was a journey in itself, and since then, we’ve had our struggles, as leaving a company you’ve founded is probably always going to be hard. But I am &lt;em&gt;really&lt;/em&gt; proud of what we did together, and I think, Marieke, you are too.&lt;/p&gt;
&lt;p&gt;In many ways, you, Marieke were the only one of us who really innately understood &amp;amp; felt the needs of a growing company. I’m often referred to as the sole founder of Yoast, and whilst that’s probably technically true, I do prefer, and want to hereby state unequivocally: without Marieke there as my co-founder, and yes, I think you should call yourself co-founder of Yoast, this company would not have been what it is today. And for that, I want to thank you, Marieke. You’re the best colleague I’ve ever had, and I look forward to working with you on much, much more, including, next to all our exciting investments, our next &lt;em&gt;own&lt;/em&gt; new project, a new WordPress plugin, which has the best working title ever: Mareekee. We hope to get to the point where you need Yoast and Mareekee for every WordPress site.&lt;/p&gt;
&lt;p&gt;I love you.&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>How to deal with plugin security issues</title><link>https://joost.blog/plugin-security-issues/</link><guid isPermaLink="true">https://joost.blog/plugin-security-issues/</guid><description>Dealing with security issues for your WordPress plugin can be hard and even a bit scary, certainly when it’s happening to you for the first time. In this post I</description><pubDate>Tue, 11 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Dealing with security issues for your WordPress plugin can be hard and even a bit scary, certainly when it’s happening to you for the first time. In this post I try to outline all the steps you should take as a plugin developer. So, let’s dive in. This isn’t a short post, so here’s a table of contents:&lt;/p&gt;
&lt;h2&gt;Table of contents&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-having-plugin-security-issues-reported-to-you&quot;&gt;Having plugin security issues reported to you&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-advertising-how-people-can-report-issues&quot;&gt;“Advertising” how people can report issues&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-encouraging-security-reports&quot;&gt;Encouraging security reports&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-learning-from-others&quot;&gt;Learning from others&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-fix-the-issue&quot;&gt;Fix the issue&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-how-fast-should-you-fix-security-issues&quot;&gt;How fast should you fix security issues?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-tell-your-users-about-your-plugin-security-issue&quot;&gt;Tell your users about your plugin security issue&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#h-changelog&quot;&gt;Changelog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-other-communication&quot;&gt;Other communication&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#h-don-t-be-too-hard-on-yourself&quot;&gt;Don’t be too hard on yourself&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Having plugin security issues reported to you&lt;/h2&gt;
&lt;p&gt;You probably use a platform like GitHub, or even the WordPress.org forums to get bugs reported to you. These are public channels which is super helpful for most of your bug reports. However, this is &lt;em&gt;not&lt;/em&gt; good for security issues. This is one of the reasons security issues in open source are a bit harder. You’re probably not used to secrecy, but where it comes to plugin security issues, you do need a bit of secrecy sometimes. So, you need a way for security researchers to contact you. And trust me, if your plugin gets popular, they &lt;em&gt;will&lt;/em&gt; contact you.&lt;/p&gt;
&lt;p&gt;You have a couple of options where it comes to receiving security reports. The simplest option is to set up an email address like &lt;code&gt;security@&lt;/code&gt; on whichever domain it is you use, or a contact form. Know though that you’ll get a lot of reports that you have to vet and not all of them are that useful. You also will find some security researchers will require you to sign emails with keys, functionality not offered by many email providers, which can be a hassle to set up.&lt;/p&gt;
&lt;p&gt;A better way (that’s relatively new) is using Patchstack’s – completely free!! – &lt;a href=&quot;https://patchstack.com/for-plugins/&quot;&gt;vulnerability disclosure program&lt;/a&gt;. They deal with validating the issue and only bother you when you actually have to fix something, with a very detailed report. They’ll deal with the security researcher, getting them to follow ethical disclosure policies, and because they’re a CNA (a “CVE numbering authority”), they can assign a so called CVE, a number by which a security issue can be referenced later. If they’re handling your vulnerabilities, it immediately gets you into the process of properly assigning CVEs. I think it’s completely awesome that they offer this service for free, and I’d recommend everyone to use it. I use it for &lt;a href=&quot;/code/&quot;&gt;my own plugins&lt;/a&gt;, see for instance &lt;a href=&quot;https://patchstack.com/database/vdp/yoast-comment-hacks&quot;&gt;the VDP page for my Comment Hacks plugin&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;“Advertising” how people can report issues&lt;/h3&gt;
&lt;p&gt;You should mention how people can report plugin security issues to you in at least a few of these places:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On the plugin’s webpage / website.&lt;/li&gt;
&lt;li&gt;On the plugin’s GitHub page (preferably through a &lt;a href=&quot;https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository&quot;&gt;security policy Security.md file&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;In the plugin’s &lt;code&gt;readme.txt&lt;/code&gt; and thus on the WordPress.org plugin page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make sure that you regularly &lt;em&gt;test&lt;/em&gt; your contact method. Trust me, you don’t want to miss these emails. They’re important.&lt;/p&gt;
&lt;h3&gt;Encouraging security reports&lt;/h3&gt;
&lt;p&gt;If you make money from your WordPress plugin(s), directly or indirectly, I’d highly suggest setting up a bug bounty program (examples: &lt;a href=&quot;https://hackerone.com/wordpress?type=team&quot;&gt;WordPress core’s bug bounty program on HackerOne&lt;/a&gt;, &lt;a href=&quot;https://yoast.com/security-program/&quot;&gt;Yoast’s bug bountry program&lt;/a&gt;, &lt;a href=&quot;https://bugcrowd.com/elementor/&quot;&gt;Elementor’s on Bugcrowd&lt;/a&gt;). It’ll encourage security researchers to look at your code a bit more. This might feel counterintuitive, because you don’t &lt;em&gt;want&lt;/em&gt; security issues, but the fact is that security issues are a fact of life. It’s best to admit that you’re human and benefit from other people scrutinizing your code. Being proactive about security is the &lt;em&gt;only&lt;/em&gt; way to make sure that you don’t find yourself feeling incredibly bad because lots of sites were hacked through your plugin.&lt;/p&gt;
&lt;h3&gt;Learning from others&lt;/h3&gt;
&lt;p&gt;When other plugins have security issues, you don’t gloat. You don’t think you’re better, trust me, you’re not. Everyone has security issues. What you do is you look at the report, and see what it says. Then you think and &lt;em&gt;check&lt;/em&gt; whether your own code would be susceptible to such an attack vector as well. If it is, you patch it.&lt;/p&gt;
&lt;h2&gt;Fix the issue&lt;/h2&gt;
&lt;p&gt;Once you’ve identified the problem in your code, you fix it. Of course. Then you go through your code and search for other places where the same thing happens. Then you check your other plugins, if you have any, and check for the same issue. Once you’re sure you’ve fixed all the instances of this particular issue, you submit the update to the plugin repository. I’m not going to go into the technical details of coding securely in this post, &lt;a href=&quot;https://developer.wordpress.org/apis/security/&quot;&gt;others&lt;/a&gt; &lt;a href=&quot;https://patchstack.com/articles/common-plugin-vulnerabilities-how-to-fix-them/&quot;&gt;can do&lt;/a&gt; that much better. But I will say this: having a pull request open on your public repository with words like “fix for security issue” in the description is probably not the best idea. This is where you have to keep things a bit of a secret until you’ve actually fixed the issue.&lt;/p&gt;
&lt;p&gt;Security researchers are often surprisingly helpful in testing your fixes. Communicate with them. If you do have a company like Patchstack vetting your reports, make sure to stay in touch with them as well.&lt;/p&gt;
&lt;h3&gt;How fast should you fix security issues?&lt;/h3&gt;
&lt;p&gt;I’m frequently baffled when I hear how long people take to fix plugin security issues. I honestly think most vetted security issues for plugins should be fixable within 48 hours. Literally 48 hours from receiving the report, I’d expect a good plugin developer / plugin development team to have shipped the fix. Depending on how severe the issue is, it could be ok to wait a few days if it happens to come in on a weekend. It’s &lt;em&gt;never&lt;/em&gt; OK to wait for weeks. As your software gets bigger, this might get a bit harder. WordPress bundles its core security releases together, I’m not part of that process but I know that that can take a bit longer. But if you’re one small group of people, just make sure to get that security release out, it’s better for everyone.&lt;/p&gt;
&lt;h2&gt;Tell your users about your plugin security issue&lt;/h2&gt;
&lt;p&gt;The hardest part of every security issues is always: how do we tell our users? I &lt;em&gt;know&lt;/em&gt; what you’re thinking “they may not trust us anymore”, “what if they switch to another plugin?”, etc. Trust me, I know those thoughts &lt;em&gt;deeply&lt;/em&gt;, I’ve been there. And trust me when I say: the truth &lt;em&gt;always&lt;/em&gt; prevails. So, don’t hide your security issues. Be open about them. Be confident enough to say: “We’ve been told about this issue. We’ve fixed it. You can rely on us to fix our security issues in a responsible manner. Please update to the latest version.” Users &lt;em&gt;will&lt;/em&gt; reward that behavior over time. So, let’s be clear about where we need to communicate:&lt;/p&gt;
&lt;h3&gt;Changelog&lt;/h3&gt;
&lt;p&gt;The most important bit first: of course your fix should be listed in your changelog, which should be in your &lt;code&gt;readme.txt&lt;/code&gt; and it &lt;em&gt;should&lt;/em&gt; clearly be identified as a security fix. If someone reported the issue to you, clearly credit the reporter (if they want to be credited). This again validates that you’re happy to receive security reports. You might want to use the &lt;code&gt;Upgrade notice&lt;/code&gt; functionality in the &lt;a href=&quot;https://wordpress.org/plugins/readme.txt&quot;&gt;&lt;code&gt;readme.txt&lt;/code&gt; standard&lt;/a&gt; as well.&lt;/p&gt;
&lt;h3&gt;Other communication&lt;/h3&gt;
&lt;p&gt;Pushing the update out and mentioning it in your &lt;code&gt;readme.txt&lt;/code&gt; is not enough if the issue is severe. If people’s sites might get hacked because of the issue, you’d better tell them, through Twitter, your newsletter, whatever channel it is you have, that you’ve released a security update.&lt;/p&gt;
&lt;h2&gt;Don’t be too hard on yourself&lt;/h2&gt;
&lt;p&gt;I know that I’ve taken security issues a bit too personal myself in the past. Security issues are a fact of life. Of course you need to think about security as you develop. Of course people can expect some level of basic security “posture”. But beyond that, security issues happen. Good developers / product owners stand out by how well they deal with them. I’d be very wary of any developer that says he never has security issues.&lt;/p&gt;
&lt;p&gt;If you have questions, feel free to ask them in the comments. If you need help communicating about a security release, I’m @joostdevalk on the WordPress Slack, and I’m always happy to help.&lt;/p&gt;
&lt;p&gt;Thanks to Oliver Sild from Patchstack and &lt;a href=&quot;https://clorith.net/&quot;&gt;Marius Jensen&lt;/a&gt; for their feedback on early drafts of this post. Photo by &lt;a href=&quot;https://unsplash.com/es/@jaye_haych?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Jaye Haych&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/s/photos/key-in-door?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The plugin developers guide to…&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you build a WordPress plugin, you obviously have to write code. But then, when you release it to the world, especially when you release it open source, there are things you have to do that you didn’t know about before. Things that don’t really show up in code tutorials. I’ve decided that I should share my experience in building one of the most popular plugins in the WordPress world in a series of posts, like this one about plugin security issues.&lt;/p&gt;
&lt;p&gt;If you have topics you’d like me to cover in this series, comment on this post or email me and let me know!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Gravity Forms notification routing with a lookup table</title><link>https://joost.blog/gravity-forms-notification-routing-with-a-lookup-table/</link><guid isPermaLink="true">https://joost.blog/gravity-forms-notification-routing-with-a-lookup-table/</guid><description>I had a need for complex routing of notification messages in Gravity Forms. The email could, depending on a dropdown field, go to some 50-60 different recipient</description><pubDate>Thu, 16 Feb 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I had a need for complex routing of notification messages in Gravity Forms. The email could, depending on a dropdown field, go to some 50-60 different recipients. So I needed a custom lookup table with notification recipients and a way to look up the recipient based on that dropdown field, without exposing all those recipient emails to the outside world. It turned out to be fairly simple once I knew how to approach it, but I thought I’d blog it to make it easier for everyone.&lt;/p&gt;
&lt;h2&gt;Backstory&lt;/h2&gt;
&lt;p&gt;For my local football club &lt;a href=&quot;https://svawc.nl/&quot;&gt;AWC&lt;/a&gt;, where I’m a volunteer, teams need to be emailed about upcoming games sometimes. Messages range from changing the date of a match, to telling the team that the game has been cancelled, etc. Historically, there’d be one email address for that and someone would manually forward those messages to the right team. As the club currently has some 55 teams, that’s becoming quite a bit of work. So I wanted to see if I could make that a bit simpler.&lt;/p&gt;
&lt;h2&gt;The setup&lt;/h2&gt;
&lt;p&gt;For this setup I’ve used my trusted forms plugin for the last decade, &lt;a href=&quot;https://www.gravityforms.com/&quot;&gt;Gravity Forms&lt;/a&gt;, plus a few other plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gravitywiz.com/documentation/gravity-forms-populate-anything/&quot;&gt;GP Populate Anything&lt;/a&gt; – one of many very useful plugins from GravityWiz.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.advancedcustomfields.com/&quot;&gt;Advanced Custom Fields&lt;/a&gt; – my go to plugin when I need to create custom fields.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wordpress.org/plugins/custom-post-type-ui/&quot;&gt;Custom Post Type UI&lt;/a&gt; – to lazily create a custom post type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now let’s go through the different steps I took for this:&lt;/p&gt;
&lt;h3&gt;The notification recipients&lt;/h3&gt;
&lt;p&gt;First, I created a post type &lt;code&gt;contact-persons&lt;/code&gt; with “supports” set to &lt;code&gt;false&lt;/code&gt; (or “none” in Custom Post Type UI), which means that it’ll not have a post editor etc. I then created a field group in Advanced Custom Fields (ACF), with 4 fields: the team name, the contact’s first and last name and an email address. I made sure that field group only showed on the &lt;code&gt;contact-persons&lt;/code&gt; custom post type:&lt;/p&gt;
&lt;p&gt;Now when I edit a contact person, it looks like this:&lt;/p&gt;
&lt;p&gt;I created a custom plugin for this setup, which has two functions. One takes care of the form routing, the other one takes care of saving the team name to the &lt;code&gt;post_title&lt;/code&gt; field. I could have left the Post title on the page, but I feel this is actually a better look for the form in terms of usability. This code is quite simple:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
  * Sets the team name as the title of the post type for team-contacts.
  *
  * @see &apos;save_post&apos;
  *
  * @param int     $post_id The post being saved.
  * @param WP_Post $post.   The post object.
  */
function joost_set_team_name_as_title( $post_id, $post ) {
    // If this is a revision, get real post ID.
    $parent_id = wp_is_post_revision( $post_id );

    if ( false !== $parent_id ) {
        $post_id = $parent_id;
    }

	if ( $post-&amp;gt;post_type !== &apos;team-contacts&apos; ) {
		return;
	}
	
    // Unhook this function so it doesn&apos;t loop infinitely.
    remove_action( &apos;save_post&apos;, &apos;joost_set_team_name_as_title&apos; );

    // Update the post, which calls save_post again.
  	$team_name = get_field( &apos;team_naam&apos;, $post_id );
	wp_update_post( [ &apos;ID&apos; =&amp;gt; $post_id, &apos;post_title&apos; =&amp;gt; $team_name ] );

    // Re-hook this function.
    add_action( &apos;save_post&apos;, &apos;joost_set_team_name_as_title&apos; );
}
add_action( &apos;save_post&apos;, &apos;joost_set_team_name_as_title&apos;, 10, 2 );
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The form&lt;/h3&gt;
&lt;p&gt;I built a contact form in Gravity Forms, just the way one normally would, and added on field to the very top: Which team do you want to send this message to? This field is a drop down, which I set to “Populate choices dynamically”, using the GP Populate Anything plugin. That field then takes the teams from the newly created custom post type and lists them here:&lt;/p&gt;
&lt;p&gt;As value, I kept the Post ID, because we’re going to use that in our second custom function, which does the routing. I hooked this to the generic &lt;code&gt;gform_notification&lt;/code&gt; filter and decided to check if the notification name matched what I used. You can also use a more specific filter, but this was easy enough for my use case:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Filter the notification recipient to the right team, if this is a team notification.
 *
 * @param array $notification The notification array.
 * @param array $form         Not used.
 * @param array $entry        The entry array.
 *
 * @return array The notification array.
 */
function joost_notification_emails_team( $notification, $form, $entry ) {
    // Only change notification to address for team notifications.
    if ( $notification[&apos;name&apos;] == &apos;Team notification&apos; ) {
 
        $field_id = 1; // The ID of the team select field.
 		$post_id  = rgar( $entry, $field_id );
		
        $notification[&apos;to&apos;] = get_field( &apos;email&apos;, $post_id );
    }
 
    return $notification;
}

add_filter( &apos;gform_notification&apos;, &apos;joost_notification_emails_team&apos;, 10, 3 );
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;The notification&lt;/h3&gt;
&lt;p&gt;Now we need to create our notification. It’s as simple as creating a notification that looks the way you want it to. You can add &lt;em&gt;any&lt;/em&gt; email in the To field, as it’ll be replaced by the code above anyway. The only thing that’s important is that you give the notification the same name as you’ve used in the code above.&lt;/p&gt;
&lt;p&gt;That’s it! Now you’ve got a working team contact form, where if you add teams, they show up in the drop down and the right contact person receives the email based on your “lookup table” post type. Notification routing done right!&lt;/p&gt;
&lt;h2&gt;Bonus points&lt;/h2&gt;
&lt;h3&gt;Disable Custom Post Type UI&lt;/h3&gt;
&lt;p&gt;One of the things I absolutely &lt;em&gt;love&lt;/em&gt; about the Custom Post Type UI setting is its export functionality. Under CPT UI → Tools → Get Code, it gives you the function to register your post type. Copy paste that code to your custom plugin and voila: you can disable Custom Post Type UI.&lt;/p&gt;
&lt;h3&gt;Create a better overview page&lt;/h3&gt;
&lt;p&gt;On the overview page, you’d see the team name and the date it was published. Not very useful. We can change that to: team name, contact name, contact email, and remove the date, with the following custom code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * Register columns for the edit contact persons overview page.
 * 
 * @param array $columns The existing columns.
 *
 * @return array The filtered columns.
 */ 
function joost_add_team_contact_columns( $columns ) {
	$columns[&apos;contact-name&apos;]  = &apos;Contact name&apos;;
	$columns[&apos;contact-email&apos;] = &apos;Email&apos;;

	unset( $columns[&apos;date&apos;] );

	return $columns;
}

// Register the columns, specifically for our custom post type.
add_filter( &apos;manage_team-contacts_posts_columns&apos;, &apos;joost_add_team_contact_columns&apos; );

/**
 * Output the column values.
 * 
 * @param string $column_name The name of the column.
 * @param int    $post_id     The post ID.
 */
function joost_team_contacts_column_content( $column_name, $post_id ) {
	if ( $column_name === &apos;contact-name&apos; ) {
		echo get_field( &apos;voornaam&apos;, $post_id ) . &apos; &apos; . get_field( &apos;achternaam&apos;, $post_id );
	}
	if ( $column_name === &apos;contact-email&apos; ) {
		printf( &apos;&amp;lt;a href=&quot;mailto:%1$s&quot;&amp;gt;%1$s&amp;lt;/a&amp;gt;&apos;, get_field( &apos;email&apos;, $post_id ) );
	}
}

// Handle the value for each of the new columns, only on this post type. 
add_action( &apos;manage_team-contacts_posts_custom_column&apos;, &apos;joost_team_contacts_column_content&apos;, 10, 2 );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, now we have a Gravity form with notifications routing, a management interface for teams and their contact persons and as you add teams or change contacts, the form updates dynamically and keeps sending notifications to the right people. Thanks to the various people in &lt;a href=&quot;https://poststatus.com/&quot;&gt;Post Status&lt;/a&gt;‘ #club channel that helped out with their suggestions!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress’ admin UI needs to be better</title><link>https://joost.blog/wordpress-admin-ui-needs-to-be-better/</link><guid isPermaLink="true">https://joost.blog/wordpress-admin-ui-needs-to-be-better/</guid><description>The WordPress’ admin UI needs to be drastically improved. It should be improved not just for WordPress core itself, but it should implement a simple and clearly</description><pubDate>Wed, 25 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The WordPress’ admin UI needs to be drastically improved. It should be improved not just for WordPress core itself, but it should implement a simple and clearly defined open design system, so that plugins and themes can use it to build their own interfaces. Now that Yoast’s new Settings UI is out in the open for everyone, it’s also clear for everyone that we’ve deviated from what we used to do, which was to use the “standard” WordPress admin UI components. The reason for that is simple: WordPress’ admin components are old fashioned and haven’t progressed as they should. We simply can’t build a modern interface with them. It saddens me enormously though, that we’ve had to take this step.&lt;/p&gt;
&lt;p&gt;The problem with us choosing our own path, is exactly that: we’re choosing our own path. &lt;em&gt;Every&lt;/em&gt; plugin is choosing their own path. For every plugin, users have to learn new UI elements and experience a new UX. Instead of building upon a common UI library, where of course we might change some colors and use our own logos, but we’d all use the same buttons, input fields, etc.&lt;/p&gt;
&lt;h2&gt;Every OS needs a design system&lt;/h2&gt;
&lt;p&gt;I think that if WordPress wants to be the Operating System of the web, as &lt;a href=&quot;https://techcrunch.com/2019/09/19/automattic-ceo-matt-mullenweg-about-raising-300-million-and-the-open-web/&quot;&gt;has been claimed&lt;/a&gt; by its project leader Matt Mullenweg, it should take a leaf out of the playbook of desktop OS’s. Microsoft has its &lt;a href=&quot;https://www.microsoft.com/design/fluent/&quot;&gt;Fluent Design System&lt;/a&gt;, Apple has its &lt;a href=&quot;https://developer.apple.com/design/human-interface-guidelines/guidelines/overview/&quot;&gt;Human Interface Guidelines&lt;/a&gt; and tons of &lt;a href=&quot;https://developer.apple.com/design/&quot;&gt;design resources&lt;/a&gt;. Apple also hosts yearly, highly prestigious, &lt;a href=&quot;https://developer.apple.com/design/awards/&quot;&gt;Design Awards&lt;/a&gt; highlighting those who’ve innovated &lt;em&gt;on top of&lt;/em&gt; their design system.&lt;/p&gt;
&lt;h2&gt;WordPress needs a design system and it needs it fast&lt;/h2&gt;
&lt;p&gt;It’s not that I think the WordPress core designers aren’t aware of this, or maybe even agreeing with it. There are &lt;a href=&quot;https://github.com/WordPress/gutenberg/issues/27484&quot;&gt;issues&lt;/a&gt; that show that they want to move in the right direction. The last version of the &lt;a href=&quot;https://wordpress.org/plugins/design-experiments/&quot;&gt;design experiments plugin&lt;/a&gt; (which was a step in the right direction) was released more than 3 years ago. &lt;a href=&quot;https://github.com/WordPress/gutenberg/issues/33447&quot;&gt;This issue&lt;/a&gt;, which has many of the needed components etc in it, is from July 2021, well over 1.5 years ago, and only about half way done. I think we really ought to speed up this design / development. Might be that there are simply not enough people working on this, or we aren’t prioritizing it right as a project.&lt;/p&gt;
&lt;p&gt;The current state is simply bad: WordPress core basically has 3 designs now. The edit post page I’m typing this in looks &lt;em&gt;nothing&lt;/em&gt; like the Posts overview page, which looks nothing like the Site Health page. And then you go into plugins and each has their own UI there too. This makes WordPress as a whole harder to use. This is how we lose CMS market share to companies like &lt;a href=&quot;https://www.wixdesignsystem.com/&quot;&gt;Wix&lt;/a&gt; and &lt;a href=&quot;https://polaris.shopify.com/&quot;&gt;Shopify&lt;/a&gt; (who each &lt;em&gt;do&lt;/em&gt; have their own design system).&lt;/p&gt;
&lt;p&gt;Now, of course, we’ve &lt;a href=&quot;https://ui-library.yoast.com/&quot;&gt;open sourced our own UI library&lt;/a&gt; at Yoast. Of course we’ve shared it with our colleagues at &lt;a href=&quot;https://newfold.com&quot;&gt;Newfold Digital&lt;/a&gt; and I’ll certainly push for us to use it more across the wider company. Other plugin developers and theme developers shouldn’t need to do all that work again and can use it, and hopefully, contribute back.&lt;/p&gt;
&lt;p&gt;I wish this had not been needed, but here we are. Maybe this post can help rally a few more people to work on WordPress core design, Despite all that, I’m super proud of what the new settings UI in Yoast looks like, so go check that out!&lt;/p&gt;
</content:encoded><category>WordPress</category><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Social &amp; Schema images: naming considerations</title><link>https://joost.blog/social-schema-images-naming-considerations/</link><guid isPermaLink="true">https://joost.blog/social-schema-images-naming-considerations/</guid><description>I’ve been playing a lot with Schema and Social images recently and one thing has become clear: we need better naming of these images and we should probably impr</description><pubDate>Wed, 06 Jul 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been playing a lot with Schema and Social images recently and one thing has become clear: we need better naming of these images and we should probably improve the Schema.org image standards a bit. In this post I want to briefly discuss the different needs and my proposed (very simple) naming scheme.&lt;/p&gt;
&lt;p&gt;As I was looking at &lt;a href=&quot;/use-avif-webp-share-images/&quot;&gt;how different social platforms supported WebP&lt;/a&gt;, I quickly realized that while they’re all using images, they’re using them in very different ways. Shocking, I know.&lt;/p&gt;
&lt;p&gt;The problem is that in Schema, we just have one &lt;code&gt;image&lt;/code&gt; attribute on articles. Well, technically, we also have &lt;code&gt;thumbnailUrl&lt;/code&gt;, but that’s not even close to descriptive enough.&lt;/p&gt;
&lt;p&gt;Let’s go through the two main types of images:&lt;/p&gt;
&lt;h2&gt;Poster images&lt;/h2&gt;
&lt;p&gt;An “OpenGraph image” these days is often the main image of the post with text on it. The title of the post, maybe an author, a site’s name and/or logo. It’s similar to a poster you would create for a movie or a performance in say a theater. So, why not call it that: a “poster image” or short “poster”. There’s precedent of that in HTML actually, where the &lt;code&gt;video&lt;/code&gt; HTML tag has a &lt;code&gt;poster&lt;/code&gt; attribute that should be shown until the video has loaded.&lt;/p&gt;
&lt;p&gt;Platforms like Facebook, Twitter and LinkedIn use these images when an article is shared on their timeline. Making these images be effective posters is &lt;em&gt;very&lt;/em&gt; important to your click-through rate from these platforms.&lt;/p&gt;
&lt;h2&gt;Featured images&lt;/h2&gt;
&lt;p&gt;The main image of an article is called the “featured image” in WordPress and many other systems. For some platforms, like Google Discover, this image is &lt;em&gt;much&lt;/em&gt; more suitable. Note that it doesn’t mean that these are all similar; this can be a good news image, a beautiful image of a travel destination or a great illustration. Important is that there’s no text on it, or at least, the text is not a main feature of the image.&lt;/p&gt;
&lt;p&gt;If a platform is going to put text &lt;em&gt;over&lt;/em&gt; the image or prominently display the title in its vicinity, it’s important that it grabs the featured image, &lt;em&gt;not&lt;/em&gt; the poster image.&lt;/p&gt;
&lt;p&gt;For example, compare this post’s featured image and post image:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/alice-donovan-rouse-yu68fUQDvOI-unsplash-1600x1068.jpg&quot; alt=&quot;Image showing torn promotional flyers&quot; /&gt;This post’s featured image&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/joost.blog-social-schema-images-1.jpg&quot; alt=&quot;Poster image for this post with joost.blog branding overlaid on the featured image&quot; /&gt;This posts’s poster image&lt;/p&gt;
&lt;h2&gt;So what’s the problem?&lt;/h2&gt;
&lt;p&gt;Facebook, LinkedIn and Twitter all read OpenGraph metadata. So we can feed them a poster image in the &lt;code&gt;meta og:image&lt;/code&gt; tag. Discover reads Schema, so we feed it an image in the &lt;code&gt;Article&lt;/code&gt; schema, and we just feed it the regular featured image (behavior we changed recently in Yoast SEO, coming to you soon).&lt;/p&gt;
&lt;p&gt;Pinterest historically read both. My thinking is that for Pinterest’s needs, it’d be much better to have the featured image.&lt;/p&gt;
&lt;p&gt;But… I would like &lt;em&gt;all&lt;/em&gt; social networks to start reading Schema at some point. I think we all want that, because the amount of metadata in a page right now just to say “hey use this image” is bordering on the ridiculous (and don’t even get me started about the platform’s ridiculous behavior around image sizes).&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;To be able to have both poster and featured images in Schema and let platforms pick the right one, we need more specificity in what type of image is what. My suggestion is to introduce an attribute &lt;code&gt;poster-image&lt;/code&gt; and an attribute &lt;code&gt;featured-image&lt;/code&gt; on the &lt;code&gt;Article&lt;/code&gt; Schema, that would both take an &lt;code&gt;ImageObject&lt;/code&gt;. This would resolve the ambiguity and make it possible for every platform to fully rely on Schema. In an even more perfect world, we could even add an array of &lt;code&gt;ImageObject&lt;/code&gt;s in both, with different sizes for the different platforms.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Optimize crawling: let’s turn things around!</title><link>https://joost.blog/optimize-crawling-lets-turn-things-around/</link><guid isPermaLink="true">https://joost.blog/optimize-crawling-lets-turn-things-around/</guid><description>I wrote about crawl optimization last week, mostly about getting stuff that you don’t want crawled to not be crawled. There’s more to say about that, and I will</description><pubDate>Thu, 02 Jun 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I wrote about &lt;a href=&quot;/optimize-crawling-for-the-environment/&quot;&gt;crawl optimization&lt;/a&gt; last week, mostly about getting stuff that you don’t want crawled to not be crawled. There’s more to say about that, and I will in follow up posts, but first we need to talk about how to get the stuff that you &lt;em&gt;do&lt;/em&gt; want indexed, crawled and indexed by search engines. To that end we’ll have to talk about XML sitemaps and the new kid in town, IndexNow.&lt;/p&gt;
&lt;p&gt;At the end of this post I’ll propose a &lt;em&gt;radical&lt;/em&gt; change to how we approach indexing. One that might not come into existence anytime soon, but that I’d love to discuss with everyone in the industry.&lt;/p&gt;
&lt;h2&gt;XML Sitemaps&lt;/h2&gt;
&lt;p&gt;XML sitemaps are meant to give search engines a list of all the URLs on a site. Ideally, they’d also include the images on those URLs and the last modification date. We’ve had &lt;a href=&quot;https://yoast.com/features/xml-sitemaps/&quot;&gt;XML sitemaps in Yoast SEO&lt;/a&gt; for years. In 2020, the Yoast team and Google team and some others collaborated on getting &lt;a href=&quot;https://make.wordpress.org/core/2020/07/22/new-xml-sitemaps-functionality-in-wordpress-5-5/&quot;&gt;XML sitemaps in WordPress core&lt;/a&gt;. The WordPress core sitemaps are &lt;em&gt;really&lt;/em&gt; just a list of URLs, the Yoast SEO ones are slightly more sophisticated.&lt;/p&gt;
&lt;p&gt;All search engines use XML sitemaps, and almost all of them use the last modified date as well as the image extensions. Notable &lt;strong&gt;non&lt;/strong&gt;-user of the last modified date? Google, as noted in &lt;a href=&quot;https://search-off-the-record.libsyn.com/lets-talk-sitemaps&quot;&gt;this recent podcast&lt;/a&gt;. This honestly actually surprised me as that seemed like a bit of information that would be fairly reliable in a lot of cases. But this might actually have to do with the definition of a “change”, something I’ll talk about below.&lt;/p&gt;
&lt;h2&gt;IndexNow&lt;/h2&gt;
&lt;p&gt;A recent addition to the website toolkit of ways to inform search engines is &lt;a href=&quot;https://www.indexnow.org/&quot;&gt;IndexNow&lt;/a&gt;. It’s a fairly simple protocol that allows you to ping search engines a URL that has changed on your site, or a list of URLs that has changed, and they say they’ll spider them quickly.&lt;/p&gt;
&lt;p&gt;This new standard is supported by Bing, Yandex and Seznam, so far, and I’ve heard rumors of other search engines joining their ranks. This might not be useful for everybody with this current set of search engines, but the idea in general isn’t a bad one, especially as they “echo” pings to each other. This means you only have to ping one endpoint and they’ll send them along to all. We’ll be adding support for it to Yoast SEO Premium soon and we look forward to seeing the impact it makes, if any.&lt;/p&gt;
&lt;h2&gt;What is a “change”?&lt;/h2&gt;
&lt;p&gt;The problem with notifications of changes is that the definition of a “change” is extremely cumbersome. If you change a single typo in a page, does that require a re-crawl? Probably not. Or was that typo in the title? That might change things.&lt;/p&gt;
&lt;p&gt;Whether something is a big or a small change is actually better judged by a human than by code, for now. That’s why I’d suggest actually making this a toggle in your CMS of choice, where a “small change” would not change the last modified date, and thus not cause a ping.&lt;/p&gt;
&lt;h2&gt;Could we turn this model around?&lt;/h2&gt;
&lt;p&gt;So I’ve been thinking about this topic for years. And something bothers me. Search engine crawlers are built on a very old concept: they follow links from one page to another, assuming that sites themselves don’t know which content lives on them. This simply isn’t true anymore for lots and lots of sites. A site purely built on WordPress, which SEO is managed by a tool like Yoast SEO, knows &lt;em&gt;exactly&lt;/em&gt; which content is on it and what should be crawled and indexed. Yet, on sites like yoast.com, search engines crawl about 10x more URLs than we &lt;em&gt;know&lt;/em&gt; are relevant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;What if search engines &lt;em&gt;only&lt;/em&gt; crawled URLs that we explicitly allowed?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So: what if we turned the model around? What if search engines &lt;em&gt;only&lt;/em&gt; crawled URLs that we explicitly allowed? What if we created a robots directive that played together with XML sitemaps. One where I could add the following lines to my &lt;code&gt;robots.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Disallow: /Allow-Sitemap: /sitemap_index.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this would allow crawlers to &lt;em&gt;only&lt;/em&gt; crawl URLs that are in those XML sitemaps. Sites should then explicitly set &lt;code&gt;Allow&lt;/code&gt; rules for their assets too, for instance &lt;code&gt;/wp-content/&lt;/code&gt; and the like. For most, if not all sites this would mean a drastic reduction in the amounts of URLs that are being crawled. It would also mean a &lt;em&gt;huge&lt;/em&gt; boost in efficiency in terms of caching and would thus be a great improvement for the environment, as sites would use less resources.&lt;/p&gt;
&lt;h2&gt;How to deal with canonicalization?&lt;/h2&gt;
&lt;p&gt;Now, I know you’ll say “but people link to URLs with parameters, we need to canonicalize those”. Yes, I know. Let’s say someone links to:&lt;/p&gt;
&lt;p&gt;https://example.com/example-post/?gclid=2&lt;/p&gt;
&lt;p&gt;And the XML sitemap only has:&lt;/p&gt;
&lt;p&gt;https://example.com/example-post/&lt;/p&gt;
&lt;p&gt;In this case, search engines / crawlers should simply canonicalize all URLs to the longest match they can find in the XML sitemap. Google, Facebook, Twitter and many others create random parameters they add to URLs &lt;em&gt;all the time&lt;/em&gt;, and in doing so, create “e-waste” of giant proportions. Site owners should not be burdened with this. Crawlers should simply stop crawling nonsense URLs.&lt;/p&gt;
&lt;h2&gt;What’s missing from this idea?&lt;/h2&gt;
&lt;p&gt;This is where I turn it to the search engines and the SEO community: what’s missing from this idea. What would make this &lt;em&gt;not&lt;/em&gt; work? What could we do differently if a site could be relied upon to know about its URLs, instead of search engines having to guess? I’d love to hear from you, on Twitter, or in the comments below.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Optimize crawling, for the environment</title><link>https://joost.blog/optimize-crawling-for-the-environment/</link><guid isPermaLink="true">https://joost.blog/optimize-crawling-for-the-environment/</guid><description>Search engines rely on spiders / bots to crawl the web and find (new) content. Every time they find a URL, they crawl it and if it’s interesting to them, they’l</description><pubDate>Tue, 24 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Search engines rely on spiders / bots to crawl the web and find (new) content. Every time they find a URL, they crawl it and if it’s interesting to them, they’ll &lt;em&gt;keep&lt;/em&gt; crawling it basically forever. The bigger your site, the more URLs you have, the more likely every individual URL is to be hit multiple times per day (or even per hour). Cloudflare’s &lt;a href=&quot;https://radar.cloudflare.com/&quot;&gt;Radar&lt;/a&gt; estimates that currently 32% of the traffic on the web is bots. That means that 32% of the energy used to serve websites is used by bots.&lt;/p&gt;
&lt;p&gt;Search engines are super eager to crawl. They will crawl literally &lt;em&gt;everything&lt;/em&gt; that looks like a URL to them. This means that every URL you create will be crawled, and therefore every URL you create has an impact. Let that sink in: every URL you add to a website’s source will be crawled. Let’s talk about what that means and how we can optimize crawling.&lt;/p&gt;
&lt;h2&gt;The cost of (too much) crawling&lt;/h2&gt;
&lt;p&gt;We rarely talk about the cost of crawling. It’s costing search engines money to crawl, of course, but it’s costing &lt;em&gt;you&lt;/em&gt; money too. If 32% of your traffic comes from bots, reducing that traffic might mean you pay less for your hosting. Regardless of whether there is a direct connection to your hosting bill, there is a connection to the electricity your site is using. To reduce crawling means your site uses less electricity which in the end is a win for the environment. Hence: optimize crawling, for the environment!&lt;/p&gt;
&lt;p&gt;Let’s have a look at what’s causing all this crawling to begin with. I’m going to assume you know how search engines work at a basic level, if you don’t, &lt;a href=&quot;https://www.youtube.com/watch?v=Lg8tmurU1_k&quot;&gt;this video&lt;/a&gt; will help you!&lt;/p&gt;
&lt;p&gt;To put it simply: search engines are crawling more than we want, because they’re spidering things that are not useful to users. How come they’re crawling more than we want? Because:&lt;/p&gt;
&lt;h2&gt;You have a &lt;em&gt;lot more&lt;/em&gt; than one URL per page&lt;/h2&gt;
&lt;p&gt;The problem I’m going to describe is by no means just a WordPress problem. Almost every CMS has this problem to some extent, but let me explain it to you by way of a WordPress example.&lt;/p&gt;
&lt;p&gt;When you publish a new post on a WordPress site, you get a URL. Let’s say we’ve just created this URL:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://example.com/example-post/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;What most people don’t realize is that WordPress automatically creates a lot of URLs “around” this post, and it links them all in the source. Let’s make a list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An RSS feed for the comments: &lt;code&gt;https://example.com/example-post/feed/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;An oEmbed URL so the page can be embedded: &lt;code&gt;https://example.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fexample.com%2Fexample-post%2F&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Another oEmbed URL in an XML format: &lt;code&gt;https://example.com/wp-json/oembed/1.0/embed?url=https%3A%2F%2Fexample.com%2Fexample-post%2F&amp;amp;#038;format=xml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;When you open these oEmbed URLs, you find that there’s an embed URL hidden in them: &lt;code&gt;https://example.com/example-post/embed/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A JSON version of the post in the WordPress REST API: &lt;code&gt;https://example.com/wp-json/wp/v2/posts/123&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;A shortlink of the same post: &lt;code&gt;https://example.com/?p=123&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;If you have date archives, you’ve now also created &lt;code&gt;https://example.com/2022/05/24/&lt;/code&gt; as a URL and &lt;code&gt;https://example.com/2022/05/&lt;/code&gt; etc. See this date archive on Matt’s blog for instance: &lt;a href=&quot;https://ma.tt/2022/03/23/&quot;&gt;https://ma.tt/2022/03/23/&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;If you have threaded comments (on by default in WordPress) and you don’t have Yoast SEO, you’ll have a link like this: &lt;code&gt;https://example.com/example-post/?replytocom=54321#respond&lt;/code&gt; for &lt;em&gt;every&lt;/em&gt; comment on your post.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you look at your server logs (unfortunately, very few people do that), you’ll see that &lt;em&gt;each and every one of those URLs gets crawled.&lt;/em&gt; None of these URLs add extra information that wasn’t already on the initial URL. None of these URLs should be of interest to search engines. But they don’t know that, so they’ll just crawl them anyway. This means that instead of crawling one single URL, your post, they’ve now got at least 7 versions of that URL to crawl &lt;em&gt;and they will&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Simply put, I think developers should consider this far more than they do:&lt;/p&gt;
&lt;p&gt;Every URL found in HTML &lt;strong&gt;will be crawled&lt;/strong&gt;.&lt;br /&gt;
Every URL you create and put in HTML therefore &lt;strong&gt;has a cost&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;You should ask yourself: is the benefit this URL brings, worth that &lt;em&gt;cost&lt;/em&gt;?&lt;/p&gt;
&lt;h2&gt;Other types of pages&lt;/h2&gt;
&lt;p&gt;This is not just true for posts and pages in WordPress of course. Tasks that seem very innocuous to the end user actually have far more impact than they realize. If you add a tag to a post, for example, and that tag has never been used before, you’ve now created a new URL on your site. In fact, because WordPress automatically makes RSS feeds for every tag, you’ve created two: &lt;code&gt;example.com/tag/new-tag/&lt;/code&gt; and &lt;code&gt;example.com/tag/new-tag/feed/&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The list goes on:&lt;/p&gt;
&lt;h2&gt;Plugins making it worse&lt;/h2&gt;
&lt;p&gt;Some plugins then add tons of extra URLs to create specific functionality. For example Jetpack’s “Sharedaddy” functionality adds one for every platform / share method you enable:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;https://example.com/example-post/?share=tumblr&amp;amp;nb=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/example-post/?share=twitter&amp;amp;nb=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/example-post/?share=facebook&amp;amp;nb=1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;https://example.com/example-post/?share=email&amp;amp;nb=1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Etc. etc.&lt;/p&gt;
&lt;p&gt;Let’s be clear: Jetpack is certainly not alone in doing this, in fact, they are nice enough to add &lt;code&gt;nofollow&lt;/code&gt; attributes to those links which should keep crawlers from following them (surprise: &lt;a href=&quot;https://developers.google.com/search/blog/2019/09/evolving-nofollow-new-ways-to-identify&quot;&gt;it doesn’t&lt;/a&gt;). Many plugins create almost infinite amounts of extra URLs (like dates in calendars) leading to infinite crawl “spaces” for search engines, which leads to very heavy crawling with absolutely no benefit.&lt;/p&gt;
&lt;h2&gt;Is this just Google?&lt;/h2&gt;
&lt;p&gt;By no means is this just a Google problem. Bing, Yandex, Neeva, Seznam, Baidu and many, many others crawl your site. Every day. They might not send you any traffic, but they &lt;em&gt;do&lt;/em&gt; crawl your site.&lt;/p&gt;
&lt;h2&gt;What should web developers do?&lt;/h2&gt;
&lt;p&gt;Web developers, including those working on WordPress core, really should stop adding URLs to the source that don’t absolutely need to be there. You should really consider whether links need to be there for discoverability. Or if the feature is really needed at all.&lt;/p&gt;
&lt;p&gt;In WordPress’ case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The REST API functions perfectly fine without links pointing to it from every page’s source.&lt;/li&gt;
&lt;li&gt;Adding shortlinks for every site that hardly anyone uses: not needed.&lt;/li&gt;
&lt;li&gt;Comment RSS feeds for every post? In my not so humble opinion, those should go the way of the dodo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Removing those links from every post would make a very real impact on crawling on the web, and therefore on power consumption by sites.&lt;/p&gt;
&lt;h2&gt;What can you do to optimize crawling?&lt;/h2&gt;
&lt;h3&gt;Everyone: block bots or slow them down&lt;/h3&gt;
&lt;p&gt;If you’re not getting any traffic from a search engine, but they are heavily crawling your site: block them or slow them down. Bing and Yandex both adhere to the &lt;a href=&quot;https://yoast.com/ultimate-guide-robots-txt/#crawl-delay-directive&quot;&gt;crawl-delay directive&lt;/a&gt;, so you might add something like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Crawl-delay: 30
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So they will only crawl your site once every 30 seconds or so. Play with the number there to get to something your comfortable with. With this setting they can still crawl 2,880 pages on your site &lt;em&gt;every&lt;/em&gt; day.&lt;/p&gt;
&lt;h3&gt;If you’re on WordPress&lt;/h3&gt;
&lt;p&gt;Yoast is adding features to Yoast SEO to remove the links to these URLs from the source. In some cases, we’ll allow disabling the functionality entirely. When those features come out, you should enable them (they won’t be on by default because we don’t want to inadvertently break anyone’s site).&lt;/p&gt;
&lt;p&gt;Sites that do not use the REST API to fill their pages (so, the vast majority of sites) should probably add this to their robots.txt file:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Disallow: /wp-json/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This blocks 4 of the 7 URLs I mentioned above and saves a ton of unnecessary crawling. Unfortunately, because some sites rely on using that endpoint for their content, we can’t default to this.&lt;/p&gt;
&lt;h3&gt;If you’re not on WordPress&lt;/h3&gt;
&lt;p&gt;Start looking at your site’s server logs. Find the URLs that are being crawled by search engines that are not useful to end users. Determine how they’re found and then start removing them. To analyze your server logs you can use a tool like &lt;a href=&quot;https://www.screamingfrog.co.uk/log-file-analyser/&quot;&gt;Screaming Frog’s Log file analyzer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you find any other patterns others should know about, share them in the comments, and let’s optimize crawling together!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Want to read more on this topic? I’ve written a follow up post: &lt;a href=&quot;/optimize-crawling-lets-turn-things-around/&quot;&gt;Optimize crawling, let’s turn things around!&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress’ market share is shrinking</title><link>https://joost.blog/wordpress-market-share-shrinking/</link><guid isPermaLink="true">https://joost.blog/wordpress-market-share-shrinking/</guid><description>There’s no more denying it: if you look at W3Techs CMS market share numbers, WordPress’ market share is shrinking, losing 0.4% market share since February. I do</description><pubDate>Wed, 11 May 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There’s no more denying it: if you look at &lt;a href=&quot;https://w3techs.com/technologies/history_overview/content_management/all&quot;&gt;W3Techs&lt;/a&gt; CMS market share numbers, WordPress’ market share is &lt;em&gt;shrinking&lt;/em&gt;, losing 0.4% market share since February. I don’t like to be or sound alarmist, so when I first noticed these numbers, I waited a bit to write about it. Shopify, the #2 CMS in my last &lt;a href=&quot;/cms-market-share/&quot;&gt;CMS market share&lt;/a&gt; report, is also shrinking a bit, though they’ve only lost a single percentage point so far.&lt;/p&gt;
&lt;p&gt;If WordPress is shrinking, something else &lt;em&gt;must&lt;/em&gt; be growing, this is, after all, a zero sum game. The very clear winners at the moment are Wix and Squarespace. Some other notable growers are Adobe, which is more on the enterprise side, and Webflow. Note that Elementor, which &lt;a href=&quot;/elementor-wordpress-secret-growth-driver/&quot;&gt;I called out&lt;/a&gt; recently as being a major growth driver for WordPress, still &lt;a href=&quot;https://w3techs.com/technologies/details/cm-elementor&quot;&gt;seems to be growing&lt;/a&gt;. So WordPress is shrinking &lt;em&gt;despite&lt;/em&gt; Elementor doing well.&lt;/p&gt;
&lt;p&gt;WooCommerce, on the other hand, seems to be &lt;a href=&quot;https://w3techs.com/technologies/details/cm-woocommerce&quot;&gt;losing&lt;/a&gt; some market share too.&lt;/p&gt;
&lt;h2&gt;WordPress market share over the last months&lt;/h2&gt;
&lt;p&gt;Let’s look at the raw numbers. Mind you, as long as I’ve been looking at this data, WordPress’ market share has &lt;em&gt;always&lt;/em&gt; gone up.&lt;/p&gt;
&lt;p&gt;MonthMarket shareJan 202243.2%Feb 202243.3%Mar 202243.3%Apr 202243.0%May 202243.0%11 May 202242.9%## Are we comparing apples to apples?&lt;/p&gt;
&lt;p&gt;We know that Alexa recently shut down, and W3Techs has always used Alexa to base their top 10 million sites on. I checked with W3Techs &lt;a href=&quot;https://x.com/jdevalk/status/1521748709696061446&quot;&gt;last week&lt;/a&gt; and their response was clear: the API is still functional and they’re still using that, so the market share data hasn’t changed from that perspective.&lt;/p&gt;
&lt;h2&gt;Are these the right numbers to look at?&lt;/h2&gt;
&lt;p&gt;So, I don’t think this is the best representation of “the web” that can be made. I’m hoping to work with the people at the &lt;a href=&quot;https://httparchive.org/&quot;&gt;HTTP archive&lt;/a&gt; to make an auto updating version of my CMS market share numbers. I’ve &lt;a href=&quot;https://console.cloud.google.com/bigquery?sq=992808990454:93ea02e6e2e14f578e2bd24ad4d263d3&quot;&gt;run the numbers&lt;/a&gt; manually today on the HTTP archive dataset and in the last full HTTP archive crawl, which was for April, WordPress’ market share was also shown to be shrinking compared to March. So we’ve verified the shrinkage in two different sources. Both sources however are based on the most popular sites, so whether &lt;em&gt;new&lt;/em&gt; sites are being built on Wix, SquareSpace or WordPress? We simply don’t know.&lt;/p&gt;
&lt;h2&gt;What’s to blame for this?&lt;/h2&gt;
&lt;p&gt;It’s honestly impossible to look at these numbers and not think “what’s happening here, why is that?”. After looking at it for a while, I’m coming to this conclusion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you look at &lt;a href=&quot;http://cwvtech.report/&quot;&gt;cwvtech.report&lt;/a&gt; you’ll see that in the last year, sites on Wix and Squarespace on average have improved their site speed more than WordPress sites. WordPress has a performance team now, and it has made some progress. But the reality is that it hasn’t really made big strides yet, and in my opinion, really should. Project leadership still seems unwilling to focus on performance though, which has to do with the next point:&lt;/li&gt;
&lt;li&gt;WordPress’ full site editing project is not done yet. Anecdotally, more and more people are having a hard time deciding how to build their site on WordPress. Wix and Squarespace are simply way simpler tools to build a site. As they improve their SEO tooling, there’s less and less reason to switch over to WordPress.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;WordPress is being out-“innovated”&lt;/h2&gt;
&lt;p&gt;Conclusion: I think WordPress, for the first time in a decade, is being out-“innovated”. Now I say “innovated” because Squarespace and Wix are not really doing anything that new. They’re just implementing best practices for both site speed and SEO. They are, however, rolling that out for all their users. So all of their users get better and better page speed performance and improved SEO. As a result more and more of their sites are doing well. This means the set of sites that W3Techs is measuring, changes. And thus the top 10 million sites in the world will have more Squarespace and Wix sites. Those sites are &lt;em&gt;actually&lt;/em&gt; doing better than their WordPress competitors.&lt;/p&gt;
&lt;p&gt;This isn’t just on the WordPress core team. WordPress hosts all over the world should be working harder to make their clients’ sites perform better too. In fact, most of them should invest in WordPress more given how much money they make from it. If they don’t, they might see their free ride go up in smoke as the hosted services win.&lt;/p&gt;
&lt;p&gt;You see: it’s not that you can’t do this with WordPress. It’s very possible to make super fast sites on WordPress, that perform incredibly well SEO wise. The problem is that &lt;em&gt;in aggregate&lt;/em&gt;, WordPress sites aren’t doing as well. Too much of what a website needs to do to do well, is left to the people building and running sites. People who don’t know what they don’t know. WordPress, which has “decisions, not options” as one of its &lt;a href=&quot;https://wordpress.org/about/philosophy/&quot;&gt;philosophies&lt;/a&gt;, is not even close to opinionated enough when it comes to performance and SEO.&lt;/p&gt;
&lt;h2&gt;How to fix WordPress’ market share?&lt;/h2&gt;
&lt;p&gt;If WordPress wants to maintain its market share or better yet, grow it, it’ll have to get its act together. That means it should focus on the performance of these sites across the spectrums of site speed and SEO. The Full Site Editing project is simply taking far too long. That’s causing the rest of the platform to lag behind current web trends. Without a drastic change in this approach I think WordPress will continue to lose market share for the next few years.&lt;/p&gt;
&lt;p&gt;PS&lt;/p&gt;
&lt;p&gt;This is not what I’d like to be seeing, of course. If you have data that proves me wrong I’d honestly love to see it.&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Transitioning to a new role at Yoast</title><link>https://joost.blog/new-role-at-yoast/</link><guid isPermaLink="true">https://joost.blog/new-role-at-yoast/</guid><description>At the end of this month, I will transition to a different role at Yoast. No, I will not leave Yoast. But, I am going to pursue some other dreams outside of Yoa</description><pubDate>Fri, 18 Mar 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At the end of this month, I will &lt;a href=&quot;https://yoast.com/joost-transitions-to-a-new-position/&quot;&gt;transition to a different role at Yoast&lt;/a&gt;. No, I will not leave Yoast. But, I &lt;em&gt;am&lt;/em&gt; going to pursue some other dreams outside of Yoast. It’s time to spread my wings! Let me explain a bit on why I am making this transition and what it will look like!&lt;/p&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;For somewhat longer than a decade, &lt;a href=&quot;https://yoast.com/&quot;&gt;Yoast&lt;/a&gt; has been my entire (work) life. And I still love the company. I still love our product. I still feel closely connected with Yoast SEO. However, I have always had ambitions outside of Yoast. I just was not able to pursue those, because I owned Yoast and was responsible for so many users and employees.&lt;/p&gt;
&lt;p&gt;However, I would love to learn new skills or improve other products. I would love to learn from other people and to help other companies. My ambitions outside of Yoast were my main personal reason for &lt;a href=&quot;/yoast-joins-newfold/&quot;&gt;selling Yoast&lt;/a&gt;. I really was getting bored. After our sale to Newfold, Yoast is running rather smoothly without me interfering on a daily basis. And that means it’s now time to spread my wings.&lt;/p&gt;
&lt;h2&gt;What about Yoast?&lt;/h2&gt;
&lt;p&gt;I was the final decision maker and primary driver for the product strategy of Yoast SEO for a long time. I’m super proud of going from 0 to currently 12.5 million users. Over the last few years, we’ve built a team of people that have a very good feel for what that product should be. The day to day SEO decisions I leave in the capable hands of Jono Alderson, who’ll &lt;a href=&quot;https://www.jonoalderson.com/blog/great-power-great-responsibility/&quot;&gt;now be Yoast’s head of SEO&lt;/a&gt;. With &lt;a href=&quot;https://marieke.blog/&quot;&gt;Marieke&lt;/a&gt; leading strategy, Thijs leading the company, and so many other great colleagues still there, I’m certain they’ll do fine.&lt;/p&gt;
&lt;p&gt;For Yoast, this is going to be a good thing. Me leaving the day to day operation opens up a lot of opportunities for other people. There are so many talented people that work at Yoast, that’ll now have so many more chances to grow. And, I’ll still be there to share my knowledge and help make sure the product evolves in a good direction. I’ll also still be advising Newfold on WordPress &amp;amp; SEO and I’ll definitely be cheering for both Yoast and all of wider Newfold in the years to come.&lt;/p&gt;
&lt;h2&gt;What am I going to do next?&lt;/h2&gt;
&lt;p&gt;As said, I will stay on board for one day every two weeks at Yoast. I will advise on product and SEO strategy. I am really looking forward to spreading my ideas without having to execute all of them.&lt;/p&gt;
&lt;p&gt;Next to that, I’ll finally have room for other things. I’m still contemplating what things get me most excited. Marieke and I invested in a number of companies and I look forward to getting a bit more involved with them. But I am also sort of curious to see what comes up now, and what I come up with when I don’t have to do day-to-day stuff at Yoast. It is so liberating not knowing exactly what I will be doing next year!&lt;/p&gt;
</content:encoded><category>Personal stuff</category><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Elementor: WordPress’ secret growth driver?</title><link>https://joost.blog/elementor-wordpress-secret-growth-driver/</link><guid isPermaLink="true">https://joost.blog/elementor-wordpress-secret-growth-driver/</guid><description>Is Elementor the secret behind WordPress’ growth in the last years? We know from my CMS market share analysis that WordPress has been growing fast. Could it be</description><pubDate>Mon, 13 Dec 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Is Elementor the secret behind WordPress’ growth in the last years? We know from my CMS market share analysis that WordPress has been growing fast. Could it be that a lot of that growth is actually caused by Elementor’s popularity?&lt;/p&gt;
&lt;p&gt;After I published the sixth iteration of my &lt;a href=&quot;/cms-market-share/&quot;&gt;CMS market share analysis&lt;/a&gt; last week, some interesting questions popped up in a couple of places. One of them was a comparison to Elementor’s growth. Now I honestly wasn’t aware that W3Techs tracked Elementor, but &lt;a href=&quot;https://w3techs.com/technologies/details/cm-elementor&quot;&gt;it does right here&lt;/a&gt;, apparently since last February. This helped me do a bit of analysis.&lt;/p&gt;
&lt;h2&gt;What is Elementor?&lt;/h2&gt;
&lt;p&gt;Elementor is a page builder. They refer to themselves as a “website builder”, which in my opinion is a bit unfair. Elementor needs WordPress to be able to build websites. Without it, it’s a page builder. It makes building websites with WordPress easier and faster, and it makes it possible to do that without knowing how to code. The latter is probably one of the main reasons why it’s seeing tremendous growth.&lt;/p&gt;
&lt;p&gt;Elementor raised $15M in capital in February 2020 and it looks like it’s applying that capital to create enormous growth. They recently launched their hosted version, which makes it truly competitive with services like WordPress.com, Wix and Squarespace.&lt;/p&gt;
&lt;h2&gt;How is Elementor doing?&lt;/h2&gt;
&lt;p&gt;Let’s start with the basic raw numbers. Elementor’s market share growth looks like this for the last 11 months:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/Elementor-market-share-1.svg&quot; alt=&quot;Elementor market share growth within the top 10 million sites over the last 11 months&quot; /&gt;Elementor market share within the top 10 million sites of the world.As you can see, Elementor has almost doubled in 11 months time. They are showing &lt;em&gt;incredible&lt;/em&gt; growth. Note that this is market share of the top 10 million sites in the world, all the &lt;a href=&quot;/cms-market-share/#notes-about-these-numbers&quot;&gt;caveats&lt;/a&gt; that apply to my CMS market share post apply here too.&lt;/p&gt;
&lt;h2&gt;How about other page builders?&lt;/h2&gt;
&lt;p&gt;W3Techs tracks some other page builders too, all since last February. The most important to note here is WP Bakery, which currently is in use by &lt;a href=&quot;https://w3techs.com/technologies/details/cm-wpbakery&quot;&gt;6.6% of websites&lt;/a&gt; in their data. What I found interesting is that while WordPress has shown decent growth, WP Bakery basically has seen &lt;em&gt;no&lt;/em&gt; growth. There are some smaller players, one of whom has seen some growth, that’s Beaver Builder. According to &lt;a href=&quot;https://w3techs.com/technologies/details/cm-wpbeaverbuilder&quot;&gt;these stats&lt;/a&gt;, they now have 0.4% of the market.&lt;/p&gt;
&lt;h2&gt;Where’s WordPress’ growth without Elementor?&lt;/h2&gt;
&lt;p&gt;When you compare all these growth numbers, it immediately becomes apparent what’s happening: Elementor is taking a bigger and bigger part of the WordPress “pie”. In the graph below, I’ve shown the relative growth of the market share percentage of both WordPress &amp;amp; Elementor and compared it with the delta between them: WordPress without Elementor. When Elementor grows 0.1% and WordPress grows 0.1%, the delta is 0%. In a few recent months, WordPress without Elementor has actually &lt;em&gt;shrunken&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/WordPress-vs-Elementor-market-share-growth.svg&quot; alt=&quot;Chart comparing WordPress, Elementor, and WordPress-without-Elementor market share growth&quot; /&gt;Elementor sites cannot exist without WordPress, so they are tied to each other. But I think the conclusion is fair that of all those new sites being built with WordPress, a &lt;em&gt;very&lt;/em&gt; large portion of them is being built with Elementor.&lt;/p&gt;
&lt;p&gt;I’ll make sure to make Elementor a part of my CMS market share analysis going forward as it has proven itself to be an important factor.&lt;/p&gt;
</content:encoded><category>Market Share Analysis</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Yoast joins Newfold</title><link>https://joost.blog/yoast-joins-newfold/</link><guid isPermaLink="true">https://joost.blog/yoast-joins-newfold/</guid><description>Today we announced that Yoast will join Newfold Digital. Marieke and myself, as well as Omar, Chaya and Thijs have sold our shares. We will, however, all remain</description><pubDate>Thu, 12 Aug 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Since this acquisition, I&apos;ve written about how the &lt;a href=&quot;/wordpress-admin-ui-needs-to-be-better/&quot;&gt;WordPress admin UI needs to improve&lt;/a&gt; and eventually &lt;a href=&quot;/new-role-at-yoast/&quot;&gt;transitioned to a new role&lt;/a&gt; before leaving Yoast entirely.&lt;/p&gt;
&lt;p&gt;Today we announced that &lt;a href=&quot;https://yoast.com/exciting-news-yoast-joins-newfold-digital/&quot;&gt;Yoast will join Newfold Digital&lt;/a&gt;. Marieke and myself, as well as Omar, Chaya and Thijs have sold our shares. We will, however, all remain in our current roles, as will all of team Yoast.&lt;/p&gt;
&lt;h2&gt;Why this step?&lt;/h2&gt;
&lt;p&gt;Well, honestly, this was a combination of a couple of things. First of all, “it’s the season”. We’re in a time of mergers and acquisitions in the WordPress world, a time of consolidation in what is still one of the most rapidly growing parts of the web. Yoast had always been bootstrapped, it was only our own money in this business, and we’d grown to 140+ employees. This was and is something we’re super proud of, but it was also causing stress. From COVID to euro/dollar exchange rates to the always existing, nagging fear of “what if they just all stop buying tomorrow” and more. We felt now was a good time for us to get away from that stress and find a company that could provide the backing we needed.&lt;/p&gt;
&lt;p&gt;Another part is more personal: I was a bit bored. I wanted to go faster, do more, do new stuff, but couldn’t anymore. I was trapped in our success. So I look forward to not being responsible for finance and that part of running a business and leave that to the much more capable people that Newfold will bring in to help our finance team and hope to find new things on my path to do within the Newfold family of brands.&lt;/p&gt;
&lt;h2&gt;Why Newfold Digital?&lt;/h2&gt;
&lt;p&gt;When you’re talking to companies in the WordPress community, you quickly find out that they’re not all alike. The team at Newfold stood out. Sharon is a very inspiring leader and the rest of their team just oozes enthusiasm. All of our conversations with them left us with more energy than we started them with, something that I can honestly tell you has not been common in a lot of these conversations.&lt;/p&gt;
&lt;h2&gt;What will change now?&lt;/h2&gt;
&lt;p&gt;Not all that much for now, as we work on getting Yoast integrated into the wider Newfold family first. I look forward to collaborating with the other WordPress core teams (notably the Bluehost team) and see if together we can be even more successful in helping WordPress grow. And after that…. Well you’ll see soon enough!&lt;/p&gt;
&lt;h2&gt;But first: champagne! 🥂&lt;/h2&gt;
&lt;p&gt;In many ways this does feel like we’ve finished the game. And immediately we’re starting it again, now at the next level of difficulty, but with the new-found confidence of having partners that’ll be with us on the ride. But before we do that, I’m going to pop open a bottle of champagne 😃&lt;/p&gt;
</content:encoded><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Organize your screenshots on MacOS</title><link>https://joost.blog/organize-screenshots-macos/</link><guid isPermaLink="true">https://joost.blog/organize-screenshots-macos/</guid><description>MacOS has great built-in screenshot functionality. The problem is that, by default, it saves screenshots to your Desktop, which in my case turned my Desktop int</description><pubDate>Thu, 22 Jul 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;MacOS has great built-in screenshot functionality. The problem is that, by default, it saves screenshots to your Desktop, which in my case turned my Desktop into a mess. I fixed this a while ago and today realized that this wasn’t an obvious thing for everybody, so I quickly wrote this blog post.&lt;/p&gt;
&lt;p&gt;Time needed: 2 minutes&lt;/p&gt;
&lt;p&gt;Let me quickly teach you how to improve your screenshot workflow and keep all your screenshots organized a bit better:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Create a screenshots folder&lt;/strong&gt;For me, my Screenshots folder is a folder in my Documents folder, that syncs to my Google drive. I’ve also dragged it into my left-hand sidebar in the finder, for easy access.&lt;img src=&quot;./images/screenshots-folder.webp&quot; alt=&quot;Screenshots folder in Finder&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open the screenshots app&lt;/strong&gt;Either press ⌘-Shift-5 or open the Screenshots app from the Applications/Utilities folder.&lt;img src=&quot;./images/applications-utilities-screenshots-app.webp&quot; alt=&quot;Screenshots app shown in the Applications/Utilities folder&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Set the screenshots folder as your screenshot destination&lt;/strong&gt;You might have to select “Other location” and find it.&lt;img src=&quot;./images/CleanShot-2021-07-22-at-18.55.23.webp&quot; alt=&quot;Set the screenshot destination folder in MacOS&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Easy access: stack in your dock&lt;/strong&gt;If you want to easily access your screenshots, drag the folder to your dock. If you right click it, you can select “View content as” and set it to “Fan”, and set “Sort by” to “Date Modified”, and you get something like this:&lt;img src=&quot;./images/CleanShot-2021-07-22-at-19.04.59.webp&quot; alt=&quot;Screenshots folder fanned out from the macOS dock, sorted by date modified&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Automatic renaming&lt;/strong&gt;Recently I’ve started using &lt;a href=&quot;https://keepitshot.com/&quot;&gt;Keep it shot&lt;/a&gt; to automatically rename my screenshots.&lt;img src=&quot;./images/file-renaming-queue-png.webp&quot; alt=&quot;Keep It Shot file renaming queue&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bonus: better screenshots&lt;/strong&gt;My screenshots got even better and more useful when I started using &lt;a href=&quot;https://cleanshot.com/&quot;&gt;CleanShot X&lt;/a&gt; (not getting paid for this). It changed some of these things a tiny bit but the setup with my Screenshots folder and the fanning stack is still what I use every day.&lt;img src=&quot;./images/cleanshot-x-screenshot.webp&quot; alt=&quot;CleanShot X, a screenshot tool, in action&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Save your screenshots on Google drive&lt;/h2&gt;
&lt;p&gt;If you want even better organization for your screenshots, you should sync your screenshot folder to Google drive. It’ll OCR all the image automatically, allowing you to search for the text on those images in your Google drive. It makes finding screenshots after a few weeks an absolute breeze.&lt;/p&gt;
&lt;p&gt;Happy that your screenshots are now organized and want to see other MacOS productivity tips? Check out my &lt;a href=&quot;/category/productivity-hacks/&quot;&gt;productivity hacks category&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Productivity hacks</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Building a trusted web, step 1.</title><link>https://joost.blog/building-a-trusted-web-step-1/</link><guid isPermaLink="true">https://joost.blog/building-a-trusted-web-step-1/</guid><description>One of the biggest long-term trust problems of the web is reliably figuring out who published something first. Who was first has deep implications for ownership</description><pubDate>Wed, 28 Oct 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the biggest long-term trust problems of the web is reliably figuring out who published something &lt;em&gt;first&lt;/em&gt;. Who was first has deep implications for ownership, and for areas like citation and (Google) news rankings. Today we have submitted &lt;a href=&quot;https://github.com/schemaorg/schemaorg/issues/2756&quot;&gt;an issue to the Schema.org GitHub&lt;/a&gt; which proposes a solution to &lt;em&gt;fix&lt;/em&gt; this using timestamps on the blockchain. I view this as a first step in fixing some of the inherent trust issues on the web.&lt;/p&gt;
&lt;p&gt;Full disclosure: this is a problem I care about deeply, which is why Marieke and I invested in &lt;a href=&quot;https://wordproof.com/&quot;&gt;WordProof&lt;/a&gt;, a company that set out to fix this problem. We’re not just investing money, I’m actively advising the company and we’re actively collaborating on proposals like this one.&lt;/p&gt;
&lt;h2&gt;What is this timestamp proposal?&lt;/h2&gt;
&lt;p&gt;Let me take the content from the issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The point is to “timestamp” new or modified content as you publish it. You then add the information on that timestamp, including where the hash &lt;em&gt;lives&lt;/em&gt;, into the markup of a page while optionally also offering the resource used to generate the hash. This way, the hash becomes &lt;em&gt;verifiable&lt;/em&gt; and optionally will allow for going through previous editions of the content (also &lt;em&gt;verifiable&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;A hash in a blockchain transaction indisputably proves that content existed in a specific moment in time (“transparency”). Furthermore, to put a transaction in a blockchain, you use a private key to authenticate. With this private key you can prove that you were the one placing the timestamp (“accountability”).&lt;/p&gt;
&lt;p&gt;Similarly, a timestamp could be added to a &lt;em&gt;transaction&lt;/em&gt; in combination with a set of terms of service, to prove that a certain set of terms are valid for that transaction. There are numerous applications of this system; this proposal focuses on time-stamping content to explain the basic architecture and provide validating services with the data they need to be able to verify the records.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;&lt;strong&gt;What is a timestamp?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;If you don’t know what a timestamp is, or don’t understand any of the concepts this proposal talks about, this might move a bit fast for you.&lt;/p&gt;
&lt;p&gt;In that case, take some time to read about timestamps. Bas van der Lans, founder of WordProof, is much better at explaining it than I am, so see this video:&lt;/p&gt;
&lt;p&gt;For me personally, this is one of the first blockchain implementations that has me deeply excited for its possibilities. Fun fact is: turns out blockchain was &lt;em&gt;invented in 1991 for the specific purpose of timestamping&lt;/em&gt; (&lt;a href=&quot;https://sebastiaans.blog/blockchain-1991-timestamp/&quot;&gt;source&lt;/a&gt;). The problems timestamping solves are very real, day to day problems of website owners, merchants etc. We can fix those problems.&lt;/p&gt;
&lt;p&gt;I hope we’ll look back at this in a few years and see that this schema.org issue was historic. That this is where we started the evolution and started building a trusted web, on top of the open web. And we’re doing that with open source software, an open standard and an open agenda. Let’s build the &lt;em&gt;&lt;strong&gt;trusted web&lt;/strong&gt;&lt;/em&gt;, together!&lt;/p&gt;
</content:encoded><category>WordPress</category><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>How to get week numbers in your Mac menu bar</title><link>https://joost.blog/how-to-get-week-numbers-in-your-mac-menu-bar/</link><guid isPermaLink="true">https://joost.blog/how-to-get-week-numbers-in-your-mac-menu-bar/</guid><description>This post explains how to get the week number in your Mac’s menu bar. Simple enough, right? At some point, as you start planning ahead in basically anything, yo</description><pubDate>Fri, 24 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This post explains how to get the week number in your Mac’s menu bar. Simple enough, right? At some point, as you start planning ahead in basically anything, you’ll run into “week numbers”. Week numbers are weird in that they’re not in your head all the time. But you do need to look them up more than you want. I ran into this and decided: there must be a way to solve this. And there is.&lt;/p&gt;
&lt;p&gt;On my Mac the menu bar looked liked this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/menu-bar-mac-default-date.webp&quot; alt=&quot;Screenshot of a standard Mac menu bar.&quot; /&gt;And what I wanted was for the week number to be next to the date, in the top right. So first I looked in the Mac’s date settings (System Preferences -&amp;gt; Date &amp;amp; Time), but unfortunately, it doesn’t allow for this. So I started Googling. Quickly I found this tool called &lt;a href=&quot;https://www.mowglii.com/itsycal/&quot;&gt;Itsycal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So, I set out to change this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;First, we install Itsycal&lt;/strong&gt;Itsycal is a nice, free, calendar integration in your menu bar, for me it looks like this when I open it, showing a nice outline of my next 3 days:&lt;img src=&quot;./images/itsycal-screenshot.webp&quot; alt=&quot;Itsycal window showing an outline of the next three days&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;We configure Itsycal to show the week number&lt;/strong&gt;Copy the top settings, under “Menu Bar” from the screenshot below. The content of the field is &lt;code&gt;E d MMM H:mmm / w&lt;/code&gt;&lt;img src=&quot;./images/istycal-settings-screenshot.webp&quot; alt=&quot;Itsycal Menu Bar settings with the week number format configured&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Make sure Itsycal starts on system boot&lt;/strong&gt;To make sure all of this isn’t gone when you reboot your Mac, check the box on the General tab next to “Launch at Login”:&lt;img src=&quot;./images/itsycal-general-settings.webp&quot; alt=&quot;Screenshot of Itsycal&apos;s general settings&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The menu bar should update&lt;/strong&gt;And it should now look something like this, with the Week number nicely showing after the date and time, on the left:&lt;img src=&quot;./images/itsycal-menu-bar-screenshot.webp&quot; alt=&quot;Mac menu bar showing the date, time, and week number after Itsycal setup&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get rid of the default calendar&lt;/strong&gt;Open System Preferences and go to Date &amp;amp; Time, uncheck “Show date and time in menu bar”. This takes the default date &amp;amp; time &lt;em&gt;out&lt;/em&gt; of the menu bar.&lt;img src=&quot;./images/date-time-menu-bar-setting-preferences.webp&quot; alt=&quot;Date &amp;amp; Time settings in Mac System Preferences&quot; /&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Move the date &amp;amp; time to the right&lt;/strong&gt;You don’t have to do this if you’re fine with the date &amp;amp; time on the left of your icons, but personally I dislike that. Luckily it’s very simple to rearrange your menu bar. Just press &amp;amp; hold &lt;code&gt;cmd&lt;/code&gt; on your keyboard and then drag the date &amp;amp; time to the right:&lt;img src=&quot;./images/drag-date-time.gif&quot; alt=&quot;Animated gif showing how to drag a menu bar icon to the right&quot; /&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s it! Now you have the date in your menu bar, just the way you want it, &lt;em&gt;with&lt;/em&gt; the week number.&lt;/p&gt;
&lt;p&gt;Happy to have your week numbers showing now? Find other quick improvements like this: check out my &lt;a href=&quot;/category/productivity-hacks/&quot;&gt;productivity hacks&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Productivity hacks</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Automatically convert WebP files to PNG</title><link>https://joost.blog/automatically-convert-webp-files-to-png/</link><guid isPermaLink="true">https://joost.blog/automatically-convert-webp-files-to-png/</guid><description>I love CloudFlare. It makes websites lots faster. One of the things it does is change PNG files to WebP automatically when your browser (f.i. Google Chrome) sup</description><pubDate>Sat, 08 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I love &lt;a href=&quot;https://www.cloudflare.com/&quot;&gt;CloudFlare&lt;/a&gt;. It makes websites lots faster. One of the things it does is change PNG files to WebP automatically when your browser (f.i. Google Chrome) supports it. This is awesome, as they’re smaller and it thus loads faster.&lt;/p&gt;
&lt;p&gt;However, sometimes I need to download an image file and I end up with a &lt;code&gt;.webp&lt;/code&gt; file. My operating system can’t handle those. So I have two options: opening the image URL in Safari (which doesn’t support WebP yet) or to convert them to PNG.&lt;/p&gt;
&lt;p&gt;I had this happen to me enough that I decided to find a better solution. I run a utility on my Mac called &lt;a href=&quot;https://www.noodlesoft.com/&quot;&gt;Hazel, by Noodlesoft&lt;/a&gt;. This utility helps me keep my Downloads and Trash folders clean, among other things, with rules I can define myself. I decided to add a new rule.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;To convert &lt;code&gt;.webp&lt;/code&gt; files to &lt;code&gt;.webp&lt;/code&gt; files, you need &lt;a href=&quot;https://developers.google.com/speed/webp/download&quot;&gt;Google’s WebP libraries&lt;/a&gt;. On your Mac, the easiest way to do that is with &lt;a href=&quot;https://brew.sh/&quot;&gt;Brew&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;brew install webp
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Converting from WebP to PNG&lt;/h3&gt;
&lt;p&gt;The command to convert a WebP file to PNG would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/bin/dwebp -o target.webp input.webp
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Adding the rule to Hazel&lt;/h2&gt;
&lt;p&gt;Now it’s time to open Hazel and add a rule:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/hazel-rule-screenshot-1557x1200.webp&quot; alt=&quot;A screenshot of the Hazel configuration screen with the configured rule.&quot; /&gt;Our rule is going to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find files in the Downloads folder that end in &lt;code&gt;.webp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run a script to convert those &lt;code&gt;.webp&lt;/code&gt; files to PNG.&lt;/li&gt;
&lt;li&gt;Move the &lt;code&gt;.webp&lt;/code&gt; file to the trash.&lt;/li&gt;
&lt;li&gt;Display a notification to show that it’s done all this.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All this is pretty easy to configure in Hazel. What you’ll need is the following embedded script:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/hazel-script-screenshot-3-1557x1200.webp&quot; alt=&quot;A screenshot of the Hazel configuration screen with the script highlighted.&quot; /&gt;The script is &lt;code&gt;/bin/bash&lt;/code&gt; as we need to strip the &lt;code&gt;.webp&lt;/code&gt; extension with some bash magic, and replace it with &lt;code&gt;.webp&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/usr/local/bin/dwebp -o &quot;${1%%.*}.webp&quot; $1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After setting up all the rules as explained above and copy pasting the script, just save the rule. You can save any WebP file to the Downloads folder (or whichever folder you decide to run this rule for) and it should be automatically converted to PNG!&lt;/p&gt;
</content:encoded><category>Productivity hacks</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Google’s robots changes, the web &amp; the law</title><link>https://joost.blog/googles-robots-changes-the-web-the-law/</link><guid isPermaLink="true">https://joost.blog/googles-robots-changes-the-web-the-law/</guid><description>Google announced a few days ago that they would change the way rel=&quot;nofollow&quot; works for them. They would start treating nofollow as a “hint” instead of a “direc</description><pubDate>Thu, 12 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google &lt;a href=&quot;https://webmasters.googleblog.com/2019/09/evolving-nofollow-new-ways-to-identify.html&quot;&gt;announced a few days ago&lt;/a&gt; that they would change the way &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt; works &lt;em&gt;for them&lt;/em&gt;. They would start treating &lt;code&gt;nofollow&lt;/code&gt; as a “hint” instead of a “directive”. This means they go from “oh you don’t want us to go into that room? ok” to “oh you don’t want us to go into that room? We’ll see about that”. Basically, they went from friendly neighbor to annoying parent real quick.&lt;/p&gt;
&lt;p&gt;Now &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt; is something you’d attach to links on your site. So I could nofollow one link and not nofollow another. There is also a &lt;code&gt;meta robots&lt;/code&gt; directive, used like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta name=&quot;robots&quot; content=&quot;noindex,nofollow&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This would historically &lt;em&gt;direct&lt;/em&gt; Google to not show that page in its index, and not follow its links. Now Gary Ilyes tweeted this last night:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1. There&apos;s no meta robots ugc and sponsored, it won&apos;t do anything if you add that.&lt;br /&gt;
2. Meta robots nofollow is a hint now, like rel-nofollow.&lt;br /&gt;
3. I&apos;ll update the docs tonight to say this explicitly.&lt;/p&gt;
&lt;p&gt;— Gary 鯨理／경리 Illyes (so official, trust me) (@methode) &lt;a href=&quot;https://twitter.com/methode/status/1171951127916699648?ref_src=twsrc%5Etfw&quot;&gt;September 12, 2019&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Gary tweeting about rel nofollow and meta nofollow. #2 is important in this context: “meta robots nofollow is a hint now”When Gary says “meta robots nofollow is a hint now”, I become slightly nervous. Because if &lt;code&gt;nofollow&lt;/code&gt; in a meta robots element is a &lt;em&gt;hint&lt;/em&gt;, what is &lt;code&gt;noindex&lt;/code&gt;? Do they now want to treat that as a hint? or as a directive? Turns out, &lt;code&gt;noindex&lt;/code&gt; remains a directive:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web.archive.org/web/20190927174635/https://twitter.com/dannysullivan/status/1171769802354057218&quot;&gt;https://web.archive.org/web/20190927174635/https://twitter.com/dannysullivan/status/1171769802354057218&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But, even if Google now says “&lt;code&gt;noindex&lt;/code&gt; will remain a directive”, won’t that lead to years and years of discussion? In my experience, even many experienced SEOs don’t always understand the difference between directives and hints, and think they’ve excluded something when they haven’t. This change will only make this worse.&lt;/p&gt;
&lt;h2&gt;Google unilaterally makes changes&lt;/h2&gt;
&lt;p&gt;My biggest gripe with this is that Google is making these changes unilaterally. Bing, Yandex, Baidu: all support &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt; and other search engines probably do too. The same is true for &lt;code&gt;meta robots nofollow&lt;/code&gt;. I don’t think it’s a good idea when Google decides on its own that it changes the “laws” of the web.&lt;/p&gt;
&lt;p&gt;Google were the ones to introduce &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt;, which gives them &lt;em&gt;some&lt;/em&gt; rights to change that “standard”. However, &lt;code&gt;meta robots nofollow&lt;/code&gt; has been &lt;a href=&quot;https://www.robotstxt.org/meta.html&quot;&gt;around since 1996&lt;/a&gt;. In fact, this part of the meta robots page made me chuckle:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;robots can ignore your &lt;code&gt;&amp;lt;META&amp;gt;&lt;/code&gt; tag. Especially malware robots that scan the web for security vulnerabilities, and email address harvesters used by spammers will pay no attention.&lt;/p&gt;
&lt;p&gt;Taken from the &lt;a href=&quot;https://www.robotstxt.org/meta.html&quot;&gt;Robots pages&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Apparently, I should consider Googlebot malware from now on 😉&lt;/p&gt;
&lt;h2&gt;Real world implications&lt;/h2&gt;
&lt;p&gt;Let’s look at real world implications: a link in a comment on a WordPress site used to have &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt; added to it automatically. We’ll now have to change &lt;code&gt;rel=&quot;nofollow&quot;&lt;/code&gt; to &lt;code&gt;rel=&quot;nofollow ugc&quot;&lt;/code&gt;. We can’t take out the &lt;code&gt;nofollow&lt;/code&gt;, because other search engines don’t support the &lt;code&gt;ugc&lt;/code&gt; part, but Google, with its market domination, will urge us to make that &lt;code&gt;ugc&lt;/code&gt; change.&lt;/p&gt;
&lt;p&gt;Now Google’s first reply to this will be “you don’t have to change anything if you don’t want to, we even said that in our post”. And they did:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There’s absolutely no need to change any nofollow links that you already have.&lt;/p&gt;
&lt;p&gt;Google’s Danny Sullivan in &lt;a href=&quot;https://webmasters.googleblog.com/2019/09/evolving-nofollow-new-ways-to-identify.html&quot;&gt;their announcement blog post on the nofollow changes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I read this and chuckled. Obviously Google needs to read up &lt;a href=&quot;https://en.wikipedia.org/wiki/Will_no_one_rid_me_of_this_turbulent_priest%3F&quot;&gt;on the murder of Thomas Becket&lt;/a&gt;. Because of Google’s market dominance, people will do anything to get into their favor. They &lt;em&gt;can&lt;/em&gt; make changes like this and the web &lt;em&gt;will&lt;/em&gt; follow. The real question here is: shouldn’t we have legislation that prevents them from making these changes unilaterally?&lt;/p&gt;
&lt;p&gt;In fact I’d say it’s time to go one step further: the web needs to have true standards for this. Standards that are preferably turned into law by the European Union, the US and China. But I dream too much perhaps. They at least need to be standards. Standards that all non-malware crawlers, &lt;em&gt;including Google&lt;/em&gt;, will adhere too.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Traveling to Peru</title><link>https://joost.blog/traveling-to-peru/</link><guid isPermaLink="true">https://joost.blog/traveling-to-peru/</guid><description>At the end of April, beginning of May, 2019, Marieke and I visited Peru with our kids. The main reason for going to Peru was to attend the wedding of two of our</description><pubDate>Mon, 17 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;At the end of April, beginning of May, 2019, Marieke and I visited Peru with our kids. The main reason for going to Peru was to attend the wedding of two of our friends Jessie &amp;amp; Jorge. These friends also happen to own a &lt;a href=&quot;https://www.machupicchutravel.nl/&quot;&gt;travel agency that specializes in travel to Peru&lt;/a&gt;, and helped us out a &lt;em&gt;lot&lt;/em&gt; during this trip which was nice.&lt;/p&gt;
&lt;p&gt;I wanted to share some highlights from this trip as I feel it’s a waste not to share these photos with more people.&lt;/p&gt;
&lt;h2&gt;Cuzco&lt;/h2&gt;
&lt;p&gt;The first city we visited in Peru was Cuzco, which, with its 3400 meters above sea level, was literally a breathtaking experience. The city is lovely, lots of tourists but the locals are very friendly and there are some very nice sights to see in and around Cuzco.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/20190422_160329.jpg&quot; alt=&quot;${1}&quot; /&gt;Cuzco’s main square, the Plaza de Armas&lt;/p&gt;
&lt;p&gt;During a tour around some sights in the surroundings of Cuzco, I snapped a few pics of the surroundings:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/20190423_090827.jpg&quot; alt=&quot;${1}&quot; /&gt;Tambomachay
&lt;img src=&quot;./images/20190423_093358.jpg&quot; alt=&quot;${1}&quot; /&gt;View from Puka Pukara
&lt;img src=&quot;./images/20190423_111608-1.jpg&quot; alt=&quot;${1}&quot; /&gt;Cuzco seen from Sacsayhuaman
&lt;img src=&quot;./images/20190423_110023.jpg&quot; alt=&quot;${1}&quot; /&gt;Sacsayhuaman&lt;/p&gt;
&lt;p&gt;After a few days in Cuzco we did what’s called a two day Inca trail towards Machu Picchu. In reality, this means you walk the Inca trails for one day and then have the next day to visit and admire Machu Picchu. This one day hike was about 12 kilometers and, according to my watch, about 240 full stairs. You go through some old Incan towns like Wiñay Wayna, which was incredibly beautiful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/20190425_113217.jpg&quot; alt=&quot;${1}&quot; /&gt;Walking the Inca trail
&lt;img src=&quot;./images/20190425_121525.jpg&quot; alt=&quot;${1}&quot; /&gt;Beautiful, beautiful trails
&lt;img src=&quot;./images/20190425_121717.jpg&quot; alt=&quot;${1}&quot; /&gt;Beautiful nature during the trails
&lt;img src=&quot;./images/20190425_124728.jpg&quot; alt=&quot;${1}&quot; /&gt;Wiñay Wayna
&lt;img src=&quot;./images/20190425_130233.jpg&quot; alt=&quot;${1}&quot; /&gt;Wiñay Wayna&lt;/p&gt;
&lt;p&gt;After that day of walking (it was heavy but doable) you end up on one of the most beautiful places I’ve ever visited in my life: Machu Picchu. Machu Picchu is of course arguably the most important touristic attraction of Peru.&lt;/p&gt;
&lt;h2&gt;Machu Picchu&lt;/h2&gt;
&lt;p&gt;This site is absolutely incredible. I don’t think you can truly capture in images what this place looks like, but I’ve tried to collect a few good ones:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/20190426_093952.jpg&quot; alt=&quot;${1}&quot; /&gt;Obligatory selfie
&lt;img src=&quot;./images/20190426_103825.jpg&quot; alt=&quot;${1}&quot; /&gt;Macchu Pichu from the side
&lt;img src=&quot;./images/20190426_102643.jpg&quot; alt=&quot;${1}&quot; /&gt;View from Macchu Pichu
&lt;img src=&quot;./images/20190426_090701.jpg&quot; alt=&quot;${1}&quot; /&gt;Macchu Pichu with Huayna Pichu in the bac&lt;/p&gt;
&lt;p&gt;While it’s hard to top Macchu Pichu, we did see a few more things in Peru. We toured around the &lt;a href=&quot;https://en.wikipedia.org/wiki/Sacred_Valley&quot;&gt;Sacred Valley&lt;/a&gt; for a day and saw some other truly marvelous sites:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/20190429_141001.jpg&quot; alt=&quot;${1}&quot; /&gt;I got out of the car during our trip to make this
&lt;img src=&quot;./images/20190429_143013.jpg&quot; alt=&quot;${1}&quot; /&gt;Mara Salt Mines
&lt;img src=&quot;./images/20190429_153522.jpg&quot; alt=&quot;${1}&quot; /&gt;Moray
&lt;img src=&quot;./images/20190429_101000.jpg&quot; alt=&quot;${1}&quot; /&gt;View from Pisac (and my lovely daughter)&lt;/p&gt;
&lt;p&gt;In all, Peru was absolutely fantastic. The wedding we attended was very lovely and in an absolutely fantastic spot as well, making a very nice end to our trip.&lt;/p&gt;
</content:encoded><category>Travel</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>(Why) I’m stepping down from my WordPress marketing role</title><link>https://joost.blog/why-im-stepping-down-from-my-wordpress-marketing-role/</link><guid isPermaLink="true">https://joost.blog/why-im-stepping-down-from-my-wordpress-marketing-role/</guid><description>I’m going to step away from my role as Marketing Lead. I consider this mostly a personal failure, both in correctly setting and getting expectations and in fitt</description><pubDate>Wed, 05 Jun 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m going to step away from my role as Marketing Lead. I consider this mostly a personal failure, both in correctly setting and getting expectations and in fitting into another type of organization. &lt;a href=&quot;https://ma.tt&quot;&gt;Matt&lt;/a&gt; and I have talked this through and there are no hard feelings on either side whatsoever. At the same time I’m sad about not having been able to leave more of a mark. Let me explain why I’m stepping down.&lt;/p&gt;
&lt;p&gt;When I first talked to Matt about this role he asked me to become “the CMO of WordPress”. In my eyes, a CMO is involved in &lt;em&gt;all&lt;/em&gt; aspects of a project / company. When I was announced, I was announced as a “&lt;a href=&quot;https://make.wordpress.org/updates/2019/01/16/expanding-wordpress-leadership/&quot;&gt;change in WordPress leadership&lt;/a&gt;”. My experience over the last few months made me feel that while I was doing things and &lt;a href=&quot;/marketing-wordpress-first-steps/&quot;&gt;getting things done&lt;/a&gt;, I certainly wasn’t leadership. Which is why I want to step away from my role: I don’t want to pretend I have a say in things I don’t have a say in.&lt;/p&gt;
&lt;h2&gt;What &lt;em&gt;is&lt;/em&gt; marketing?&lt;/h2&gt;
&lt;p&gt;It seems the problem of defining of what marketing is beforehand, is one of the problems of why I failed in my role. Marketing to me is not just the last step of “promotion”, but the entire process of bringing a product to market. It’s clear that others within the WordPress project don’t necessarily see it like that, and when they describe marketing, it’s a lot more like what I would call advertising. A lot more tactical. I don’t dislike that tactical work, but I think my qualities lie elsewhere.&lt;/p&gt;
&lt;p&gt;There’s a stark difference between where I thought I would be in the organization in this role, and where I am actually finding myself now. Even things that every outsider would consider marketing (release posts, about pages) are created without even so much as talking to me or others in the marketing team. Because I felt left out of all these decisions, I feel I can’t &lt;em&gt;be&lt;/em&gt; a marketing lead.&lt;/p&gt;
&lt;h2&gt;Clarity about my position&lt;/h2&gt;
&lt;p&gt;My position is unclear, not just to me, but to many people which makes me uncomfortable. I’ve been asked dozens of times on Twitter, Facebook and at WordCamps why I now work for Automattic, which of course I don’t but &lt;em&gt;that&lt;/em&gt; is the perception for a lot of people. On other occasions I seem to be the token non-Automattician, which I’m also uncomfortable with.&lt;/p&gt;
&lt;p&gt;At the same time I feel hampered by my WordPress position to do the work I need to do at Yoast. I notice I’m sometimes shutting up about things Yoast does because that would look weird on the outside and could be perceived wrong. I also felt I was “defending” WordPress too much, on stuff I had otherwise perhaps been more critical of.&lt;/p&gt;
&lt;h2&gt;WordPress mission and vision&lt;/h2&gt;
&lt;p&gt;I am used to having a strong vision and mission for a company and a product, and to be translating that into product &amp;amp; marketing decisions. Matt has certainly shown some of his product vision in State of the Words over the year but I’ve found it very hard to get more of the vision behind all the recent changes and the roadmap “out there”.&lt;/p&gt;
&lt;p&gt;I’ve not encountered (or been brought into) any discussions about our product vision, something I would need to translate into day-to-day actions. I was expecting there to be some backchannels where these discussions were had and these decisions were made, turns out these simply don’t exist. Matt takes his input from core devchats and lots of other chats and then decides what the roadmap should look like. I honestly think that process needs opening up, even though I do appreciate that Matt has so far been pretty good at bringing the product forward.&lt;/p&gt;
&lt;h2&gt;An inevitable conclusion&lt;/h2&gt;
&lt;p&gt;Combined, this doesn’t work for me. I was expecting to be actively involved in larger product and marketing decisions. That didn’t happen. At the same time I have to explain what we do to the outside world and to other people &lt;em&gt;within&lt;/em&gt; the WordPress ecosystem, because they assume I know and I’ve been involved. I’m unwilling and unable to do that.&lt;/p&gt;
&lt;p&gt;I think some of these things need to change. I see the value in what Josepha is doing and also in projects like the &lt;a href=&quot;https://wpgovernance.com/&quot;&gt;governance project&lt;/a&gt;, but these processes take time and patience, and patience is a virtue I’ve not developed well. This was my way of trying to broaden WordPress leadership. I’m sad to conclude that I failed. I’m of course still available to advise and strategize should the project want for that, and will give my opinion, whether I’m asked to or not 😉&lt;/p&gt;
&lt;p&gt;Turns out failing burns me out faster than going fast does… That’s why I’m taking an extended holiday this summer, after which I’ll focus my work time for 100% on Yoast and my Chief Product Officer role there. In that role, and outside of it, I’ll absolutely still be an active voice and contributor in the WordPress community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update December 2024&lt;/strong&gt;: A lot has happened in the WordPress community and certainly also the marketing team over the years, prompting me to &lt;a href=&quot;/people-behind-the-platform/&quot;&gt;write about it once more&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Photo by &lt;a href=&quot;https://unsplash.com/photos/green-forest-during-daytime-Dag9cv89jb4&quot;&gt;Ugne Vasyliute&lt;/a&gt; on &lt;a href=&quot;https://unsplash.com/search/photos/path?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Marketing WordPress – first steps</title><link>https://joost.blog/marketing-wordpress-first-steps/</link><guid isPermaLink="true">https://joost.blog/marketing-wordpress-first-steps/</guid><description>I’ve had the question a few times now of what I’ve been up to since I got appointed lead marketing for WordPress. The first few weeks have been busy, but very i</description><pubDate>Fri, 22 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve had the question a few times now of what I’ve been up to since I got appointed lead marketing for WordPress. The first few weeks have been busy, but very interesting. The marketing team is doing a &lt;em&gt;lot&lt;/em&gt; of things. I won’t go over all of them here, but wanted to show some of the projects we’ve worked on and how the processes around them work. As you’ll see, most of my focus so far has been on WordPress.org itself and team processes.&lt;/p&gt;
&lt;h2&gt;Quick wins&lt;/h2&gt;
&lt;p&gt;During the first weeks one of the things I started working on were some quick wins: making some of our about pages more reflective of the current state of the project. We:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;updated the &lt;a href=&quot;https://wordpress.org/about/roadmap/&quot;&gt;roadmap page&lt;/a&gt; (actually a couple of times already, most recently after the 5.1 release);&lt;/li&gt;
&lt;li&gt;removed mentions of jQuery and other libraries from &lt;a href=&quot;https://wordpress.org/about/features/&quot;&gt;the features page&lt;/a&gt; and added a lot of links to the support pages to it;&lt;/li&gt;
&lt;li&gt;fixed the &lt;a href=&quot;https://wordpress.org/about/testimonials/&quot;&gt;testimonials page&lt;/a&gt; as it was broken;&lt;/li&gt;
&lt;li&gt;and finally: started working on a better history page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Showcase&lt;/h2&gt;
&lt;p&gt;One of the things that immediately became clear as I was talking to the marketing team is that the showcase needs an update. The marketing team has been publishing great &lt;a href=&quot;https://make.wordpress.org/marketing/tag/case-studies/&quot;&gt;WordPress case studies&lt;/a&gt; on the make marketing blog, but those deserve a better place. At the same time the &lt;a href=&quot;https://wordpress.org/showcase/&quot;&gt;Showcase&lt;/a&gt;, where this content would make a lot of sense, obviously needs more love.&lt;/p&gt;
&lt;p&gt;When I proposed changing the showcase I got an immediate enthusiastic response from quite a few people, which was very warming. &lt;a href=&quot;https://pragmatic.agency/&quot;&gt;Pragmatic&lt;/a&gt; offered to help with design time and has been hard at work based on my (admittedly very simple) &lt;a href=&quot;https://make.wordpress.org/marketing/2019/01/23/showcase-redesign/&quot;&gt;outline&lt;/a&gt;, leading to some very cool first designs. In the last meeting of the &lt;a href=&quot;https://make.wordpress.org/design/&quot;&gt;WordPress design team&lt;/a&gt; we’ve been discussing their first designs and based on the feedback that came from that, we’ll iterate on those designs a bit more.&lt;/p&gt;
&lt;p&gt;This process has already showed me what an enormous ecosystem we have. Let me show you: in this case the marketing team comes up with a plan. The design team, at our request, starts thinking about a design, someone steps up and starts designing, on which the entire design team gives feedback, along with marketing and the accessibility team. Once we’ve agreed on a design, the meta team will start implementing the changes, after which I’m guessing the accessibility and marketing team will once again review the result. At the same time other members of the marketing team are coming up with a process for what a showcase entry will now require and how to get all that data. WordPress really is a &lt;em&gt;huge&lt;/em&gt; organization.&lt;/p&gt;
&lt;h2&gt;Team processes&lt;/h2&gt;
&lt;p&gt;We’ve made some &lt;a href=&quot;https://make.wordpress.org/marketing/2019/01/23/team-process/&quot;&gt;small adjustments&lt;/a&gt; to the team’s processes and I’m certain more will follow. The team’s Trello board is very useful for smaller tasks, but for bigger tasks like the Showcase redesign, we really need to figure out how we can do that more efficiently together with other teams. This is something that I’m hoping (and honestly, expecting) &lt;a href=&quot;https://josepha.blog/&quot;&gt;Josepha Haden&lt;/a&gt;, the new executive director for WordPress, will help us improve on.&lt;/p&gt;
&lt;h2&gt;SEO&lt;/h2&gt;
&lt;p&gt;Last year, as one of the actions leading from the growth council I was part of, Jono Alderson and I did an SEO analysis of WordPress.org (Jono really did the bulk of the initial analysis). Over the last few months we’ve been creating meta tickets to fix those issues one by one. The meta team (the team that handles all things WordPress.org) has been hard at work fixing these, leading to &lt;a href=&quot;https://meta.trac.wordpress.org/query?status=closed&amp;amp;keywords=~seo&amp;amp;col=id&amp;amp;col=summary&amp;amp;col=status&amp;amp;col=owner&amp;amp;col=type&amp;amp;col=priority&amp;amp;col=milestone&amp;amp;order=priority&quot;&gt;55 closed tickets&lt;/a&gt; at the current count and &lt;a href=&quot;https://meta.trac.wordpress.org/query?status=accepted&amp;amp;status=assigned&amp;amp;status=new&amp;amp;status=reopened&amp;amp;status=reviewing&amp;amp;keywords=~seo&amp;amp;col=id&amp;amp;col=summary&amp;amp;col=status&amp;amp;col=owner&amp;amp;col=type&amp;amp;col=priority&amp;amp;col=milestone&amp;amp;order=priority&quot;&gt;19 open ones&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Google Search Console is showing us that errors are going down. It’s also showing a small uptick in traffic that could probably be attributed to these improvements. I expect this is a sign of things to come, as I truly think we can get more traffic to WordPress.org.&lt;/p&gt;
&lt;h2&gt;Analytics&lt;/h2&gt;
&lt;p&gt;As we’re working on SEO, Jono and I are also slowly improving the Google Analytics implementation. Using Google Tag Manager we’ve started improving our measurement, for instance by tagging more events. This means we can better track events like downloads, thread creation and replies on the forums and other events.&lt;/p&gt;
&lt;p&gt;We have already had the first few requests for analytics data from the Docs and Support teams, who want to use that data to improve &lt;em&gt;their&lt;/em&gt; pages. It’s been very nice to quickly be able to answer those as our data gets better.&lt;/p&gt;
&lt;h2&gt;Is that all?&lt;/h2&gt;
&lt;p&gt;No, not even close. There is some very exciting work being done by the team on social media campaigns for WordCamps, something I hope we can build and expand on in the future. Another project is the promotion of sustainability for WordCamps. yet another one is the ongoing promotion of WordPress.tv stuff (which probably also deserves a more prominent spot on WordPress.org). There’s also work being done on the &lt;a href=&quot;https://wordpress.org/mobile/&quot;&gt;mobile page&lt;/a&gt; on .org, we’ve started discussing what should happen to the &lt;a href=&quot;https://wordpress.org/gutenberg/&quot;&gt;Gutenberg page&lt;/a&gt;, and I’m probably still forgetting tons of things as there is &lt;em&gt;so much&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;What’s next?&lt;/h2&gt;
&lt;p&gt;A lot of the above is ongoing work that we’ll have to work on for at least a few more months. At the same time, as I now understand our challenges a bit better, I want to take a step back and produce a “meta view” of how I think we should be marketing WordPress. This marketing plan will then serve as a discussion starter for the marketing team, but I certainly hope also across a wider part of the community.&lt;/p&gt;
&lt;p&gt;It’s been very exciting to see what people in the marketing team are capable of. I hope to be able to shine more light on what they do and also to amplify the reach of what they do in the coming months. Combined with my own experience, I look forward to making a meaningful difference in WordPress’ growth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update January 2025&lt;/strong&gt;: my period as &lt;a href=&quot;/why-im-stepping-down-from-my-wordpress-marketing-role/&quot;&gt;marketing lead of WordPress ended rather quickly&lt;/a&gt;. I’ve since done many different things around WordPress, most recently, I’ve shared my thoughts around (the current) &lt;a href=&quot;/wordpress-leadership/&quot;&gt;WordPress leadership&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Stepping “down” at Yoast</title><link>https://joost.blog/stepping-down-at-yoast/</link><guid isPermaLink="true">https://joost.blog/stepping-down-at-yoast/</guid><description>Today we’ve announced that Marieke is the new CEO at Yoast, and I’m stepping down. With me recently stepping up as Marketing &amp; Communications Lead for WordPress</description><pubDate>Thu, 24 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today &lt;a href=&quot;https://yoast.com/new-ceo-marieke-van-de-rakt/&quot;&gt;we’ve announced&lt;/a&gt; that &lt;a href=&quot;https://marieke.blog/&quot;&gt;Marieke&lt;/a&gt; is the new CEO at Yoast, and I’m stepping down. With me recently &lt;a href=&quot;/leading-marketing-communication-for-wordpress/&quot;&gt;stepping &lt;/a&gt;&lt;em&gt;&lt;a href=&quot;/leading-marketing-communication-for-wordpress/&quot;&gt;up&lt;/a&gt;&lt;/em&gt; as Marketing &amp;amp; Communications Lead for WordPress, this made a lot of sense. In many ways, Marieke was already the acting CEO at Yoast. While this step makes a lot of sense &lt;em&gt;to us&lt;/em&gt;, it probably needs some explaining to the outside world.&lt;/p&gt;
&lt;p&gt;We do things differently than many other companies do. We run Yoast with the four of us: Marieke, Omar, Michiel and myself. Marieke is &lt;em&gt;always&lt;/em&gt; the one that forces us to move forward, to take decisions. Yoast Academy? Her work. Our HR strategy? Her work. Our project management team? Her work. Our research team? Led by her. And that’s not even close to a complete list. Of course there is stuff I do, Omar does and Michiel does, but Marieke is certainly the most prolific of the four of us.&lt;/p&gt;
&lt;p&gt;While people seem to think &lt;em&gt;I&lt;/em&gt; have been successful it really is &lt;em&gt;we&lt;/em&gt; that have been successful. So henceforth I’ll be focusing more on what I am good at, and become Chief Product Officer. I’ll happily say “I don’t know, you’re the boss” from time to time, and that’ll be it. Not all that much will change for me, other than that we’ve switched offices, because she deserves to have the biggest office. But perceptions &lt;em&gt;will&lt;/em&gt; change. And that’s good.&lt;/p&gt;
&lt;p&gt;You see, all too often people have assumed that Marieke is my secretary. Or that she has a job at Yoast solely because she’s my wife. While she &lt;em&gt;is&lt;/em&gt; my wife and I love her dearly, saying that does her great injustice. She’s one of the most capable persons I’ve &lt;em&gt;ever&lt;/em&gt; met and I’m happy to be working with her every single day. I’m sure you’ll enjoy seeing her thrive in her new role as much as I do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update January 2025:&lt;/strong&gt; We ended up &lt;a href=&quot;/yoast-joins-newfold/&quot;&gt;selling Yoast&lt;/a&gt; in August of 2021. I left relatively quickly to join Newfold Digital, the company that acquired Yoast, for another 9 months, after which I left the company to focus on &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Leading marketing &amp; communication for WordPress</title><link>https://joost.blog/leading-marketing-communication-for-wordpress/</link><guid isPermaLink="true">https://joost.blog/leading-marketing-communication-for-wordpress/</guid><description>Last week I was appointed Marketing &amp; Communications Lead of WordPress. I think WordPress is one of the most essential platforms on the web and I’m really proud</description><pubDate>Mon, 21 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Marieke and I later wrote about the &lt;a href=&quot;/people-behind-the-platform/&quot;&gt;people behind the platform&lt;/a&gt; and the role community marketing played in WordPress&apos;s growth.&lt;/p&gt;
&lt;p&gt;Last week &lt;a href=&quot;https://make.wordpress.org/updates/2019/01/16/expanding-wordpress-leadership/&quot;&gt;I was appointed&lt;/a&gt; Marketing &amp;amp; Communications Lead of WordPress. I think WordPress is one of the most essential platforms on the web and I’m &lt;em&gt;really&lt;/em&gt; proud to be able to do my part for it. I have been in the WordPress community for well over a decade now. In that time I’ve done a lot of different things but I think this will be my biggest challenge yet.&lt;/p&gt;
&lt;p&gt;WordPress currently &lt;a href=&quot;https://w3techs.com/&quot;&gt;powers 32.9% of websites&lt;/a&gt; on the web, which, especially in absolute number of sites, is an incomprehensible number. It is also a wonderful project with a great community around it. Many people outside of WordPress don’t know much about the community, something I think we should change. Very often, people don’t even know about the great steps WordPress is making in re-defining content editing. We have to get that knowledge out there. The next phases of Gutenberg will even redefine website building. I hope to be instrumental in spreading this message. Let’s show the 67,1% that’s not using WordPress that maybe they should be.&lt;/p&gt;
&lt;p&gt;With the announcement this week has already been tumultuous, but let me describe my next steps below. I’ve also got a couple of questions people have asked a lot, so I’m adding answers to those below.&lt;/p&gt;
&lt;h2&gt;Next steps&lt;/h2&gt;
&lt;p&gt;There a few things I’ve already done: I’ve spoken to a lot of members of the marketing team. I had already seen a lot of the things they’re busy with. As I was talking to them I was surprised to find some really high quality work that hardly anybody knew about, that just need a small push. I’m very happy to be able to provide that push. I’m dividing stuff I encounter into two different buckets right now: quick wins and longer term.&lt;/p&gt;
&lt;p&gt;I’ve started writing a marketing and communications strategy. This will be based on my own ideas plus all the input I’ve had from people so far. From that strategy we should be able to extract campaigns. Within those campaigns there will be individual tasks that people can pick up.&lt;/p&gt;
&lt;p&gt;At the same time I’m looking to update some of the core pages on WordPress.org. These pages describe who we are and what we do. For example: we’ve already updated the &lt;a href=&quot;https://wordpress.org/about/roadmap/&quot;&gt;Roadmap page&lt;/a&gt;, it now reflects our plans for 2019 much better. The &lt;a href=&quot;https://wordpress.org/about/testimonials/&quot;&gt;Testimonials page&lt;/a&gt; that was broken &lt;a href=&quot;https://meta.trac.wordpress.org/ticket/4086&quot;&gt;has been fixed&lt;/a&gt;, and the Marketing team has a new task to update the &lt;a href=&quot;https://wordpress.org/about/history/&quot;&gt;History page&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Frequently asked questions&lt;/h2&gt;
&lt;p&gt;Let me answer the questions I’ve had a couple of times so far:&lt;/p&gt;
&lt;h3&gt;What does this mean for your day to day?&lt;/h3&gt;
&lt;p&gt;Obviously this comes with a bit of work. Several people asked me how this combines with my role at &lt;a href=&quot;https://yoast.com/&quot;&gt;Yoast&lt;/a&gt; and at home. It’s clear: this is going to have to come out of “Yoast time”. I’m simply not willing to spend less time with my family. Now, I’ll admit: I do work a slight bit more than the average Yoast employee. But saying that, this is still going to impact what I do at Yoast. Most importantly it will probably impact my ability to code as much myself.&lt;/p&gt;
&lt;h3&gt;What did you think about the discussion around your appointment?&lt;/h3&gt;
&lt;p&gt;There was some discussion around my appointment, due to the fact that Bridget, the marketing team’s leading representative, had not been informed. This was, oh the irony, a communications issue between Matt, Josepha and myself. We’ve all apologized to Bridget, and I think we’re good now. Of course, I hope I’ll be able to prevent these kinds of communication issues in the future.&lt;/p&gt;
&lt;p&gt;Let’s be clear: I’m not replacing her. She was a rep for the marketing team (she has stepped down since) and had done a good job as such. I’ve been given a wider task. I want us to do &lt;em&gt;much&lt;/em&gt; more than just write content.&lt;/p&gt;
&lt;h3&gt;What if there’s a conflict of interest between Yoast and WordPress?&lt;/h3&gt;
&lt;p&gt;These do come up. And let’s be fair: I’m not exactly the first in WordPress’ leadership who might have a conflict of interest sometimes. Not so long ago I urged people to &lt;a href=&quot;https://yoast.com/should-you-update-to-wordpress-5-0/&quot;&gt;wait with upgrading to WordPress 5.0.&lt;/a&gt; (Btw: you’re &lt;a href=&quot;https://yoast.com/on-gutenberg-and-wordpress-5-0/&quot;&gt;fine to update now&lt;/a&gt;). While I hope that my deeper involvement in the project will allow me to avoid these situations, when they happen, they happen. I’ll deal with them as transparently as I can. It could mean that I, with my WordPress hat on, have a different opinion than Yoast, the company.&lt;/p&gt;
&lt;h3&gt;Are you being paid for this?&lt;/h3&gt;
&lt;p&gt;No. This is a volunteer position. I do benefit indirectly from WordPress’ growth, as that’ll probably mean Yoast will grow too, but I don’t think you can really call that payment. The fact that my position is unpaid does &lt;em&gt;not&lt;/em&gt; mean there won’t be any paid positions within my team. I think marketing for WordPress as a whole would definitely benefit from having a few people that are able to consistently spend more than a few hours a week on it.&lt;/p&gt;
&lt;h3&gt;Are you going to fix the ambiguity between WordPress.com and WordPress.org?&lt;/h3&gt;
&lt;p&gt;No. There are limits to what I’m allowed to change and this falls outside of those limits. Not that I’d even want to: I don’t feel like it’s that much of a problem, it’s definitely an ambiguity but both sides &lt;em&gt;benefit&lt;/em&gt; from it too.&lt;/p&gt;
&lt;p&gt;I do think that we’re not always doing a good job of making the distinction between the two. Just pointing it out to f.i. journalists when they make the mistake might clear some things up and make the distinction more widely understood.&lt;/p&gt;
&lt;h3&gt;Does this mean Automattic is acquiring you?&lt;/h3&gt;
&lt;p&gt;It’s surprising how many people have asked me this or similar questions in the past few days. The answer is simple: No. Automattic is not acquiring us. The reason for that is simple: we’re not for sale. We’re having fun, and we’re not looking to be acquired.&lt;/p&gt;
&lt;h2&gt;More questions? Just ask!&lt;/h2&gt;
&lt;p&gt;If you have more questions for me: go ahead and ask. In the comments below, on &lt;a href=&quot;https://twitter.com/jdevalk&quot;&gt;Twitter&lt;/a&gt;, or on &lt;a href=&quot;https://chat.wordpress.org/&quot;&gt;WordPress Slack&lt;/a&gt;, where I am @joostdevalk.&lt;/p&gt;
&lt;h2&gt;Update January 2025&lt;/h2&gt;
&lt;p&gt;My period as marketing and communications lead for WordPress &lt;a href=&quot;/why-im-stepping-down-from-my-wordpress-marketing-role/&quot;&gt;ended rather quickly&lt;/a&gt;. I’ve since done many things around the WordPress project, and invested in many companies in the WordPress space. Most recently I’ve shared my thoughts on (the current) &lt;a href=&quot;/wordpress-leadership/&quot;&gt;WordPress leadership&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>Personal stuff</category><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Karen Sparck Jones</title><link>https://joost.blog/karen-sparck-jones/</link><guid isPermaLink="true">https://joost.blog/karen-sparck-jones/</guid><description>&gt; “Computing is too important to be left to men.”</description><pubDate>Thu, 03 Jan 2019 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;“Computing is too important to be left to men.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I had never heard of Karen Sparck Jones before, I’m thankful I have now:&lt;/p&gt;
&lt;p&gt;https://www.nytimes.com/2019/01/02/obituaries/karen-sparck-jones-overlooked.html&lt;/p&gt;
&lt;p&gt;We have a team of linguists / developers at Yoast, looks like we owe her a great deal.&lt;/p&gt;
</content:encoded><category>Short</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Democratizing publishing</title><link>https://joost.blog/democratizing-publishing/</link><guid isPermaLink="true">https://joost.blog/democratizing-publishing/</guid><description>Matt blogged, in response to a question asked at WordCamp US: what does “democratizing publishing” mean to you? His answer:</description><pubDate>Thu, 27 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Six years later, the question of what &quot;democratizing publishing&quot; means in practice became central to the &lt;a href=&quot;/transparency-contribution-and-the-future-of-wordpress/&quot;&gt;WordPress governance discussion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Matt blogged, in response to a question asked at WordCamp US: what does “democratizing publishing” mean to you? His answer:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;… the mission of “Democratize Publishing” to me means that people of all backgrounds, interests, and abilities should be able to access Free-as-in-speech software that empowers them to express themselves on the open web and to own their content. …&lt;/p&gt;
&lt;p&gt;Matt Mullenweg in &lt;a href=&quot;https://ma.tt/2018/12/democratize-publishing-revisited/&quot;&gt;his blog post&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I agree with this explanation of the mission, it’s something we at &lt;a href=&quot;https://yoast.com&quot;&gt;Yoast&lt;/a&gt; deeply believe in. We also want to further it with Yoast SEO by making it possible for everyone to not just express themselves, but also to be found. If people want to express themselves, a large chunk of those people also want those expressions to be found by others. While search engines aim to get everything findable, getting ranked so other people see you takes a bit more work. Hence our own mission: SEO for everyone.&lt;/p&gt;
</content:encoded><category>WordPress</category><category>Yoast</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>After WCUS 2018: WordPress &amp; Gutenberg FAQ</title><link>https://joost.blog/the-after-wcus-wordpress-gutenberg-faq/</link><guid isPermaLink="true">https://joost.blog/the-after-wcus-wordpress-gutenberg-faq/</guid><description>I just came back from WordCamp US in Nashville, which was awesome. In this post I want to address some of the questions I’ve had a couple of times. So here we g</description><pubDate>Wed, 12 Dec 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I just came back from WordCamp US in Nashville, which was awesome. In this post I want to address some of the questions I’ve had a couple of times. So here we go:&lt;/p&gt;
&lt;p&gt;**What is it you don’t like about Gutenberg?**There is &lt;em&gt;nothing&lt;/em&gt; I don’t like about Gutenberg. Ok, that might be a bit of an exaggeration but in general, I seriously love it. Gutenberg makes WordPress ready for the web of today. That’s the reason our development team spent so much time to help make Gutenberg better.&lt;/p&gt;
&lt;p&gt;**How well does Yoast SEO do with Gutenberg?**It’s fine! In fact, &lt;a href=&quot;https://yoast.com/yoast-seo-in-wordpress-5-0/&quot;&gt;it’s better than ever&lt;/a&gt;. Gutenberg has some performance issues (in the admin) at the moment that are more obvious when you use Yoast SEO, but I expect the next few minor releases of WordPress 5.0 to fix those. If you’re suffering from them now, installing and activating the Gutenberg plugin and keeping that up to date will get you those changes earlier.&lt;/p&gt;
&lt;p&gt;**But I thought you were mad about the release?**I was annoyed with the communication failure around the release. Initially, WordPress 5.0 was slated for November. In the post outlining that, a specific choice had been made to skip December, should November not be feasible, and go to January. Then, when the November timeframe proved unfeasible, the team tracked back on that and Matt decided to release in December anyway, with 2 days notice, despite several people’s objections, including &lt;a href=&quot;/wordpress-5-0-needs-a-different-timeline/&quot;&gt;my own&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I still do not agree with the &lt;em&gt;timing&lt;/em&gt; of the release, but it’s done now. Matt has offered his apologies for the poor communication around that and with that, we’re now looking onward. I’m looking forward to seeing how we, as a WordPress community, can improve the consistency of our communication in the future.&lt;/p&gt;
&lt;p&gt;**So should I upgrade to WordPress 5.0 now?**Our suggestion is still to wait, as I said in &lt;a href=&quot;https://yoast.com/should-you-update-to-wordpress-5-0/&quot;&gt;my post on Yoast.com on WordPress 5.0&lt;/a&gt;. Yoast SEO is awesome with Gutenberg and I can’t wait for everyone to try it, but if you’re going to try it &lt;em&gt;now&lt;/em&gt;, you might think that it’s annoying and slow. If you use it with Gutenberg 4.7 (currently in RC phase, &lt;a href=&quot;https://github.com/WordPress/gutenberg/releases&quot;&gt;you can get it here&lt;/a&gt;), it’s already much much better. This means that if you update in January, you should certainly be OK.&lt;/p&gt;
&lt;p&gt;**Is your support team suffering under WordPress 5.0?**Honestly: no. It seems to be doing reasonably fine, with very limited support on our end. Only one plugins so far is left that is known to break Yoast SEO’s meta box: &lt;a href=&quot;https://www.gravityforms.com/gutenberg-add-on-v1-0-beta-1/&quot;&gt;Gravity Forms’ Gutenberg Add-On&lt;/a&gt; (which is still in beta). I’m assuming that will get fixed.&lt;/p&gt;
&lt;p&gt;Two plugins have already been updated:&lt;br /&gt;
– &lt;a href=&quot;https://wpml.org/&quot;&gt;WPML&lt;/a&gt; you should update to 4.1.2 or higher.&lt;br /&gt;
– &lt;a href=&quot;https://wordpress.org/plugins/pods/&quot;&gt;Pods&lt;/a&gt; has been fixed in 2.7.11, so update to that, or a newer version.&lt;/p&gt;
&lt;p&gt;In general, many plugins will probably need to update a few more times the next few days and weeks.&lt;/p&gt;
&lt;p&gt;**Should people stick with the classic editor?**No! You’re missing out on lots and lots of cool stuff, so sticking with the classic editor should not be a long term solution. It might be fine for a few months as plugin and theme developers work out their issues with Gutenberg, but I’d suggest switching it on as soon as you can.&lt;/p&gt;
&lt;p&gt;There are always going to be people who don’t like interface changes, and such is the case with this release too. The awesome teams in the &lt;a href=&quot;https://wordpress.org/support/forums/&quot;&gt;WordPress forums&lt;/a&gt; are helping those people where they can and I assume a few of them will continue using the &lt;a href=&quot;https://wordpress.org/plugins/classic-editor/&quot;&gt;classic editor&lt;/a&gt; for now.&lt;/p&gt;
&lt;p&gt;**What are you looking forward to in WordPress?**In Matt Mullenweg’s &lt;a href=&quot;https://ma.tt/2018/12/state-of-the-word-2018/&quot;&gt;State of the Word&lt;/a&gt; he announced a longer term roadmap than we’ve had for a while, something I really like. He announced the next phase of Gutenberg, which looks promising, and &lt;a href=&quot;https://make.wordpress.org/core/2018/12/08/9-priorities-for-2019/&quot;&gt;9 focus points for the next year&lt;/a&gt;, which all seem to be very nice projects. On top of that he announced the intent for WordPress core to support multi-lingual sites, something I’ve been asking for as a core feature for quite some time now.&lt;/p&gt;
&lt;p&gt;**Was this post just an excuse to use the Yoast SEO FAQ block?**No, not really… Well, maybe a little 😉&lt;/p&gt;
&lt;p&gt;My experience: I had to switch to using the Gutenberg 4.7 RC as I was doing this. Otherwise it does indeed become slow when you have more than ~ 5 questions in an FAQ block. As soon I switched to the 4.7 RC though, it immediately became useful and in fact, very nice to work with.&lt;/p&gt;
&lt;p&gt;At the same time, I’ve found some minor issues that we’re going to have to solve in our FAQ blocks, so those will get an update too.&lt;/p&gt;
&lt;p&gt;Have more questions for me? Drop them in the comments, I’ll answer them there!&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>WordPress 5.0 needs a different timeline</title><link>https://joost.blog/wordpress-5-0-needs-a-different-timeline/</link><guid isPermaLink="true">https://joost.blog/wordpress-5-0-needs-a-different-timeline/</guid><description>For the last few months, the WordPress developer community has been moving towards a release of WordPress 5.0. This is the highly anticipated release that will</description><pubDate>Tue, 06 Nov 2018 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;For the last few months, the WordPress developer community has been moving towards a release of WordPress 5.0. This is the highly anticipated release that will contain the new Gutenberg editing experience. It’s arguably one of the biggest leaps forward in WordPress’ editing experience &lt;em&gt;and&lt;/em&gt; its developer experience in this decade. It’s also not done yet, and if we keep striving for its planned November 19th release date, we are setting ourselves up for failure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update November 9th: WordPress 5.0 has been moved&lt;/strong&gt;&lt;br /&gt;
The new release date is November 27th, 2018. See the &lt;a href=&quot;https://make.wordpress.org/core/2018/11/09/update-on-5-0-release-schedule/&quot;&gt;Make/Core post&lt;/a&gt; for details. While I’m happy that it’s been postponed, I’m not sure whether this is enough to get WordPress 5.0 to be as accessible and stable as I think it should be. Time will tell, I guess.&lt;/p&gt;
&lt;p&gt;Let me begin by stating that I love Gutenberg. It’s the best thing since sliced bread as far as content editing is concerned. I’m writing this post in Gutenberg. I started writing it on my iPhone. It rocks. But it also still has numerous bugs. In fact, the editor broke on me during writing this post and failed to autosave all the contents. Luckily I saw it breaking and copied the paragraphs to an external editor.&lt;/p&gt;
&lt;h2&gt;Reasons for delaying&lt;/h2&gt;
&lt;p&gt;There are a two main reasons why the November 19th timeline is in my opinion untenable:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are some &lt;a href=&quot;https://make.wordpress.org/accessibility/2018/10/29/report-on-the-accessibility-status-of-gutenberg/&quot;&gt;severe accessibility concerns&lt;/a&gt;. While these aren’t new and a few people are working hard on them, I actually think we can get a better handle on fixing them if we push the release back. Right now it looks to me as though keyboard accessibility has &lt;em&gt;regressed&lt;/em&gt; in the last few releases of Gutenberg.&lt;/li&gt;
&lt;li&gt;The most important reason: the overall stability of the project isn’t where it needs to be yet. There are so many open issues for the 5.0 milestone that even fixing all the blockers before we’d get to Release Candidate stage next week is going to prove impossible. We have, at time of writing &lt;a href=&quot;https://github.com/wordpress/gutenberg/issues?utf8=%E2%9C%93&amp;amp;q=is%3Aissue+is%3Aopen+no%3Amilestone+label%3A%22%5BType%5D+Bug%22+-label%3A%22future%22+&quot;&gt;212 untriaged bugs&lt;/a&gt; and &lt;a href=&quot;https://github.com/wordpress/gutenberg/issues?q=is%3Aopen+is%3Aissue+milestone%3A%22WordPress+5.0%22&quot;&gt;165 issues on the WordPress 5.0 milestone&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;People are working &lt;em&gt;hard&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;The amount of work being done every day right now by the development team is bordering on the insane. Look at the work for the last three days:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/image.webp&quot; alt=&quot;GitHub commit activity on the WordPress 5.0 Gutenberg repository over three days&quot; /&gt;I’d normally be happy with this for a week. This is 3 days, also including a Sunday. It’s been like this for a while. I appreciate all these people doing the hard work, but moving this fast only increases the chance of regressions.&lt;/p&gt;
&lt;p&gt;When &lt;a href=&quot;https://wordpress.slack.com/archives/C02QB2JS7/p1541503290677300&quot;&gt;I mentioned earlier today&lt;/a&gt; in the WordPress Slack’s #core-editor channel that I think we should push back, the response was pretty positive:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/image-1.webp&quot; alt=&quot;Slack thread in the WordPress core-editor channel supporting pushing back on the 5.0 timeline&quot; /&gt;Let’s get this straight: &lt;em&gt;this is in the channel with a large part of the people working on this release&lt;/em&gt;. I’m not the first to say this. I hope this post will help the powers that be come to the same conclusion.&lt;/p&gt;
&lt;h2&gt;Conclusion: push back, and zoom out&lt;/h2&gt;
&lt;p&gt;All these things considering, my conclusion is simple: we need to push back the release. My preference would be to January. This would allow us to zoom out a bit, prevent regressions and overall, lead to a better product, with finished documentation. Something that’s worthy of the label RC when we decide to stick that on it. Right now, I feel that the beta is more of an alpha, and we’ll end up with an RC that’s more of a beta.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Poetry is not an expression of the party line. It’s that time of night, lying in bed, thinking what you really think, making the private world public, that’s what the poet does.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Allen Ginsberg, from &lt;a href=&quot;http://books.google.com/books?id=E4gXAQAAMAAJ&amp;amp;focus=searchwithinvolume&amp;amp;q=%E2%80%9CPoetry+is+not+an+expression+of+the+party+line.+It%27s+that+time+of+night%2C+lying+in+bed%2C+thinking+what+you+really+think%2C+making+the+private+world+public%2C+that%27s+what+the+poet+does.&quot;&gt;Ginsberg, a biography&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Gutenberg and Yoast SEO</title><link>https://joost.blog/gutenberg-and-yoast-seo/</link><guid isPermaLink="true">https://joost.blog/gutenberg-and-yoast-seo/</guid><description>I’m typing this as I sit in Track 1 of WordCamp Europe in Paris. Matt Mullenweg just announced Gutenberg as a plugin is available, so I installed it on here and</description><pubDate>Sat, 17 Jun 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’m typing this as I sit in Track 1 of WordCamp Europe in Paris. Matt Mullenweg just announced Gutenberg as a plugin is available, so I installed it on here and am writing this post in it.&lt;/p&gt;
&lt;p&gt;This was the beginning of an interesting number of posts, eventually asking for a different &lt;a href=&quot;/wordpress-5-0-needs-a-different-timeline/&quot;&gt;timeline for Gutenberg&lt;/a&gt;. In many ways, this was the beginning of a &lt;a href=&quot;/wordpress-leadership/&quot;&gt;lot of discussion about leadership&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Gutenberg is a radical rethinking of the post screen and that means we at Yoast have to rethink how we integrate with it. Some of what we do, our real time content and SEO analysis will still have to be visible all the time. Our snippet preview though, might actually move to a pre-publish workflow, a concept that the Gutenberg team has been thinking about.&lt;/p&gt;
&lt;p&gt;None of this is set in stone yet, I look forward to building an integration that works well. I would also love, if you’re reading this, your ideas on how we could integrate with this.&lt;/p&gt;
&lt;h2&gt;Accessibility and Gutenberg&lt;/h2&gt;
&lt;p&gt;One of the things that we at Yoast worry about is the accessibility of Gutenberg. We have a team member, Andrea Fercia, who’s very active as a WordPress contributor. We have freed him up to work on accessibility on this project so it can be merged into WordPress soon without losing the accessibility that the current editor has.&lt;/p&gt;
&lt;h2&gt;Install Gutenberg&lt;/h2&gt;
&lt;p&gt;You should really install Gutenberg on a test site and play with it: &lt;a href=&quot;http://wordpress.org/plugins/gutenberg/&quot;&gt;Install Gutenberg&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>WordPress</category><author>joost@joost.blog (Joost de Valk)</author></item><item><title>Blogging about not SEO</title><link>https://joost.blog/blogging-about-not-seo/</link><guid isPermaLink="true">https://joost.blog/blogging-about-not-seo/</guid><description>I’ve been wanting to do this for a while: blogging for fun, on a domain separate from Yoast.com. Blogging about stuff I do outside of Yoast, which, surprisingly</description><pubDate>Sat, 14 Jan 2017 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I’ve been wanting to do this for a while: blogging for fun, on a domain separate from &lt;a href=&quot;https://yoast.com/&quot;&gt;Yoast.com&lt;/a&gt;. Blogging about stuff I do outside of Yoast, which, surprisingly to some people, is more and more. I will blog here about the investments Marieke and I make with Altha, our investment company and my advisory work, when I’m allowed to.&lt;/p&gt;
&lt;p&gt;I’ll also use this to vent my political opinions every so often. I like discussion, so, if you do too, I’ll see you in the comments 🙂&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update January 2025:&lt;/strong&gt; This post was written in 2017, 8 years later, this is still my blog. The only change: Altha was renamed to &lt;a href=&quot;https://emilia.capital/&quot;&gt;Emilia Capital&lt;/a&gt; a few years ago and became a bit more important when we &lt;a href=&quot;/yoast-joins-newfold/&quot;&gt;sold Yoast&lt;/a&gt;.&lt;/p&gt;
</content:encoded><category>SEO</category><author>joost@joost.blog (Joost de Valk)</author></item></channel></rss>