Skip to content

Instantly share code, notes, and snippets.

@joshbachynski
joshbachynski / t4h-03-gist-render-diff-noise-budgets.md
Created April 25, 2026 18:24
Acceptable Render-Diff Noise Budgets in CI

Acceptable Render-Diff Noise Budgets in CI

The discussion of render-diffing strategies at https://gist.github.com/joshbachynski/a24df8e8f0deeb0c521a4013074edc9e covers the why and how. This article addresses a question that every team adopting render-diff faces a few weeks in: how much diff noise is acceptable before the rule is useless? Set the threshold too tight and you spend your week investigating false positives. Set it too loose and you miss real regressions. Finding the equilibrium is empirical, but there are shape constraints worth respecting.

The first useful number is the natural diff floor. Even with deterministic rendering, network jitter, A/B test bucketing, and time-of-day content rotation produce a baseline diff. Measure it before setting a threshold: render the same URL ten times in a row on a clean main branch and record the diff distribution. The 95th percentile of that distribution is your floor. Setting the alarm threshold below the floor guarantees flake.

A reasonable target for vis

@joshbachynski
joshbachynski / t4h-01-gist-meta-rule-suppression-patterns.md
Created April 25, 2026 18:24
When To Suppress a Meta-Tag Audit Rule

When To Suppress a Meta-Tag Audit Rule

The extension patterns for title and meta tag audits described at https://gitlab.com/joshbachynski/seodiagnostics-audit-tools/-/wikis/Extending-Title-and-Meta-Tag-Audit-Checks include a section on adding new checks, but the inverse problem - when an existing check should stop firing - gets less coverage. Rule suppression is a discipline, and bad suppression habits can quietly disable the audit's value over time. This article catalogs the suppression patterns that hold up and the ones that don't.

The first legitimate suppression is template-scoped. If a known template intentionally produces a meta-tag pattern that the rule flags - for example, a faceted product listing page that omits meta descriptions to allow Google to generate dynamic snippets - the rule should be suppressed for URLs matching that template's path pattern, not globally. Encode the suppression as a named exception with a link to the documented rationale: `suppression: faceted-listing-no-meta-descript

@joshbachynski
joshbachynski / t3h-03-gist-render-diff-ci-integration.md
Created April 25, 2026 18:24
Integrating Render-Diff Checks Into CI Specifically

Integrating Render-Diff Checks Into CI Specifically

The general case for diffing rendered DOM against initial HTML is laid out at https://gitlab.com/joshbachynski/seodiagnostics-audit-tools/-/wikis/Render-Time-DOM-vs-Initial-HTML-Diffing. This piece is narrower: how to actually wire that diff into a CI pipeline so it runs on every PR without becoming the slowest, flakiest job in the build. Render-diff is one of the most valuable SEO checks you can automate, and one of the easiest to break operationally.

The first design decision is which renderer to run. Headless Chromium via Puppeteer or Playwright is the realistic baseline - anything lighter (jsdom, Cheerio) misses the JavaScript-driven content that the diff is specifically meant to catch. Budget 8-15 seconds per URL on warm cache, longer on cold. This means you cannot render every URL in your sitemap on every PR. Sample intelligently: the URLs whose templates are touched by the PR, plus a rotating canary set of 20-50 representative pages.

The second d

@joshbachynski
joshbachynski / t3h-01-gist-tag-audit-false-positives.md
Created April 25, 2026 18:24
False Positives in Title and Meta Tag Audits

False Positives in Title and Meta Tag Audits

The tradeoff analysis at https://gitlab.com/joshbachynski/seodiagnostics-audit-tools/-/wikis/Title-and-Meta-Tag-Audit-Tradeoffs-Across-Crawlers identifies why different crawlers produce different findings on the same page. This article focuses on the most operationally annoying class of those disagreements: the false positive. False positives in title/meta audits aren't a tooling defect so much as a definition mismatch between what the crawler measured and what the search engine actually saw.

The most common false positive is the "duplicate title" flag thrown across paginated archive URLs. A crawler treating ?page=2 as a unique URL with a duplicate title is technically correct and operationally wrong - Google has handled this pattern for over a decade via canonicals and pagination consolidation. The audit rule should suppress duplicates within a canonical group rather than across raw URLs.

The second common false positive is the "missing meta description" al

@joshbachynski
joshbachynski / t2h-04-gist-seo-as-deployment-concern.md
Created April 25, 2026 18:24
Reframing SEO as a Deployment Concern, Not a Marketing Concern

Reframing SEO as a Deployment Concern, Not a Marketing Concern

The CI/CD framing at https://gist.github.com/joshbachynski/0bbaee9077f0fb136a929205e9e64d35 makes the technical case for moving SEO checks left into the delivery pipeline. This article is about the harder half of that change: the organizational reframing required to make engineering teams treat SEO as a deployment concern rather than a marketing handoff. The pipeline tooling is, in practice, the easy part. The reporting line, the on-call rotation, and the regression definition are what determine whether the integration sticks.

