<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bipin Rimal</title>
    <description>The latest articles on DEV Community by Bipin Rimal (@bipin_rimal314).</description>
    <link>https://dev.to/bipin_rimal314</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3770936%2F8802fdab-b1ee-46ac-9f06-a5929c624019.png</url>
      <title>DEV Community: Bipin Rimal</title>
      <link>https://dev.to/bipin_rimal314</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bipin_rimal314"/>
    <language>en</language>
    <item>
      <title>Why Your Fintech API Code Examples Are a Liability</title>
      <dc:creator>Bipin Rimal</dc:creator>
      <pubDate>Thu, 12 Mar 2026 14:34:28 +0000</pubDate>
      <link>https://dev.to/bipin_rimal314/why-your-fintech-api-code-examples-are-a-liability-4afb</link>
      <guid>https://dev.to/bipin_rimal314/why-your-fintech-api-code-examples-are-a-liability-4afb</guid>
      <description>&lt;p&gt;A developer copies a code example from your API docs. Changes the account ID. Updates the amount. Hits send.&lt;/p&gt;

&lt;p&gt;The money moves. To the wrong account. Using a deprecated endpoint your docs still show as valid.&lt;/p&gt;

&lt;p&gt;In a social media API, a wrong code example posts the wrong image. Embarrassing. In a financial services API, a wrong code example moves money. And &lt;code&gt;Ctrl+Z&lt;/code&gt; is not a feature that banking infrastructure supports.&lt;/p&gt;

&lt;p&gt;Wrong API examples in fintech aren't documentation bugs. They're operational incidents waiting to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sxrwf5g43uydii0ayfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1sxrwf5g43uydii0ayfi.png" alt=" " width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stat&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;75% of production APIs have variances from their OpenAPI specs&lt;/td&gt;
&lt;td&gt;Nordic APIs / APIContext&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;70% of devs rate code examples as the #1 most important doc component&lt;/td&gt;
&lt;td&gt;SmartBear 2020 State of API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;55% of teams struggle with inconsistent/outdated documentation&lt;/td&gt;
&lt;td&gt;Postman 2025 State of the API&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;83% of developers consider doc quality when evaluating an API&lt;/td&gt;
&lt;td&gt;Monetizely&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;71% have chosen one API over another because of better docs&lt;/td&gt;
&lt;td&gt;Monetizely&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three-quarters of APIs don't match their own docs. The thing developers trust most is code examples. And doc quality is the deciding factor for most adoption decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  3 Ways Code Examples Silently Break
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrh5gi3gfpclakmryp3j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqrh5gi3gfpclakmryp3j.png" alt=" " width="800" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The endpoint changes. The example doesn't.
&lt;/h3&gt;

&lt;p&gt;Payments team ships V3 of the transfers endpoint. API reference gets updated. The four code examples in tutorials still point to V2. V2 still works — for now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Your docs still show this:
POST /v2/transfers

# Your API is actually on:
POST /v3/transfers
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developer builds an entire integration on a deprecated path.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A required field gets added. The example lies.
&lt;/h3&gt;

&lt;p&gt;Compliance mandates a new &lt;code&gt;purpose_code&lt;/code&gt; field on international transfers. The API rejects requests without it. But the quickstart guide doesn't include it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;your&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;docs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;show:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acct_xxx"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;What&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;API&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actually&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;requires:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"acct_xxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"purpose_code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"P0101"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;error&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;without&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Default values change. Silently.
&lt;/h3&gt;

&lt;p&gt;Your API defaults &lt;code&gt;settlement_speed&lt;/code&gt; from &lt;code&gt;"standard"&lt;/code&gt; (T+1) to &lt;code&gt;"instant"&lt;/code&gt;. The code example doesn't specify the field because "the default is fine."&lt;/p&gt;

&lt;p&gt;Every developer copying that example now gets instant settlement — with different fees — and doesn't know it.&lt;/p&gt;

&lt;p&gt;This is the one that should terrify you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real-World Cost
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxlx5gufywa1yvqh8xpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffxlx5gufywa1yvqh8xpw.png" alt=" " width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This isn't theoretical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PayPal&lt;/strong&gt; had endpoints listed in docs that didn't exist, undocumented webhook delays, and session timeouts merchants discovered through trial and error — in production&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Healthcare:&lt;/strong&gt; A deprecated SOAP endpoint remained accessible for 6 months while vulnerabilities were only patched in newer REST services. 450,000 patient records exposed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Financial services:&lt;/strong&gt; Average $300,000+ per hour in API-related downtime costs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failed payments&lt;/strong&gt; cost the global economy $118.5 billion annually&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Write Once, Pray Forever
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjj8xi8pa2zmjixaomgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgjj8xi8pa2zmjixaomgd.png" alt=" " width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's how fintech API documentation actually works in practice: An engineer writes a feature, someone writes the docs and code examples, and on the day they're written, everything is accurate. Then the feature evolves over 6-12 months. The API reference gets updated — maybe. But the code examples scattered across guides and tutorials? Almost certainly not.&lt;/p&gt;

&lt;p&gt;76% of failed API integrations result from inadequate documentation or support.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Stripe, Twilio, and Plaid Do
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8vm9ze6xyeyuvmvqevn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh8vm9ze6xyeyuvmvqevn.png" alt=" " width="800" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stripe:&lt;/strong&gt; SDK generation pipeline built on Ruby DSL → OpenAPI specs → auto-generated library code in multiple languages. They also built and open-sourced &lt;a href="https://markdoc.dev" rel="noopener noreferrer"&gt;Markdoc&lt;/a&gt;. Interactive examples see 62% higher engagement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twilio:&lt;/strong&gt; Migrated 5,000+ pages with Yoyodyne. Samples update automatically when API or codegen tool changes. Zero manual sync.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Plaid:&lt;/strong&gt; Discovered developers bypassed navigation for search. Expanded their search index by hundreds of entries. Behavior data drives docs improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common thread:&lt;/strong&gt; They treat code examples as testable code, not documentation text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your 4-Week Safety Net
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vx07p5l9uarecdwummc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0vx07p5l9uarecdwummc.png" alt=" " width="800" height="560"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Week 1: Audit your quickstart. Fresh environment. Run every example.
Week 2: Fix the broken quickstart examples. Highest traffic first.
Week 3: Add example testing to CI. One file, one test. Fail the build.
Week 4: Expand to top 5 integration guides. Set up freshness tracking.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tools That Help
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://pact.io" rel="noopener noreferrer"&gt;Pact&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Contract testing — catches breaking API changes before production&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://vale.sh" rel="noopener noreferrer"&gt;Vale&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Open source prose linter for style guide enforcement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Managed docs automation — style, links, terminology on every PR in CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://stoplight.io/spectral" rel="noopener noreferrer"&gt;Spectral&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;OpenAPI linting — catches spec drift&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;In most software, documentation that's 95% accurate is fine. In financial services, the 5% that's wrong is where someone loses money.&lt;/p&gt;

