Dev.to WebDev 🛠 Dev 👁 0 📖 4 min read

Should a broken external link fail your build? Comparing four CI policies, and the one I'd pick

Adding a link checker to CI is easy. Deciding what it should do when it finds a broken link is where teams get stuck. Internal links are straightforward: if your docs link to a page in your own repo that doesn't exist,

Adding a link checker to CI is easy. Deciding what it should do when it finds a broken link is where teams get stuck.

Internal links are straightforward: if your docs link to a page in your own repo that doesn't exist, the build should fail. You broke it; you can fix it.

External links are different. A link to someone else's site can break because their server is slow, because they rate-limit bots, because of a regional outage, or because the page genuinely disappeared. None of that is caused by the pull request being tested. So when an external link fails, what should happen?

Here are four common policies, compared.

Policy 1: fail the build on any broken link

How it works: Internal and external links are checked on every pull request. Any failure turns the build red.

Pros:

  • Simple. Nothing broken ever gets merged.
  • Broken external links are noticed immediately.

Cons:

  • Flaky builds. Builds fail for reasons unrelated to the change. A contributor fixing a typo gets a red build because a third-party site timed out.
  • People learn to ignore red builds, or to retry until green, which is worse than not checking at all.
  • Rate limits. Checking hundreds of external links on every push can get your CI's IP addresses blocked by popular sites, producing more failures.

Best for: Small projects with few external links and a tolerance for the occasional retry.

Policy 2: check only links changed in the pull request

How it works: CI checks only links that were added or modified in the diff. Existing links are left alone.

git diff --unified=0 origin/main... -- '*.md' \
  | grep -oE 'https?://[^) ">]+' | sort -u > changed-links.txt
lychee --no-progress changed-links.txt

Pros:

  • Fast and relevant. Contributors are only held responsible for links they touched.
  • New broken links, including typos, are caught before merging.
  • Far fewer external requests per build.

Cons:

  • Existing links rot unnoticed. A link that was fine when added and broke a year later is never reported.
  • Link checking that relies on parsing diffs can miss links in generated content, includes or templates.

Best for: Any project, as part of a policy. It's the best way to catch mistakes at the moment they're made.

Policy 3: warn on external, fail on internal

How it works: Every build checks all links. Internal failures fail the build. External failures produce a warning or a comment on the pull request, but the build stays green.

Pros:

  • Internal link integrity is enforced strictly.
  • External problems are visible without blocking anyone.

Cons:

  • Warnings get ignored. After a few weeks of the same five warnings on every build, nobody reads them.
  • Still makes many external requests on every build.

Best for: Teams that genuinely review warnings, or projects with a small number of external links.

Policy 4: scheduled full check, opening an issue

How it works: Pull request builds check internal links (and optionally changed external links). A separate scheduled job, say nightly or weekly, checks every link, internal and external, and opens or updates a single issue listing what's broken.

on:
  schedule:
    - cron: "17 6 * * 1"   # Mondays 06:17 UTC
  workflow_dispatch:

Pros:

  • Pull request builds stay fast and reliable.
  • Existing external links are still checked regularly.
  • Failures become a tracked task with an owner, not a red cross nobody understands.
  • Checking less often reduces rate-limit trouble, and a single slow day at a third-party site doesn't create noise if you require a link to fail on two consecutive runs before reporting it.

Cons:

  • Broken external links can exist for up to a week before being reported.
  • Someone has to own the issue, or it becomes another ignored list.

Best for: Most documentation sites and projects of any real size.

Side by side

Catches new mistakes Catches rot Stable builds External requests
1. Fail on all Yes Yes No Many, every build
2. Changed links only Yes No Mostly Few
3. Warn on external Yes Yes (if read) Yes Many, every build
4. Scheduled + issue With PR check Yes Yes Few, on a schedule

What I'd pick

A combination:

  • On every pull request: fail on broken internal links; check external links that the PR adds or changes, and fail on those too, since the author is right there to fix a typo.
  • On a schedule: check everything, and open or update one issue with the results, ignoring links that failed only once.
  • Keep an allowlist for sites that block automated checks, with a comment saying why, and review it occasionally.
  • Treat some codes differently. A 404 or 410 is almost certainly real. A 429, 403 or timeout is often the checker being blocked rather than the page being gone; record those, but don't panic over them.

This keeps the signal where it belongs: contributors are responsible for what they changed, and the project is responsible, on a regular rhythm, for everything else.

Why we care

External links break on their own schedule, not yours. Checking them continuously and reporting only what's really broken is the problem we work on at linkbruno.com.

Related reading

Which policy does your project use today, and has a flaky external link ever blocked a merge you needed?

📰 Read the original article on Dev.to WebDev

Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.