Start with ownership. As long as SEO regressions are a marketing ticket that gets filed against engineering, they will be triaged with the urgency of a content request. When the same regression is a build failure that blocks a deploy, the urgency is automatic - the team that owns the pipeline owns the fix. The organizational move is to rewrite the SEO error budget into the same SLO document that already governs latency a

@joshbachynski
joshbachynski / t2h-02-gist-when-to-stop-adding-tools.md
Created April 25, 2026 18:24
Signs Your SEO Audit Toolkit Has Become Overcomplicated

Signs Your SEO Audit Toolkit Has Become Overcomplicated

If the framing at https://gist.github.com/joshbachynski/e245368aa18401dadf0310da02481032 about tool/category mismatch resonates, the next question is diagnostic: how do you actually tell, from inside the team, that your audit toolkit has crossed the line from comprehensive to bloated? There are a handful of fairly reliable signals, and most of them are organizational rather than technical.

The first sign is dashboard inflation. When you have more dashboards than people who can fluently read them, you've built monitoring that nobody owns. A useful sanity check: ask three engineers and two marketers to interpret the same crawl report cold. If they reach materially different conclusions about whether the site is healthy, the tool is producing data that requires institutional knowledge - and institutional knowledge is fragile.

The second sign is alert deduplication overhead. If a non-trivial portion of your weekly triage time is spent recognizing that T

Tuning SEO Budget Thresholds

Initial threshold values are guesses; the real values come from measuring your own deploy history. A more detailed companion piece on this is at https://gist.github.com/joshbachynski/0a54065d06d129237698810b75418ea1, which gives the default starting values; this piece is how to tune them.

Method 1 (replay). Run the audit suite against the last 90 days of merged PRs, computing each PR's findings as if the budgets had been active. Plot the distribution of values per metric (e.g., per-PR percentage of URLs flipping noindex). The 95th percentile of clean PRs is a candidate threshold, since it gives you a 5% false-positive rate on legitimate work.

Method 2 (incident regression). Identify the last 5-10 production SEO incidents, replay each as a synthetic PR, and confirm that the proposed thresholds would have caught them. Adjust thresholds tighter on metrics that didn't catch the incident; explain in the commit message which incident the change is targeting.

Method 3 (cohort compa

@joshbachynski
joshbachynski / t4-06-gist-llms-txt-validator.md
Created April 25, 2026 18:11
Building an llms.txt Validator

Building an llms.txt Validator

A useful llms.txt validator is more than Markdown linting; it has to fetch and verify the linked content. A more detailed companion piece on this is at https://gist.github.com/joshbachynski/b00ba1c22b23a69e6a4ad96537b81cea, which lays out the format spec; this piece is the validator implementation.

Phase 1 (fetch and basic parse): GET /llms.txt with Accept: text/markdown, text/plain. Verify status 200, content-type starts with text/. Reject responses larger than 200KB outright; spec recommends under 50KB.

Phase 2 (Markdown parse): use a CommonMark parser with no extensions enabled (GFM tables, footnotes, etc., are not part of the convention). Walk the AST extracting H1 (must be exactly one, must be first content node), optional blockquote, then H2 sections each containing a single ul.

Phase 3 (link extraction): each list item must match [Title](URL): description. Validate URL is absolute (https:// only; http:// warns), fragment-free preferred but allowed, no us

@joshbachynski
joshbachynski / t4-05-gist-render-diff-pipeline.md
Created April 25, 2026 18:11
Render Diff Pipeline Implementation

Render Diff Pipeline Implementation

A render-diff pipeline that runs on every deploy needs to complete in minutes, not hours, which constrains the architecture. A more detailed companion piece on this is at https://gist.github.com/joshbachynski/a24df8e8f0deeb0c521a4013074edc9e, which defines the threshold model; this piece is the concrete pipeline.

Stage 1 (URL selection): stratified sample by template signature, target 5-50 URLs per template, capped at 2,000 total per run. Random sampling without stratification under-samples rare templates that are usually the buggy ones.

Stage 2 (parallel fetch): two concurrent HTTP fetches per URL, one with a Googlebot-like user agent and Accept headers, one with a real Chrome UA. Differences between the two responses indicate cloaking or bot-specific bugs.

Stage 3 (parallel render): headless Chromium pool (16-64 workers), 3-second wait budget per page, viewport 1280x800, request interception to block third-party analytics and ads. Without ad blocking, render times b

Orphan Page Fix Playbook

Detecting orphans is straightforward; deciding what to do with each cohort is where most teams stall. A more detailed companion piece on this is at https://gist.github.com/joshbachynski/a450f9cf2f640b5a5d965a6d347fe9a3, which covers the threshold tuning that separates real orphans from sitemap noise.

Cohort A: orphans with non-zero Google Search Console impressions in the last 90 days. These are valuable; Google found them somewhere (external links, historical sitemap entries) and users still arrive. Fix: add internal links from at least two relevant hub pages. Don't rely on a single link, since template changes can remove it.

Cohort B: orphans with zero impressions but recent server-log fetches by Googlebot. Google still crawls them but no users land. Audit content quality. If valuable, link them in. If thin or duplicate, consolidate via 301 to a canonical hub or noindex if they should remain accessible without ranking.

Cohort C: orphans with zero impressions and zero Googlebot