&lt;p&gt;Outdated docs isn't a typo. It's a bug. In fintech, that bug moves money.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;We'll be at Fintech Meetup at the end of March. If any of this resonated, let's connect. &lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;ekline.io&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>api</category>
      <category>documentation</category>
      <category>softwareengineering</category>
      <category>fintech</category>
    </item>
    <item>
      <title>Docs-as-Code: The Complete CI/CD Workflow (From Git to Production)</title>
      <dc:creator>Bipin Rimal</dc:creator>
      <pubDate>Tue, 03 Mar 2026 18:07:30 +0000</pubDate>
      <link>https://dev.to/bipin_rimal314/docs-as-code-the-complete-cicd-workflow-from-git-to-production-89b</link>
      <guid>https://dev.to/bipin_rimal314/docs-as-code-the-complete-cicd-workflow-from-git-to-production-89b</guid>
      <description>&lt;p&gt;You know what docs-as-code is. I'm not going to explain it for the 900th time. Store docs in Git, write in Markdown, review through PRs, deploy through CI/CD. Got it. Moving on.&lt;/p&gt;

&lt;p&gt;What nobody seems to write about is what a working pipeline actually looks like. The real config files, the real tradeoffs, the stuff that took us three iterations to get right. Every "docs-as-code guide" I've read either stops at the philosophy or gives you a toy example that falls apart the moment you have more than five pages.&lt;/p&gt;

&lt;p&gt;This is the actual pipeline. Config files you can copy. Decisions explained so you understand &lt;em&gt;why&lt;/em&gt;, not just &lt;em&gt;what&lt;/em&gt;. Opinions included at no extra charge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository Structure
&lt;/h2&gt;

&lt;p&gt;Start with a layout that scales. Here's what works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;your-product/
├── docs/
│   ├── getting-started/
│   │   ├── quickstart.md
│   │   ├── installation.md
│   │   └── first-project.md
│   ├── guides/
│   │   ├── authentication.md
│   │   ├── configuration.md
│   │   └── deployment.md
│   ├── api-reference/
│   │   ├── endpoints.md
│   │   └── error-codes.md
│   ├── assets/
│   │   └── images/
│   └── index.md
├── .github/
│   └── workflows/
│       └── docs.yml
├── .vale.ini              # Style linting config
├── .lychee.toml           # Link checker config
├── cspell.json            # Spell checker config
└── .ekline/
    └── config.yaml        # EkLine config (if using)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, before you blindly copy this, let me explain the decisions, because they matter more than the structure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs live in the same repo as code. Not a separate repo.&lt;/strong&gt; This is the hill I will die on. When an engineer changes the authentication flow, the docs for authentication should be right there, in the same PR. Separate repos create a gap between "code changed" and "docs should change". That gap is where documentation debt lives. Every guide that tells you to use a separate docs repo is optimizing for the docs team's convenience at the expense of accuracy.&lt;/p&gt;

&lt;p&gt;If your docs are already in a separate repo, don't panic. Everything in this guide still applies. But if you're starting fresh, co-locate them. You'll thank me when someone changes an API endpoint and the docs PR is part of the same review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Group by user task, not by content type.&lt;/strong&gt; &lt;code&gt;getting-started/&lt;/code&gt; is a user journey. &lt;code&gt;api-reference/&lt;/code&gt; is a content type. I'd love to tell you I'm perfectly consistent about this, but the truth is most real doc structures are a hybrid. The important thing: a new user should be able to look at the folder names and know where to go. If they can't, your IA needs work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Config files live at the root.&lt;/strong&gt; Not in a &lt;code&gt;.config/&lt;/code&gt; folder, not inside &lt;code&gt;docs/&lt;/code&gt;. Root level. Why? Because every tool expects them there by default, and fighting tool defaults is a waste of your finite time on earth.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Writing Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Branching
&lt;/h3&gt;

&lt;p&gt;Treat docs PRs like code PRs. Same workflow, same rigor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; docs/update-auth-guide
&lt;span class="c"&gt;# write your changes&lt;/span&gt;
git add docs/guides/authentication.md
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update auth guide for OAuth2 PKCE flow"&lt;/span&gt;
git push origin docs/update-auth-guide
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a &lt;code&gt;docs/&lt;/code&gt; prefix for documentation branches. Two reasons: it makes filtering your PR list trivial, and you can apply branch-specific CI rules (like running docs checks only on &lt;code&gt;docs/*&lt;/code&gt; branches). Small thing, surprisingly useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  PR Template
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.github/PULL_REQUEST_TEMPLATE/docs.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What changed&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- Brief description of documentation changes --&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Why&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- What triggered this update? Feature change, user feedback, bug fix? --&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Pages affected&lt;/span&gt;
&lt;span class="c"&gt;&amp;lt;!-- List the doc pages modified --&amp;gt;&lt;/span&gt;

&lt;span class="gu"&gt;## Checklist&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Tested all code examples
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Verified all links work
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Screenshots are current (if applicable)
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Reviewed on mobile/responsive view
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Why" field is the most important part of this template, and the one people skip most often. "Updated the auth docs" tells a reviewer nothing. "Updated auth docs because we switched from API keys to OAuth2 in v3.2" tells them everything. It connects the docs change to the trigger, and six months from now when someone asks "why did we rewrite this page," the answer is right there in the PR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automated Quality Checks
&lt;/h2&gt;

&lt;p&gt;This is where docs-as-code stops being a philosophy and starts being a system. Every PR that touches documentation triggers a pipeline of automated checks. Here's each stage, with the configs we actually use.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 1: Spell Checking
&lt;/h3&gt;

&lt;p&gt;The least glamorous check and the one that catches the most embarrassing mistakes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/docs.yml (spell check job)&lt;/span&gt;
&lt;span class="na"&gt;spell-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;contains(github.event.pull_request.labels.*.name, 'documentation') ||&lt;/span&gt;
      &lt;span class="s"&gt;contains(join(github.event.pull_request.changed_files), 'docs/')&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streetsidesoftware/cspell-action@v6&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**/*.md'&lt;/span&gt;
        &lt;span class="na"&gt;incremental_files_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;cspell.json&lt;/code&gt; needs a product-specific dictionary. Without one, CSpell will flag every product name, every technical term, and every acronym. After this, your engineers will disable the check within a week:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"words"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"your-product-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"OAuth"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PKCE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webhook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"webhooks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"authn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kubectl"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignorePaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"docs/assets/**"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Start with &lt;code&gt;incremental_files_only: true&lt;/code&gt;. I cannot stress this enough. Running spell check on all files the first time will generate hundreds of findings. Nobody will fix them. The check will become noise. Check only changed files, build the dictionary organically, and clean up old files as you touch them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: Link Validation
&lt;/h3&gt;

&lt;p&gt;Broken links are the cockroaches of documentation. For every one you see, there are ten you don't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Link check job&lt;/span&gt;
&lt;span class="na"&gt;link-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lycheeverse/lychee-action@v2&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
          &lt;span class="s"&gt;--no-progress&lt;/span&gt;
          &lt;span class="s"&gt;--exclude-mail&lt;/span&gt;
          &lt;span class="s"&gt;--exclude 'localhost'&lt;/span&gt;
          &lt;span class="s"&gt;--exclude '127.0.0.1'&lt;/span&gt;
          &lt;span class="s"&gt;--exclude 'example.com'&lt;/span&gt;
          &lt;span class="s"&gt;'docs/**/*.md'&lt;/span&gt;
        &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lychee is fast (Rust-based, concurrent) and handles most edge cases. Your &lt;code&gt;.lychee.toml&lt;/code&gt; config handles the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"example.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;accept&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;204&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;301&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;timeout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="py"&gt;max_retries&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Accept 429 (rate limited) as OK.&lt;/strong&gt; Some sites aggressively rate-limit automated checkers. Without this, you'll get false failures from GitHub, npm, and basically any popular site. You'll chase ghosts for an hour before you figure out it's not your links that are broken but it's the target servers telling you to slow down. Ask me how I know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: Style Guide Enforcement
&lt;/h3&gt;

&lt;p&gt;This is where consistency happens at scale. Two options, both legitimate:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Vale (DIY, full control)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Style lint job&lt;/span&gt;
&lt;span class="na"&gt;style-lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;errata-ai/vale-action@reviewdog&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/&lt;/span&gt;
        &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Option B: EkLine (managed — includes link checking and style in one action)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Docs review job&lt;/span&gt;
&lt;span class="na"&gt;docs-review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ekline-io/ekline-github-action@v6&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;content_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docs&lt;/span&gt;
        &lt;span class="na"&gt;ek_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EKLINE_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Full disclosure: EkLine is ours. The honest difference: Vale gives you granular control over every rule and costs nothing. EkLine bundles style, links, and terminology checking into one action and manages the rules for you. If you use EkLine, skip the separate link check job. That is already included. If you use Vale, keep Lychee for links.&lt;/p&gt;

&lt;p&gt;Pick based on your situation, not my sales pitch.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Pipeline
&lt;/h2&gt;

&lt;p&gt;Here's the complete workflow file. Copy this, adjust the paths, and you have a working docs-as-code pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Documentation Quality&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.md'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!README.md'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;spell-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Spell Check&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streetsidesoftware/cspell-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;incremental_files_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;link-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Link Validation&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lycheeverse/lychee-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--no-progress --exclude-mail 'docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;style-lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Style Guide&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;errata-ai/vale-action@reviewdog&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;

  &lt;span class="c1"&gt;# Optional: build and preview&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Preview&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;spell-check&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;link-check&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;style-lint&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three quality jobs run in parallel which is the point. The build step waits for all checks to pass before generating a preview. On most repos, the whole thing finishes in under a minute.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Human Review Process
&lt;/h2&gt;

&lt;p&gt;Here's the part that trips people up: automated checks don't replace human review. They change what humans review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the machine handles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spelling errors&lt;/li&gt;
&lt;li&gt;Broken links&lt;/li&gt;
&lt;li&gt;Style guide violations (headings, terminology, voice, tone)&lt;/li&gt;
&lt;li&gt;Formatting consistency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What humans handle:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the content actually accurate?&lt;/li&gt;
&lt;li&gt;Is the explanation clear to someone who doesn't already understand it?&lt;/li&gt;
&lt;li&gt;Are the code examples correct and tested? (Machines can check syntax. They can't check if your example actually solves the problem it claims to.)&lt;/li&gt;
&lt;li&gt;Is anything missing?&lt;/li&gt;
&lt;li&gt;Does the information architecture make sense?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation is the real value of docs-as-code. Not "docs in Git". That's just storage. The value is this: when a reviewer opens a PR and the automated checks have already passed, they know the formatting is clean, the links work, and the style guide is followed. They can focus entirely on whether the content is &lt;em&gt;right&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That's a fundamentally different (and much more useful) review conversation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;Once a PR merges, deploy automatically. I'll keep this section short because deployment is well-documented elsewhere and the choices are straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub Pages&lt;/strong&gt; (simplest, free):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.ref == 'refs/heads/main'&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run build&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peaceiris/actions-gh-pages@v3&lt;/span&gt;
      &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;publish_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./build&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Vercel/Netlify:&lt;/strong&gt; Connect your repo, get preview URLs on every PR, auto-deploy on merge. Both auto-detect Docusaurus, Next.js, Astro, and most doc frameworks. Setup takes about five minutes. The docs for both platforms cover this better than I can.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docs platforms (Mintlify, GitBook, ReadMe):&lt;/strong&gt; They have their own deployment pipelines: push to a branch, they rebuild. Your CI checks still run; they just validate before the platform builds.&lt;/p&gt;

&lt;p&gt;Pick one and move on. The deployment step is the least interesting part of this pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes (The Part You Actually Came Here For)
&lt;/h2&gt;

&lt;p&gt;If you've set up a docs-as-code pipeline before, you probably scrolled straight to this section. I respect that. Here's what goes wrong:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Running all checks on all files from day one.&lt;/strong&gt; You will generate 500 findings on existing docs. Nobody will fix them. The checks will become noise that everyone ignores. This is the single most common mistake, and I've watched teams make it repeatedly. Start incremental by only checking changed files. Clean up existing docs gradually, file by file, as you touch them for other reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocking PRs on warnings.&lt;/strong&gt; Set style violations to &lt;code&gt;warning&lt;/code&gt; for the first month. Let people see them, get used to the feedback loop, understand why the rules exist. Once the team accepts the system, upgrade critical rules to &lt;code&gt;error&lt;/code&gt;. If you start with &lt;code&gt;error&lt;/code&gt;, you'll get a revolt. I promise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate docs repo without a trigger.&lt;/strong&gt; If docs live in a separate repo from code, you need a way to signal "the product changed, docs might need updating." A webhook, a GitHub Action that runs on product repo changes, a Slack notification. Something. Anything. Without this signal, the docs repo slowly diverges from reality and nobody notices until a customer complains. This is how documentation debt starts: not with bad writing, but with missing signals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No code example testing.&lt;/strong&gt; The most common docs bug isn't a typo or a broken link. It's a code example that doesn't work. The developer copies your snippet, gets an error, and blames your product. Not the docs. If your docs include code snippets, consider extracting and testing them. Tools like &lt;a href="https://mdxjs.com/" rel="noopener noreferrer"&gt;mdx-js&lt;/a&gt; or custom scripts can help. At minimum, have someone actually run the examples before publishing. Yes, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring the contributor experience.&lt;/strong&gt; If your CI takes 10 minutes to run, contributors won't wait. They'll push, context-switch, forget, and your PR will go stale. Keep the docs pipeline under 2 minutes. Run checks in parallel. Use incremental checking. The fastest way to kill a docs-as-code culture is to make the feedback loop slow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start Here
&lt;/h2&gt;

&lt;p&gt;If you're setting up docs-as-code from scratch, don't try to build the whole pipeline in a day. Here's the order that works:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1:&lt;/strong&gt; Set up the repo structure. Add Lychee for link checking. This catches real problems immediately with zero configuration. You'll find broken links you didn't know existed. It's both satisfying and slightly alarming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 2:&lt;/strong&gt; Add CSpell. Build your product dictionary as you go. Add words when they're flagged incorrectly. The first few days will be annoying. By day five, it's useful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 3:&lt;/strong&gt; Add style enforcement (Vale or EkLine). Start with a preset style guide. Warnings only. Do not start with errors. I will say this as many times as it takes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 4:&lt;/strong&gt; Review the findings from week 3. Tune the rules. Disable noisy ones, tighten important ones. Upgrade key rules from warning to error. Now you have a system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ongoing:&lt;/strong&gt; Clean up existing docs gradually. Don't create a "fix all style issues" ticket. That ticket will sit in your backlog for eternity and make everyone sad. Fix issues as you touch files for other reasons. This is how real documentation quality improves: incrementally, consistently, without heroics.&lt;/p&gt;

&lt;p&gt;The pipeline is never "done." It evolves with your docs, your team, and your style guide. The important thing is that it exists. And that it runs on every PR, quietly catching the stuff that used to require a human to notice.&lt;/p&gt;

&lt;p&gt;That's what docs-as-code actually means. Not "docs in Git." Docs with the same quality guarantees we give to code.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What does your docs pipeline look like? I'd love to compare setups — especially the weird edge cases. Drop a comment or find me at &lt;a href="https://ekline.io?utm_source=devto&amp;amp;utm_medium=blog&amp;amp;utm_campaign=docs-engineering-series" rel="noopener noreferrer"&gt;ekline.io&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cicd</category>
      <category>devops</category>
      <category>documentation</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 GitHub Actions That Save Our Docs Team Hours Every Week</title>
      <dc:creator>Bipin Rimal</dc:creator>
      <pubDate>Wed, 25 Feb 2026 16:22:29 +0000</pubDate>
      <link>https://dev.to/bipin_rimal314/5-github-actions-that-save-our-docs-team-hours-every-week-jkc</link>
      <guid>https://dev.to/bipin_rimal314/5-github-actions-that-save-our-docs-team-hours-every-week-jkc</guid>
      <description>&lt;p&gt;We're a small startup. And documentation is literally our product — we build tools to help other teams keep their docs accurate. So if our own docs have broken links, inconsistent terminology, or sloppy formatting, that's not just embarrassing. It's the kind of credibility problem that makes potential customers close the tab.&lt;/p&gt;

&lt;p&gt;Here's the thing nobody tells you about running a docs-focused company: you become absurdly paranoid about your own documentation. Every broken link feels personal. Every style inconsistency keeps you up at night. But we're a small team — we can't spend our days playing copy editor on every PR.&lt;/p&gt;

&lt;p&gt;So we automated the stuff that shouldn't require a human brain. Here are the five GitHub Actions that run on every docs PR in our repo. They're all free for open source, and together they catch the vast majority of issues that used to eat up our review cycles.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Catch Broken Links Before They Go Live
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem nobody wants to admit:&lt;/strong&gt; You write docs, link to other pages, API references, external resources. You feel good about it. Three months later, half those links are dead. Someone moved a page, a third-party site decided to restructure their entire URL scheme (thanks, I love re-doing work), or you linked to a branch that got merged and deleted.&lt;/p&gt;

&lt;p&gt;Broken links are the fastest way to make users distrust your documentation. One dead link and they start wondering what else is wrong. It's like finding a cockroach in a restaurant — you know there are more.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool:&lt;/strong&gt; &lt;a href="https://github.com/lycheeverse/lychee-action" rel="noopener noreferrer"&gt;Lychee&lt;/a&gt; — a Rust-based link checker that's fast enough to run on every PR without making you want to throw your laptop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Links&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.md'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;link-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check links&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lycheeverse/lychee-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
            &lt;span class="s"&gt;--no-progress&lt;/span&gt;
            &lt;span class="s"&gt;--exclude-mail&lt;/span&gt;
            &lt;span class="s"&gt;'docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Lychee and not the Node-based alternatives:&lt;/strong&gt; Speed. Lychee is written in Rust and checks links concurrently. On our docs repo (~200 pages), it finishes in under 30 seconds. We tried &lt;code&gt;markdown-link-check&lt;/code&gt; first — 3+ minutes. When you're running this on every PR, that difference matters. Nobody wants to wait for CI when they just fixed a typo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We learned this the hard way:&lt;/strong&gt; Add a &lt;code&gt;.lychee.toml&lt;/code&gt; config to exclude URLs that are expected to fail in CI. We spent a very confusing afternoon debugging "broken" links that were just localhost examples in code snippets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s"&gt;"localhost"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"127.0.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s"&gt;"example.com"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; About 2 hours per week of "why does this link 404" tickets. Which, honestly, were the most soul-crushing tickets to deal with.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Enforce Your Style Guide Automatically
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Your style guide says "use second person" and "prefer active voice." Your docs say "the configuration can be completed by the user." Everyone knows the rules. Nobody catches everything. Including you — especially at 4pm on a Friday.&lt;/p&gt;

&lt;p&gt;Style guide violations are like dishes in the sink. Nobody notices one. But after a month, your kitchen is a health hazard and nobody wants to cook. After a year, your docs read like they were written by 20 different people — because they were.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool:&lt;/strong&gt; &lt;a href="https://github.com/errata-ai/vale-action" rel="noopener noreferrer"&gt;Vale&lt;/a&gt; — a prose linter with presets for Google, Microsoft, and other style guides. Think ESLint, but for words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prose Lint&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Vale lint&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;errata-ai/vale-action@reviewdog&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docs/&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
          &lt;span class="na"&gt;vale_flags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--minAlertLevel=warning"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key configuration is the &lt;code&gt;.vale.ini&lt;/code&gt; file in your repo root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="py"&gt;StylesPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;.vale/styles&lt;/span&gt;
&lt;span class="py"&gt;MinAlertLevel&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;

&lt;span class="nn"&gt;[docs/*.md]&lt;/span&gt;
&lt;span class="py"&gt;BasedOnStyles&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Google&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This uses Google's style guide out of the box. Want Microsoft's instead? Change &lt;code&gt;Google&lt;/code&gt; to &lt;code&gt;Microsoft&lt;/code&gt;. Want your own custom rules? Create YAML files in &lt;code&gt;.vale/styles/YourCompany/&lt;/code&gt;. Fair warning: you will go down a rabbit hole. It's kind of fun, though.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Honest caveat:&lt;/strong&gt; Vale is powerful but it has a learning curve. Writing custom rules is its own skill, and it took us a few iterations to get our config dialed in without drowning in false positives. If you want something that works out of the box with less yak-shaving, that's where tools like EkLine come in (see #5). But if you're the type who enjoys fine-grained control and building your own system, Vale is the gold standard. No question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; About 1-2 hours per week of leaving "please change this to active voice" comments. My least favorite type of review comment to leave, and probably everyone's least favorite to receive.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Kill Typos Without a Human Proofreader
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Traditional spell checkers and technical writing are mortal enemies. They flag &lt;code&gt;kubectl&lt;/code&gt;, &lt;code&gt;OAuth&lt;/code&gt;, &lt;code&gt;GraphQL&lt;/code&gt;, and every product name as errors. So people turn them off. And then actual typos ship. And then you're the company whose security doc says "authetication."&lt;/p&gt;

&lt;p&gt;(Don't laugh. We caught that one in a PR. Twice.)&lt;/p&gt;

&lt;p&gt;Documentation with typos reads as careless. And in code examples, a typo isn't just embarrassing — it means the example literally doesn't work. A developer copies your snippet, gets an error, and blames your product. Not the typo. Your product.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool:&lt;/strong&gt; &lt;a href="https://github.com/streetsidesoftware/cspell-action" rel="noopener noreferrer"&gt;CSpell&lt;/a&gt; — a spell checker that actually understands technical terminology, so it won't flag every third word in your API docs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Spell Check&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cspell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Spell check&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streetsidesoftware/cspell-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;incremental_files_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The magic is in &lt;code&gt;cspell.json&lt;/code&gt;, where you define your product dictionary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"0.2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dictionaries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"tech-terms"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dictionaryDefinitions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tech-terms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".cspell/tech-terms.txt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"addWords"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"words"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"EkLine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ekline"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Docusaurus"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mintlify"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitBook"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"devops"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cicd"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ignorePaths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"node_modules/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"*.min.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Do this or you'll hate yourself:&lt;/strong&gt; Use &lt;code&gt;incremental_files_only: true&lt;/code&gt; to only check changed files. We made the mistake of running it on everything the first time. Hundreds of findings. Nobody wanted to touch it. It sat in a ticket for three weeks before we wised up and switched to incremental-only. Build the dictionary over time — don't try to boil the ocean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; About 30 minutes per week, plus the priceless benefit of never shipping "authetication" in a security doc. Again.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Stop Docs PRs From Going Stale
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Someone opens a docs PR. Reviewer is busy. A week passes. The contributor moves on to something else. The PR sits for two months, accumulates merge conflicts, and eventually gets closed with a sad little "closing due to inactivity" comment.&lt;/p&gt;

&lt;p&gt;We've all been on both sides of this. It's demoralizing for contributors, and for small teams it creates this invisible drag — every time you open the PR list, you have to mentally re-process "is this still relevant?" for PRs that should have been closed weeks ago.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool:&lt;/strong&gt; &lt;a href="https://github.com/actions/stale" rel="noopener noreferrer"&gt;Stale&lt;/a&gt; — GitHub's official action for managing inactive issues and PRs. Simple, effective, slightly passive-aggressive in the best way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Stale Docs PRs&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;9&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;MON'&lt;/span&gt;  &lt;span class="c1"&gt;# Every Monday at 9am&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;stale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/stale@v9&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;repo-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;stale-pr-message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="s"&gt;This PR has been inactive for 14 days.&lt;/span&gt;
            &lt;span class="s"&gt;If the changes are still needed, please rebase&lt;/span&gt;
            &lt;span class="s"&gt;and request a re-review. Otherwise, it will be&lt;/span&gt;
            &lt;span class="s"&gt;closed in 7 days.&lt;/span&gt;
          &lt;span class="na"&gt;stale-pr-label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;stale'&lt;/span&gt;
          &lt;span class="na"&gt;days-before-pr-stale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;14&lt;/span&gt;
          &lt;span class="na"&gt;days-before-pr-close&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;21&lt;/span&gt;
          &lt;span class="na"&gt;only-labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;documentation'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this matters more than it sounds:&lt;/strong&gt; Before we added this, our PR list was a graveyard. Eight open PRs, four of them from two months ago. Every Monday's standup included someone asking "should we close these?" and nobody committing to it. Now the bot handles it. The mental overhead just... disappeared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One thing we got wrong at first:&lt;/strong&gt; Set &lt;code&gt;only-labels: 'documentation'&lt;/code&gt; so this only affects docs PRs. Code PRs have different lifecycle expectations. We didn't scope it initially and accidentally auto-closed a feature PR that was waiting on a design review. That was a fun conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; About 1 hour per week of PR grooming and "is this still needed?" purgatory.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Automated Docs Review With Inline PR Comments
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; Actions #1-3 each solve one piece of the puzzle. But running three separate actions means three separate configurations, three sets of output to parse, and no single view of "how's this PR's documentation quality, overall?"&lt;/p&gt;

&lt;p&gt;What if one action could check your style guide, validate links, flag terminology issues, and give you a quality score — all as inline PR comments, right on the lines where the issues are?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The tool:&lt;/strong&gt; &lt;a href="https://github.com/ekline-io/ekline-github-action" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt; — full disclosure, this is ours. I'll keep this brief and honest.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Documentation Review&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.md'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docs-review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EkLine Docs Review&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ekline-io/ekline-github-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docs&lt;/span&gt;
          &lt;span class="na"&gt;ek_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EKLINE_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It analyzes changed files and leaves inline review comments directly on the PR — style guide compliance, link validity, terminology consistency, quality score per file. Not a wall of CI output. Comments on the exact lines where the issues are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use EkLine vs. the DIY stack:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DIY Stack&lt;/th&gt;
&lt;th&gt;EkLine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hours (configure each tool)&lt;/td&gt;
&lt;td&gt;Minutes (one action, managed rules)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Customization&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full control over every rule&lt;/td&gt;
&lt;td&gt;Presets + custom config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Free (all open source)&lt;/td&gt;
&lt;td&gt;Free for public repos, paid for private&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Separate CI logs per tool&lt;/td&gt;
&lt;td&gt;Unified inline PR comments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;You maintain configs + updates&lt;/td&gt;
&lt;td&gt;Managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Teams who want granular control&lt;/td&gt;
&lt;td&gt;Teams who want it working now&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We obviously eat our own dogfood — EkLine runs on every PR we open. But the honest truth is: if you enjoy building your own toolchain and you want total control, the DIY stack (Vale + Lychee + CSpell) is excellent. If you're a small team and you'd rather spend your time writing docs than configuring linters, that's what we built EkLine for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time saved:&lt;/strong&gt; About 3-4 hours per week of mechanical review work.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Combined Workflow
&lt;/h2&gt;

&lt;p&gt;You don't have to pick just one. Here's a minimal combined workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Docs Quality&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.md'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;spell-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;streetsidesoftware/cspell-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;incremental_files_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;link-check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lycheeverse/lychee-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--no-progress --exclude-mail 'docs/**/*.md'&lt;/span&gt;
          &lt;span class="na"&gt;fail&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;docs-review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ekline-io/ekline-github-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docs&lt;/span&gt;
          &lt;span class="na"&gt;ek_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EKLINE_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The three jobs run in parallel. Most PR builds finish in under a minute. Contributors get feedback before a human reviewer even opens the PR.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Actually Looks Like in Practice
&lt;/h2&gt;

&lt;p&gt;Before we set this up, our docs PR review went like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contributor opens PR&lt;/li&gt;
&lt;li&gt;Waits 1-3 days for reviewer (we're a small team, we're busy)&lt;/li&gt;
&lt;li&gt;Reviewer leaves 8 comments — 3 about style, 2 about links, 1 typo, 2 about actual content&lt;/li&gt;
&lt;li&gt;Contributor fixes, pushes again&lt;/li&gt;
&lt;li&gt;Reviewer re-reviews (another day)&lt;/li&gt;
&lt;li&gt;Merged on day 5&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Contributor opens PR&lt;/li&gt;
&lt;li&gt;Automated checks run in 60 seconds&lt;/li&gt;
&lt;li&gt;Contributor sees inline feedback, fixes mechanical issues themselves&lt;/li&gt;
&lt;li&gt;Reviewer sees clean PR, focuses on one question: "Is this content accurate and helpful?"&lt;/li&gt;
&lt;li&gt;Merged same day&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The biggest shift isn't time saved — it's &lt;strong&gt;who fixes what&lt;/strong&gt;. Contributors fix their own mechanical issues because the feedback is instant and specific. It's not a person telling them "you got this wrong." It's a bot, and somehow that stings less. Reviewers stop being the grammar police and start being the content experts they were hired to be.&lt;/p&gt;

&lt;p&gt;That's the real win. Not the hours. The sanity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start With One, Add More Later
&lt;/h2&gt;

&lt;p&gt;If you're starting from zero, don't set up all five on day one. You'll spend more time configuring tools than writing docs, and that defeats the entire point.&lt;/p&gt;

&lt;p&gt;Here's the order I'd recommend:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start with Lychee&lt;/strong&gt; (link checking). Broken links are the most common and most damaging docs issue. Basically zero configuration. Immediate value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add CSpell&lt;/strong&gt; once you've built a product dictionary. This takes about a week of "add to dictionary" clicks before it stops being annoying and starts being useful.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Vale or EkLine&lt;/strong&gt; when you have a style guide you actually want to enforce. If you don't have a style guide yet, pick Google's and customize from there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add Stale&lt;/strong&gt; when your PR backlog grows beyond what you can track in your head. For us, that was about PR number six.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each one is useful on its own. Together, they're a system. But a system you build up over time — not one you configure in a weekend and then never touch again (because that system will rot just like your docs).&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's in your docs CI pipeline? We're always looking for new tools to try. Drop a comment or reach out — we genuinely want to know what's working for other teams.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;About the author:&lt;/strong&gt; Bipin works on documentation tooling at &lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt; — a small team building documentation quality tools. He's interested in how teams scale documentation quality without scaling review burden. Check out our public repos at &lt;a href="https://github.com/ekline-io/ekline-github-action" rel="noopener noreferrer"&gt;github.com/ekline-io/ekline-github-action&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>documentation</category>
    </item>
    <item>
      <title>Automating Documentation Review in Your CI/CD Pipeline</title>
      <dc:creator>Bipin Rimal</dc:creator>
      <pubDate>Fri, 13 Feb 2026 12:34:59 +0000</pubDate>
      <link>https://dev.to/bipin_rimal314/automating-documentation-review-in-your-cicd-pipeline-goj</link>
      <guid>https://dev.to/bipin_rimal314/automating-documentation-review-in-your-cicd-pipeline-goj</guid>
      <description>&lt;p&gt;You lint your code. You run tests. You check for security vulnerabilities.&lt;/p&gt;

&lt;p&gt;But what about your documentation?&lt;/p&gt;

&lt;p&gt;Most teams treat docs as second-class citizens in CI/CD. Code gets automated quality gates; docs get... a human reading them whenever they have time.&lt;/p&gt;

&lt;p&gt;Here's how to change that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Documentation Review is a Bottleneck
&lt;/h2&gt;

&lt;p&gt;Every docs PR in our repo sat waiting for review. Someone had to manually check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are headings capitalized correctly?&lt;/li&gt;
&lt;li&gt;Did the author use our terminology ("click" not "select")?&lt;/li&gt;
&lt;li&gt;Are all the links still valid?&lt;/li&gt;
&lt;li&gt;Is the structure consistent with other pages?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was tedious for reviewers and slow for contributors. Engineers would submit docs, wait days for feedback, then get a list of nitpicky style fixes.&lt;/p&gt;

&lt;p&gt;We decided to automate the mechanical stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Automated
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Style guide compliance&lt;/td&gt;
&lt;td&gt;Manual review&lt;/td&gt;
&lt;td&gt;Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken links&lt;/td&gt;
&lt;td&gt;Occasional spot checks&lt;/td&gt;
&lt;td&gt;Every PR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terminology consistency&lt;/td&gt;
&lt;td&gt;"I think we use X?"&lt;/td&gt;
&lt;td&gt;Enforced&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heading format&lt;/td&gt;
&lt;td&gt;Manual review&lt;/td&gt;
&lt;td&gt;Automated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quality metrics&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Per-file scores&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The Setup: GitHub Actions + EkLine
&lt;/h2&gt;

&lt;p&gt;We use &lt;a href="https://github.com/ekline-io/ekline-github-action" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt; for automated docs review. Here's our workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Documentation Review&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;docs/**'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*.md'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;docs-review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;EkLine Docs Review&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ekline-io/ekline-github-action@v6&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./docs&lt;/span&gt;
          &lt;span class="na"&gt;ek_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.EKLINE_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;github_token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-review&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. When someone opens a PR that touches documentation, EkLine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Analyzes the changed files against our style guide&lt;/li&gt;
&lt;li&gt;Checks all links&lt;/li&gt;
&lt;li&gt;Validates terminology&lt;/li&gt;
&lt;li&gt;Leaves inline comments directly on the PR&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What the Feedback Looks Like
&lt;/h2&gt;

&lt;p&gt;Instead of a wall of linter output in CI logs, contributors get comments exactly where issues are:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9l86qv3q5zzj8zxpw3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9l86qv3q5zzj8zxpw3w.png" alt=" " width="800" height="618"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The feedback is actionable: "Define 'MCP', if it's unfamiliar to the audience." with a suggestion to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Your Style Rules
&lt;/h2&gt;

&lt;p&gt;EkLine comes with presets (Google, Microsoft), but you can customize:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .ekline/config.yaml&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;headings&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;capitalization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sentence-case&lt;/span&gt;
  &lt;span class="na"&gt;voice&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;prefer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;active&lt;/span&gt;
  &lt;span class="na"&gt;terminology&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;banned&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;utilize&lt;/span&gt;  &lt;span class="c1"&gt;# use "use"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;leverage&lt;/span&gt; &lt;span class="c1"&gt;# use "use"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;click&lt;/span&gt;    &lt;span class="c1"&gt;# use "select"&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;API&lt;/span&gt;      &lt;span class="c1"&gt;# not "api" or "Api"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results After 3 Months
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg. review cycles per docs PR&lt;/td&gt;
&lt;td&gt;2.4&lt;/td&gt;
&lt;td&gt;1.4&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Time to first feedback&lt;/td&gt;
&lt;td&gt;1-3 days&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Style-related review comments&lt;/td&gt;
&lt;td&gt;~60%&lt;/td&gt;
&lt;td&gt;~10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken links shipped&lt;/td&gt;
&lt;td&gt;Monthly&lt;/td&gt;
&lt;td&gt;Rare&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The biggest win wasn't efficiency—it was &lt;strong&gt;contributor confidence&lt;/strong&gt;. Engineers now get instant feedback, fix issues themselves, and submit cleaner PRs. They're not waiting days just to learn they used the wrong heading style.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Automating the "What Needs Updating?" Problem
&lt;/h2&gt;

&lt;p&gt;Automated review catches issues in what you submit. But there's a harder problem: knowing what &lt;em&gt;should&lt;/em&gt; be submitted after a product change.&lt;/p&gt;

&lt;p&gt;EkLine also has a &lt;strong&gt;Docs Agent&lt;/strong&gt; that addresses this. You can prompt it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The user invitation flow changed from 2 steps to 3 steps"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Scan your docs repo for all files mentioning invitations&lt;/li&gt;
&lt;li&gt;Draft updated content&lt;/li&gt;
&lt;li&gt;Create a PR with the changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This shifts documentation maintenance from "hope someone remembers" to "tell the agent what changed." We're still rolling this out, but early results show it dramatically reduces docs drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives to Consider
&lt;/h2&gt;

&lt;p&gt;If EkLine doesn't fit your needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vale.sh/" rel="noopener noreferrer"&gt;Vale&lt;/a&gt;&lt;/strong&gt; — The underlying linter many tools use. More DIY, but very flexible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://alexjs.com/" rel="noopener noreferrer"&gt;alex&lt;/a&gt;&lt;/strong&gt; — Focused on inclusive language checking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;markdownlint&lt;/strong&gt; — Basic Markdown formatting rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference with EkLine is the GitHub integration (inline PR comments) and managed style guide configs. If you want to build your own setup, Vale is excellent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should You Automate Docs Review?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Yes, if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple people contribute to documentation&lt;/li&gt;
&lt;li&gt;You have (or want) a style guide&lt;/li&gt;
&lt;li&gt;Review cycles are slowing down releases&lt;/li&gt;
&lt;li&gt;You're already using docs-as-code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maybe not, if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo writer on a small project&lt;/li&gt;
&lt;li&gt;Docs are in Confluence/Notion (no Git integration)&lt;/li&gt;
&lt;li&gt;Your review process works fine&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sign up at &lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;ekline.io&lt;/a&gt; — free for public repos, 30-day trial on Standard ($50/user/month)&lt;/li&gt;
&lt;li&gt;Add the GitHub Action to your repo&lt;/li&gt;
&lt;li&gt;Open a PR with docs changes&lt;/li&gt;
&lt;li&gt;See the automated review&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Full setup guide: &lt;a href="https://docs.ekline.io" rel="noopener noreferrer"&gt;docs.ekline.io&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in Your Docs CI?
&lt;/h2&gt;

&lt;p&gt;I'm curious what other teams are doing for documentation quality. Are you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running any automated checks?&lt;/li&gt;
&lt;li&gt;Using different tools?&lt;/li&gt;
&lt;li&gt;Still doing everything manually?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drop a comment—I'd love to learn from your setup.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bipin works on documentation tooling at &lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt;. He's interested in how teams scale documentation quality without scaling review burden.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>devops</category>
      <category>github</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Your Documentation is Lying to Your Users</title>
      <dc:creator>Bipin Rimal</dc:creator>
      <pubDate>Fri, 13 Feb 2026 12:32:51 +0000</pubDate>
      <link>https://dev.to/bipin_rimal314/your-documentation-is-lying-to-your-users-41o6</link>
      <guid>https://dev.to/bipin_rimal314/your-documentation-is-lying-to-your-users-41o6</guid>
      <description>&lt;p&gt;Let me be direct: your documentation is probably lying to your users right now.&lt;/p&gt;

&lt;p&gt;Not maliciously. Not intentionally. But lying nonetheless.&lt;/p&gt;

&lt;p&gt;That endpoint you deprecated three months ago? Still documented as active. That config flag you renamed in v2.3? Still showing the old name. That "quick start" guide that takes 47 steps because your product evolved?&lt;/p&gt;

&lt;p&gt;Every day, developers hit your docs, follow instructions that no longer work, and quietly conclude your product is broken. They don't file a bug. They don't complain. They just leave.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Math Doesn't Work
&lt;/h2&gt;

&lt;p&gt;Here's the uncomfortable reality most teams avoid:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Documentation decays faster than you can maintain it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about it. Every feature ship, every API change, every UI update, every renamed variable creates potential doc drift. In a healthy engineering org shipping weekly? That's 50+ potential documentation landmines per year. Per product.&lt;/p&gt;

&lt;p&gt;Now look at your docs team. One person? Two? Part-time contributors who'd rather be coding?&lt;/p&gt;

&lt;p&gt;The math doesn't work. It never did.&lt;/p&gt;

&lt;h2&gt;
  
  
  "We'll Update Docs Before Release"
&lt;/h2&gt;

&lt;p&gt;Every team says this. Almost none do it consistently.&lt;/p&gt;

&lt;p&gt;Why? Because documentation updates compete with shipping features. And shipping features wins. Every time.&lt;/p&gt;

&lt;p&gt;This isn't a discipline problem. It's a systems problem.&lt;/p&gt;

&lt;p&gt;Your CI catches code bugs before merge. Your tests catch regressions. Your linters catch style violations.&lt;/p&gt;

&lt;p&gt;What catches documentation drift? A customer complaint three months later? A support ticket from someone who wasted an hour on outdated instructions?&lt;/p&gt;

&lt;p&gt;That's not a system. That's hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bug Reframe
&lt;/h2&gt;

&lt;p&gt;Here's what changed my thinking: &lt;strong&gt;treat outdated documentation as a bug.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not a "nice to have." Not a "we'll get to it." A bug. With the same urgency you'd give a production incident.&lt;/p&gt;

&lt;p&gt;Because that's what it is. When your docs say one thing and your product does another, that's a defect in your customer experience. It's a reliability issue.&lt;/p&gt;

&lt;p&gt;Bugs get tracked. Bugs get prioritized. Bugs get fixed.&lt;/p&gt;

&lt;p&gt;Documentation drift? It just... accumulates. Until someone notices. Which is usually a customer. Who's now frustrated.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Works
&lt;/h2&gt;

&lt;p&gt;After watching this pattern repeat across teams, here's what I've seen work:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Automate the detection, not just the writing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hard part isn't writing docs. It's &lt;em&gt;knowing&lt;/em&gt; when docs need updating.&lt;/p&gt;

&lt;p&gt;When your product changes, something should flag which documentation is now potentially wrong. Not "maybe we should check the docs." A specific alert: "This PR modified the authentication flow. These 4 doc pages reference authentication."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Put docs in the same workflow as code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docs-as-code isn't just a storage strategy. It's a workflow strategy.&lt;/p&gt;

&lt;p&gt;If docs live in the same repo, reviewed in the same PRs, tested in the same CI—they get the same attention as code. Not because people suddenly care more about docs. Because the system treats them equally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Make "done" include documentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is cultural, but systems can enforce it.&lt;/p&gt;

&lt;p&gt;A feature isn't shipped until the docs are updated. A breaking change isn't merged until the migration guide exists. Not as a suggestion. As a gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tooling Gap
&lt;/h2&gt;

&lt;p&gt;For code quality, we have: linters, formatters, type checkers, test frameworks, security scanners, dependency auditors.&lt;/p&gt;

&lt;p&gt;For documentation quality, most teams have: a human reading it when they have time.&lt;/p&gt;

&lt;p&gt;That gap is why docs rot faster than code.&lt;/p&gt;

&lt;p&gt;The fix isn't more humans. It's applying the same thinking we already apply to code: automated checks, continuous validation, systematic enforcement.&lt;/p&gt;

&lt;p&gt;Style guide violations? Flag them automatically. Broken links? Catch them in CI. Terminology inconsistency? Enforce it the same way you enforce code style.&lt;/p&gt;

&lt;p&gt;This isn't complicated. We just haven't bothered to apply existing patterns to documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Cost
&lt;/h2&gt;

&lt;p&gt;"Our docs are a little out of date" sounds minor.&lt;/p&gt;

&lt;p&gt;Here's what it actually costs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Support tickets&lt;/strong&gt; for problems the docs created&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lost deals&lt;/strong&gt; when prospects can't complete your quick start&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Churn&lt;/strong&gt; from users who concluded your product doesn't work&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engineering time&lt;/strong&gt; answering questions that docs should handle&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reputation damage&lt;/strong&gt; in every "your docs are wrong" GitHub issue&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most teams never connect these costs to documentation. They're too diffuse. Too delayed.&lt;/p&gt;

&lt;p&gt;But they're real. And they compound.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Challenge
&lt;/h2&gt;

&lt;p&gt;Here's something you can do in 10 minutes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pick your most important user journey (signup, first API call, basic integration)&lt;/li&gt;
&lt;li&gt;Follow your own docs as if you've never seen your product&lt;/li&gt;
&lt;li&gt;Note every place where instructions don't match reality&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most teams find 3-5 issues. In their most important flow. That users hit every day.&lt;/p&gt;

&lt;p&gt;That's not a documentation problem. That's a customer experience bug hiding in plain sight.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Use
&lt;/h2&gt;

&lt;p&gt;Full disclosure: I use &lt;a href="https://ekline.io" rel="noopener noreferrer"&gt;EkLine&lt;/a&gt; for automated docs review. It runs in CI, flags style issues, catches broken links, and tells me when product changes affect documentation.&lt;/p&gt;

&lt;p&gt;It's not the only option. &lt;a href="https://vale.sh/" rel="noopener noreferrer"&gt;Vale&lt;/a&gt; is excellent if you want to build your own system. The point isn't the tool—it's treating documentation with the same rigor we apply to code.&lt;/p&gt;

&lt;p&gt;Whatever you use, the first step is the same: stop treating documentation as a writing problem. Start treating it as a systems problem.&lt;/p&gt;

&lt;p&gt;The writing was never the hard part.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's the oldest lie in your documentation? I'm genuinely curious—drop a comment. No judgment. We've all been there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>devops</category>
      <category>devrel</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
