<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://ka2in.github.io/guides/enterprise-documentation</id>
    <title>Farowave · Enterprise Documentation</title>
    <updated>2026-07-28T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://ka2in.github.io/guides/enterprise-documentation"/>
    <subtitle>Technical resources, tooling guides, and documentation strategy for SaaS and regulated industries.</subtitle>
    <icon>https://ka2in.github.io/img/favicon.ico</icon>
    <rights>Copyright © 2026 Farowave</rights>
    <entry>
        <title type="html"><![CDATA[Why your OpenAPI spec passes linting and still fails your developers]]></title>
        <id>https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit</id>
        <link href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit"/>
        <updated>2026-07-28T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[A documentation audit of the Frankfurter v1 API — and what it reveals about the gap between technically valid and developer-ready.]]></summary>
        <content type="html"><![CDATA[<p>API documentation is only as good as it helps a developer reach their goal without consulting external resources, reverse-engineering silent errors, or opening a support ticket to decipher a cryptic response.</p>
<p>A spec that passes linting is officially done. The tooling says it's valid, the CI pipeline goes green, and the documentation ships alongside the release. What linting cannot measure is whether a developer facing the API for the first time can actually use it: whether the descriptions explain what the API does rather than merely naming it, whether the error responses tell the user why a request failed and how to fix it, and whether the edge cases that will inevitably be hit in production are documented before they become support tickets.</p>
<p>To illustrate the difference concretely, we audited the Frankfurter v1 OpenAPI specification — a real, publicly available API with a technically valid spec. The Frankfurter API provides endpoints to retrieve the latest currency rates, historical data for specific dates, time series over a given period, and currency metadata.</p>
<p>The original spec lints cleanly. What it doesn't do is tell a developer everything they need, as the following table shows.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-the-audit-found">What the audit found<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#what-the-audit-found" class="hash-link" aria-label="Direct link to What the audit found" title="Direct link to What the audit found" translate="no">​</a></h2>
<table><thead><tr><th>Gap</th><th>Original spec</th><th>Improved spec</th></tr></thead><tbody><tr><td><strong>Error response descriptions</strong></td><td><code>description: Resource not found</code></td><td>Describes specific causes: invalid currency code, date before 1999-01-04, end date before start date</td></tr><tr><td><strong>Silent date adjustment</strong></td><td>Not documented</td><td>Documents that a start date before 1999-01-04 is silently adjusted to 1999-01-04, and that the adjusted date appears in the response body</td></tr><tr><td><strong>Today's rates instability</strong></td><td>Not mentioned</td><td>Explicitly states that data returned for today may update if new rates are published during the day</td></tr><tr><td><strong>Working days constraint</strong></td><td>Not mentioned</td><td>All time series endpoints document that results only include working days — weekends and bank holidays are excluded</td></tr><tr><td><strong>Base currency default</strong></td><td>No default declared on the <code>base</code> parameter</td><td><code>default: EUR</code> declared and visible in the rendered output</td></tr><tr><td><strong>Response examples</strong></td><td>No examples on any schema</td><td>Real response examples on all schemas, sourced from live API calls</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-details">The details<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#the-details" class="hash-link" aria-label="Direct link to The details" title="Direct link to The details" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-response-descriptions">Error response descriptions<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#error-response-descriptions" class="hash-link" aria-label="Direct link to Error response descriptions" title="Direct link to Error response descriptions" translate="no">​</a></h3>
<p>A 404 with no context forces the developer to guess what went wrong. Naming the cause cuts debugging time and reduces support overhead.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">responses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">NotFound</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Resource not found</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">responses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">NotFound</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">|</span><span class="token scalar string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      No data found for the requested date or currency code.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Common causes: invalid currency code, date before 1999-01-04</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      on the /{date} endpoint, or end date before start date on</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      period endpoints.</span><br></div></code></pre></div></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="silent-date-adjustment">Silent date adjustment<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#silent-date-adjustment" class="hash-link" aria-label="Direct link to Silent date adjustment" title="Direct link to Silent date adjustment" translate="no">​</a></h3>
<p>Without this, a developer whose requested date doesn't match the response date will assume a bug in their own code rather than an API behaviour. This is one of the more insidious gaps in developer-written specs — the API does something unexpected and says nothing about it.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">start_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Returns historical rates for every day within a</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      time period starting from the provided date until today.</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">start_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">|</span><span class="token scalar string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Returns currency rates from a specific start date up to the present.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      If the start date falls before 1999-01-04, the API silently adjusts</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      it to 1999-01-04, which is the earliest date for which data is available.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      The adjusted start date is reflected in the response body.</span><br></div></code></pre></div></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="todays-rates-instability">Today's rates instability<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#todays-rates-instability" class="hash-link" aria-label="Direct link to Today's rates instability" title="Direct link to Today's rates instability" translate="no">​</a></h3>
<p>A developer caching today's rates or comparing them across calls needs to know the data isn't stable until the daily update completes around 16:00 CET. The original spec gave no indication that today's response is a moving target.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">/latest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Returns the last working day's rates.</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">/latest</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">|</span><span class="token scalar string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Fetch the latest working day's currency rates, updated daily</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      around 16:00 CET. Data returned for today may update if new</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      rates are published during the day.</span><br></div></code></pre></div></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="working-days-constraint">Working days constraint<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#working-days-constraint" class="hash-link" aria-label="Direct link to Working days constraint" title="Direct link to Working days constraint" translate="no">​</a></h3>
<p>A developer expecting continuous date coverage will be confused by gaps in the time series response. Weekends and bank holidays produce no data — documenting this prevents incorrect assumptions about missing entries.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">start_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">end_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Returns historical rates for every day within</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      a time period.</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">start_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain">..</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">end_date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">|</span><span class="token scalar string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Returns currency rates from a specific start date up to an end date.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Dates are stored in UTC. Results only include working days —</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      weekends and bank holidays are excluded.</span><br></div></code></pre></div></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="base-currency-default">Base currency default<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#base-currency-default" class="hash-link" aria-label="Direct link to Base currency default" title="Direct link to Base currency default" translate="no">​</a></h3>
<p>A developer who omits the <code>base</code> parameter needs to know what the API assumes. Without a declared default they have to test or read external documentation to find out. Declaring it in the spec makes it visible in every tool that renders the spec — Redocly, Swagger UI, Postman.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">base</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> base</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">in</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> query</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Base currency to convert from</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">schema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">$ref</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"#/components/schemas/base"</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">components</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">schemas</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">baseIn</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">allOf</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">$ref</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"#/components/schemas/base"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">default</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> EUR</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Base currency for the rates. Defaults to EUR when not specified.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">parameters</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">base</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> base</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">in</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> query</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token scalar string" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      Base currency for the rates. All rates in the response are expressed</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token scalar string" style="color:rgb(255, 121, 198)">      relative to this currency. Defaults to EUR when not specified.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">schema</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">$ref</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"#/components/schemas/baseIn"</span><br></div></code></pre></div></div></div></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="response-examples">Response examples<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#response-examples" class="hash-link" aria-label="Direct link to Response examples" title="Direct link to Response examples" translate="no">​</a></h3>
<p>Examples let a developer verify their integration against a known-good response shape before making a real API call. They also make schema definitions immediately readable — a developer can scan the example and understand the data structure in seconds rather than parsing nested schema definitions.</p>
<div class="theme-tabs-container tabs-container tabList__CuJ"><ul role="tablist" aria-orientation="horizontal" class="tabs"><li role="tab" tabindex="0" aria-selected="true" class="tabs__item tabItem_LNqP tabs__item--active">Before</li><li role="tab" tabindex="-1" aria-selected="false" class="tabs__item tabItem_LNqP">After</li></ul><div class="margin-top--md"><div role="tabpanel" class="tabItem_Ymn6"><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">singleDateRates</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> object</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> number</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">base</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> string</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> string</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">rates</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> object</span><br></div></code></pre></div></div></div><div role="tabpanel" class="tabItem_Ymn6" hidden=""><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">singleDateRates</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> object</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> number</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">base</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> string</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> string</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">rates</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> object</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">example</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1.0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">base</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> EUR</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"2026-07-24"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">rates</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">AUD</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1.6281</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">USD</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">1.1418</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">CHF</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">0.9302</span><br></div></code></pre></div></div></div></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-this-means-for-your-api-documentation">What this means for your API documentation<a href="https://ka2in.github.io/guides/enterprise-documentation/2026/07/28/frankfurter-openapi-audit#what-this-means-for-your-api-documentation" class="hash-link" aria-label="Direct link to What this means for your API documentation" title="Direct link to What this means for your API documentation" translate="no">​</a></h2>
<p>Each of the gaps above passed linting. None of them were bugs in the spec — the original was structurally valid and rendered correctly. What it lacked was the layer of context that turns a technically correct document into one a developer can actually rely on.</p>
<p>That layer doesn't come from the API. It comes from someone whose job is to read the spec the way a developer would encounter it for the first time, verify its claims against the live API, and fill in what the implementation knows but the documentation doesn't say.</p>
<p>The full before and after specs are available here:</p>
<ul>
<li class=""><a href="https://portal.farowave.com/demos/frankfurter-v1-original.html" target="_blank" rel="noopener noreferrer" class="">Original Frankfurter v1 spec</a></li>
<li class=""><a href="https://portal.farowave.com/demos/frankfurter-v1-improved.html" target="_blank" rel="noopener noreferrer" class="">Improved Frankfurter v1 spec</a></li>
</ul>]]></content>
        <author>
            <name>Faycal Alami-Hassani</name>
            <uri>https://github.com/ka2in</uri>
        </author>
        <category label="openapi" term="openapi"/>
        <category label="api-documentation" term="api-documentation"/>
        <category label="documentation-audit" term="documentation-audit"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Docusaurus vs Sphinx: Choosing the Right Documentation Tool]]></title>
        <id>https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx</id>
        <link href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx"/>
        <updated>2026-04-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[If you've spent time writing technical documentation, you've almost certainly encountered both Sphinx and Docusaurus. They're both widely used, both open source, and both capable of producing polished documentation sites — but they are built on fundamentally different assumptions about who is writing the docs, what the docs need to do, and how much structural control matters.]]></summary>
        <content type="html"><![CDATA[<p>If you've spent time writing technical documentation, you've almost certainly encountered both Sphinx and Docusaurus. They're both widely used, both open source, and both capable of producing polished documentation sites — but they are built on fundamentally different assumptions about who is writing the docs, what the docs need to do, and how much structural control matters.</p>
<p>This article is a practical comparison based on hands-on experience with both tools. The goal isn't to declare a winner, but to give you a clear picture of where each tool excels so you can make an informed choice for your project.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-quick-orientation">A quick orientation<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#a-quick-orientation" class="hash-link" aria-label="Direct link to A quick orientation" title="Direct link to A quick orientation" translate="no">​</a></h2>
<p><strong>Sphinx</strong> was created in 2008 for the Python documentation and has since become the standard for technical documentation in the Python ecosystem and beyond. It uses reStructuredText (RST) as its default markup language and has an enormous extension ecosystem built up over nearly two decades.</p>
<p><strong>Docusaurus</strong> was released by Meta in 2017 and has grown rapidly in the JavaScript and open source communities. It uses Markdown and MDX (Markdown + JSX), and is built on React, which means it leans heavily on the JavaScript toolchain.</p>
<p>That origin story matters. Sphinx was designed by people who needed to document complex software. Docusaurus was designed by people who needed to publish open source project websites quickly. Their feature priorities reflect that difference.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="structured-content-directives-cross-references-and-tables">Structured content: directives, cross-references, and tables<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#structured-content-directives-cross-references-and-tables" class="hash-link" aria-label="Direct link to Structured content: directives, cross-references, and tables" title="Direct link to Structured content: directives, cross-references, and tables" translate="no">​</a></h2>
<p>This is where Sphinx genuinely pulls ahead for technical documentation work.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="cross-references-and-labels">Cross-references and labels<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#cross-references-and-labels" class="hash-link" aria-label="Direct link to Cross-references and labels" title="Direct link to Cross-references and labels" translate="no">​</a></h3>
<p>Sphinx's cross-reference system is label-based, not URL-based. You place a label before any section, figure, or table, and then link to it by name from anywhere in the documentation — even across separate RST files or separate Sphinx projects (via the <code>intersphinx</code> extension). If you rename or restructure a file, valid references keep resolving; broken ones generate build warnings with the exact file and line number.</p>
<p>Here is what a cross-reference looks like in practice. This is taken directly from the <a href="https://github.com/sphinx-doc/sphinx/blob/master/doc/usage/referencing.rst" target="_blank" rel="noopener noreferrer" class="">Sphinx documentation source</a>, which is MIT licensed:</p>
<div class="language-rst codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-rst codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">.. _my-reference-label:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">Section to cross-reference</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">--------------------------</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">This is the text of the section. It refers to the section itself,</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">see :ref:`my-reference-label`.</span><br></div></code></pre></div></div>
<p>The <code>:ref:</code> role generates a link whose display text is automatically pulled from the section title. You don't hard-code link text, which means section renames propagate cleanly. The same mechanism works for figures and tables that carry an explicit caption.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="domain-directives-and-class-documentation">Domain directives and class documentation<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#domain-directives-and-class-documentation" class="hash-link" aria-label="Direct link to Domain directives and class documentation" title="Direct link to Domain directives and class documentation" translate="no">​</a></h3>
<p>Beyond generic labels, Sphinx has typed <em>domains</em> — collections of directives for documenting specific kinds of objects. The Python domain, for instance, lets you describe a class, its methods, and their parameters as structured, linkable objects:</p>
<div class="language-rst codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-rst codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">.. py:class:: Translator(source_lang, target_lang)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   Handles translation between two locales.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :param source_lang: BCP 47 language tag for the source (e.g. ``en-US``).</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :type source_lang: str</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :param target_lang: BCP 47 language tag for the target (e.g. ``ar-SA``).</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :type target_lang: str</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   .. py:method:: translate(text)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      Translate a string from the source language to the target language.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :param text: The string to translate.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :type text: str</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :returns: Translated string.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :rtype: str</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      :raises ValueError: If either language tag is not supported.</span><br></div></code></pre></div></div>
<p>Every method, parameter, and return type becomes an individually linkable item. Elsewhere in the docs, <code>:py:class:</code>Translator`` generates a hyperlink to this definition. Autodoc can generate this entire block automatically from Python docstrings, which is how most large Python projects document their APIs.</p>
<p>Docusaurus has no equivalent. Its documentation model is flat Markdown: headings create anchors, and you link to them with standard Markdown syntax. There are no typed objects, no parameter tables, no method-level cross-references, and no autodoc equivalent for code.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tables-with-layout-control">Tables with layout control<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#tables-with-layout-control" class="hash-link" aria-label="Direct link to Tables with layout control" title="Direct link to Tables with layout control" translate="no">​</a></h3>
<p>RST's <code>list-table</code> directive gives you precise control over column widths, alignment, and headers — things standard Markdown tables cannot express:</p>
<div class="language-rst codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-rst codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">.. list-table:: Supported output formats</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :widths: 20 15 65</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   :header-rows: 1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   * - Format</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - Command</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - Notes</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   * - HTML</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - ``make html``</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - Default; produces a multi-page site</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   * - PDF</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - ``make latexpdf``</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - Requires a LaTeX installation</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">   * - EPUB</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - ``make epub``</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">     - Suitable for e-reader distribution</span><br></div></code></pre></div></div>
<p>The <code>:widths:</code> option sets proportional column widths that hold across all output formats. You can also use <code>csv-table</code> to pull table data from a separate CSV file, which is useful for auto-generated content or data maintained outside the documentation source.</p>
<p>Docusaurus uses standard GitHub-flavored Markdown tables. They work for simple comparisons but offer no column width control, no captions, and no way to reference the table from elsewhere in the docs.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="mdx-and-interactive-react-components">MDX and interactive React components<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#mdx-and-interactive-react-components" class="hash-link" aria-label="Direct link to MDX and interactive React components" title="Direct link to MDX and interactive React components" translate="no">​</a></h2>
<p>This is one area where Docusaurus has no peer.</p>
<p>Because <code>.mdx</code> files are compiled into React components, you can define and use JSX directly inside your documentation. The Docusaurus official documentation itself demonstrates this with a custom <code>Highlight</code> component defined and used inline in a single <code>.mdx</code> file (<a href="https://github.com/facebook/docusaurus/blob/main/website/docs/guides/markdown-features/markdown-features-react.mdx" target="_blank" rel="noopener noreferrer" class="">source</a>, MIT license):</p>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function maybe-class-name" style="color:rgb(80, 250, 123)">Highlight</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token parameter">children</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> color</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token arrow operator">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">span</span><span class="token tag" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag" style="color:rgb(255, 121, 198)">    </span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">style</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)">      </span><span class="token tag script language-javascript literal-property property" style="color:rgb(255, 121, 198)">backgroundColor</span><span class="token tag script language-javascript operator" style="color:rgb(255, 121, 198)">:</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"> color</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)">      </span><span class="token tag script language-javascript literal-property property" style="color:rgb(255, 121, 198)">borderRadius</span><span class="token tag script language-javascript operator" style="color:rgb(255, 121, 198)">:</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"> </span><span class="token tag script language-javascript string" style="color:rgb(255, 121, 198)">'2px'</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)">      </span><span class="token tag script language-javascript literal-property property" style="color:rgb(255, 121, 198)">color</span><span class="token tag script language-javascript operator" style="color:rgb(255, 121, 198)">:</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"> </span><span class="token tag script language-javascript string" style="color:rgb(255, 121, 198)">'#fff'</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)">      </span><span class="token tag script language-javascript literal-property property" style="color:rgb(255, 121, 198)">padding</span><span class="token tag script language-javascript operator" style="color:rgb(255, 121, 198)">:</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"> </span><span class="token tag script language-javascript string" style="color:rgb(255, 121, 198)">'0.2rem'</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token tag script language-javascript" style="color:rgb(255, 121, 198)">    </span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token tag script language-javascript punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain-text">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">children</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain-text"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain-text">  </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">span</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag class-name" style="color:rgb(255, 121, 198)">Highlight</span><span class="token tag" style="color:rgb(255, 121, 198)"> </span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">color</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">#25c2a0</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain-text">Docusaurus green</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag class-name" style="color:rgb(255, 121, 198)">Highlight</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> and</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token string" style="color:rgb(255, 121, 198)">' '</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag class-name" style="color:rgb(255, 121, 198)">Highlight</span><span class="token tag" style="color:rgb(255, 121, 198)"> </span><span class="token tag attr-name" style="color:rgb(241, 250, 140)">color</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(248, 248, 242)">=</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag attr-value" style="color:rgb(255, 121, 198)">#1877F2</span><span class="token tag attr-value punctuation" style="color:rgb(248, 248, 242)">"</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain-text">Facebook blue</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag class-name" style="color:rgb(255, 121, 198)">Highlight</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> are my favorite colors</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token constant" style="color:rgb(189, 147, 249)">I</span><span class="token plain"> can write </span><span class="token operator">**</span><span class="token maybe-class-name">Markdown</span><span class="token operator">**</span><span class="token plain"> alongside my _JSX_</span><span class="token operator">!</span><br></div></code></pre></div></div>
<p>This renders the colored spans inline in the documentation page, alongside normal Markdown text. You can go further with the <code>@docusaurus/theme-live-codeblock</code> plugin, which turns a fenced code block into an editable, live-executing sandbox:</p>
<div class="language-jsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-jsx codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function maybe-class-name" style="color:rgb(80, 250, 123)">Greeting</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token parameter"> name </span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">h2</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain-text">Hello, </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain-text">! Welcome to the docs.</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">h2</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Readers can edit the code directly in the browser and see the output update in real time. This is genuinely useful for component libraries, API clients, and framework documentation where showing a runnable example beats describing one.</p>
<p>Sphinx has no native equivalent of this. You can embed raw HTML, and there are extensions for embedding Jupyter notebooks, but nothing that matches MDX's seamless integration of interactive components into authored content.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="error-reporting-and-debuggability">Error reporting and debuggability<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#error-reporting-and-debuggability" class="hash-link" aria-label="Direct link to Error reporting and debuggability" title="Direct link to Error reporting and debuggability" translate="no">​</a></h2>
<p>This point comes from direct experience, and the evidence is well-documented across both tools' issue trackers.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sphinx-file-and-line-every-time">Sphinx: file and line, every time<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#sphinx-file-and-line-every-time" class="hash-link" aria-label="Direct link to Sphinx: file and line, every time" title="Direct link to Sphinx: file and line, every time" translate="no">​</a></h3>
<p>Sphinx's error messages are specific and actionable. When a build encounters a broken cross-reference, it tells you precisely where:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">Warning, treated as error:</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">/home/user/docs/source/api/translator.rst:6: undefined label: 'configuration-guide'</span><br></div></code></pre></div></div>
<p>You get the full file path and the line number pointing to the offending <code>:ref:</code> role. For syntax errors in RST, the same applies:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">/home/user/docs/source/install.rst:42: ERROR: Unexpected indentation.</span><br></div></code></pre></div></div>
<p>This precision comes from the fact that Sphinx processes RST files directly, as a Python program walking your source tree. The build pipeline has no intermediary compilation layer between the content file and the error report.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docusaurus-errors-at-the-framework-level">Docusaurus: errors at the framework level<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#docusaurus-errors-at-the-framework-level" class="hash-link" aria-label="Direct link to Docusaurus: errors at the framework level" title="Direct link to Docusaurus: errors at the framework level" translate="no">​</a></h3>
<p>Docusaurus builds through webpack and React's server-side rendering pipeline. When something fails, the error typically surfaces inside the framework, not in your content:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">[INFO] [en] Creating an optimized production build...</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[ERROR] Error: Unable to build website for locale en.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  at tryToBuildLocale (.../node_modules/@docusaurus/core/lib/commands/build/build.js:78:15)</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  at async .../node_modules/@docusaurus/core/lib/commands/build/build.js:34:9</span><br></div></code></pre></div></div>
<p>No file name. No line number. No indication of whether the problem is in your Markdown, your MDX JSX, your front matter YAML, or your <code>docusaurus.config.js</code>. This pattern appears repeatedly in Docusaurus's issue tracker and has been raised as a recurring request for improvement. Users report spending significant time narrowing down which file caused the failure — often by binary-search commenting out content.</p>
<p>A simpler version of the same problem: an invalid YAML front matter block produces a generic parsing error that gives no indication of which document is malformed. Sphinx, by contrast, fails with the file name and line number of the invalid directive. The difference in developer experience is significant on larger documentation sets where a single malformed file can halt the entire build.</p>
<p>The root cause is architectural. Docusaurus is a React application that happens to be statically built from content. Its error messages live at the React/webpack compilation level, which is a different abstraction layer from where documentation problems occur.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="versioning">Versioning<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#versioning" class="hash-link" aria-label="Direct link to Versioning" title="Direct link to Versioning" translate="no">​</a></h2>
<p>Both tools support documentation versioning, but the implementation and trade-offs are quite different.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docusaurus-built-in-snapshot-based">Docusaurus: built-in, snapshot-based<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#docusaurus-built-in-snapshot-based" class="hash-link" aria-label="Direct link to Docusaurus: built-in, snapshot-based" title="Direct link to Docusaurus: built-in, snapshot-based" translate="no">​</a></h3>
<p>Docusaurus versioning is one command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">npm run docusaurus docs:version 2.0</span><br></div></code></pre></div></div>
<p>This takes a snapshot of the current <code>docs/</code> folder and stores it in <code>versioned_docs/version-2.0/</code>. It also updates <code>versions.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"2.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><br></div></code></pre></div></div>
<p>A version selector dropdown appears in the navbar automatically, routed as follows:</p>
<table><thead><tr><th>Content location</th><th>URL served</th></tr></thead><tbody><tr><td><code>docs/</code> (current, in-progress)</td><td><code>/docs/next/</code></td></tr><tr><td><code>versioned_docs/version-2.0/</code></td><td><code>/docs/</code> (latest)</td></tr><tr><td><code>versioned_docs/version-1.0/</code></td><td><code>/docs/1.0/</code></td></tr></tbody></table>
<p>The user experience is polished and the setup is minimal. The main limitation is that each version is a full copy of the docs directory. For large documentation sets — thousands of files across many versions — the repository can become very large, which slows down CI clones and makes storage management a manual responsibility.</p>
<p>Docusaurus versioning is also decoupled from git. There is no built-in mechanism to tie a documentation snapshot to a git tag or branch. Some teams build custom scripting to bridge this gap, but it's not provided out of the box.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sphinx-git-native-via-sphinx-multiversion">Sphinx: git-native via sphinx-multiversion<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#sphinx-git-native-via-sphinx-multiversion" class="hash-link" aria-label="Direct link to Sphinx: git-native via sphinx-multiversion" title="Direct link to Sphinx: git-native via sphinx-multiversion" translate="no">​</a></h3>
<p>Sphinx has no built-in versioning. The standard approach is <code>sphinx-multiversion</code>, which builds documentation directly from git branches and tags. A typical <code>conf.py</code> configuration looks like this:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># conf.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">extensions </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'sphinx_multiversion'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Build docs from these git tags (e.g. v1.0.0, v2.1.3)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">smv_tag_whitelist    </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">r'^v\d+\.\d+\.\d+$'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Also build from main and develop branches</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">smv_branch_whitelist </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">r'^(main|develop)$'</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># Only use the remote origin</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">smv_remote_whitelist </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">r'^origin$'</span><br></div></code></pre></div></div>
<p>Building all versions then runs as:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">sphinx-multiversion docs/ _build/html/</span><br></div></code></pre></div></div>
<p>This produces one HTML build per matched git ref, with a version selector that reflects your actual release history. Unlike Docusaurus's folder copies, sphinx-multiversion doesn't duplicate files in the repository — it reads directly from git objects.</p>
<p>The real-world caveat: <code>sphinx-multiversion</code> had its last stable release in 2020, which raises concerns about long-term maintenance. A number of organizations (ScyllaDB, for instance, with their custom Sphinx theme) have built their own wrappers around it for production use. Teams that need reliable multi-version Sphinx documentation often end up hosting separate builds on different URL paths — the model that Read the Docs uses natively — rather than relying on sphinx-multiversion directly.</p>
<p><strong>Bottom line:</strong> Docusaurus versioning wins on ease of setup; Sphinx versioning wins on git integration and storage efficiency, at the cost of additional configuration and ecosystem uncertainty.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="internationalization-and-localization">Internationalization and localization<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#internationalization-and-localization" class="hash-link" aria-label="Direct link to Internationalization and localization" title="Direct link to Internationalization and localization" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sphinx--gettext-the-professional-translators-workflow">Sphinx + gettext: the professional translator's workflow<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#sphinx--gettext-the-professional-translators-workflow" class="hash-link" aria-label="Direct link to Sphinx + gettext: the professional translator's workflow" title="Direct link to Sphinx + gettext: the professional translator's workflow" translate="no">​</a></h3>
<p>The Sphinx + gettext pipeline produces <code>.po</code> files — the industry standard format for translated content, compatible with tools like Poedit, Lokalize, OmegaT, and every major TMS.</p>
<p>Here is a minimal example of what a Sphinx gettext workflow looks like. First, extract strings from your RST source:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">make gettext</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">sphinx-intl update -p _build/gettext -l ar -l fr</span><br></div></code></pre></div></div>
<p>This produces a <code>.po</code> file for each language. For Arabic, <code>locales/ar/LC_MESSAGES/install.po</code> might look like this:</p>
<div class="language-po codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-po codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain"># SOME DESCRIPTIVE TITLE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Copyright (C) 2025, Farowave</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># This file is distributed under the same license as the project.</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#, fuzzy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgid ""</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgstr ""</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">"Content-Type: text/plain; charset=UTF-8\n"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">"Language: ar\n"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#: install.rst:5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgid "Installation"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgstr ""</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#: install.rst:8</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgid ""</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">"Before installing, make sure Python 3.9 or later is available on your "</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">"system."</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgstr ""</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">#: install.rst:12</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgid "Run the following command to install the package:"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">msgstr ""</span><br></div></code></pre></div></div>
<p>A translator opens this file in Poedit, fills in the <code>msgstr</code> fields with Arabic translations, and saves. The <code>:5</code>, <code>:8</code>, <code>:12</code> references tell the translator exactly where in the source document each string appears. Comments, context notes, and plural forms are all natively supported by the PO format.</p>
<p>Once translated, rebuild for Arabic:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">sphinx-build -b html -D language=ar . _build/html/ar</span><br></div></code></pre></div></div>
<p>This workflow maps directly onto professional TMS processes. Translators don't need to be developers or know anything about RST. The format is standardized across the industry, which matters when you're working with external translation vendors.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docusaurus-i18n-filesystem-based-crowdin-oriented">Docusaurus i18n: filesystem-based, Crowdin-oriented<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#docusaurus-i18n-filesystem-based-crowdin-oriented" class="hash-link" aria-label="Direct link to Docusaurus i18n: filesystem-based, Crowdin-oriented" title="Direct link to Docusaurus i18n: filesystem-based, Crowdin-oriented" translate="no">​</a></h3>
<p>Docusaurus uses a different model. After configuring your locales in <code>docusaurus.config.js</code>:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token literal-property property">i18n</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token literal-property property">defaultLocale</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'en'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token literal-property property">locales</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">'en'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'ar'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'fr'</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token literal-property property">localeConfigs</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token literal-property property">ar</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token literal-property property">direction</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'rtl'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><br></div></code></pre></div></div>
<p>You copy your Markdown files into the corresponding locale directory and translate them in place:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">mkdir -p i18n/ar/docusaurus-plugin-content-docs/current</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">cp -r docs/* i18n/ar/docusaurus-plugin-content-docs/current/</span><br></div></code></pre></div></div>
<p>UI strings (navbar labels, footer text, theme labels) are extracted to JSON files via <code>docusaurus write-translations</code>, which produces files like <code>i18n/ar/docusaurus-theme-classic/navbar.json</code>. The RTL layout for Arabic is handled automatically by setting <code>direction: 'rtl'</code> in the locale config — this is genuinely well-implemented.</p>
<p>The approach works and Crowdin integration makes it viable for larger teams. However, it has documented friction points: the file structure and naming conventions are non-obvious, translation key mismatches fail silently (no build error, translations simply don't appear), and the JSON format carries none of the metadata richness of PO files — no source references, no context strings, no plural rules. Teams coming from gettext workflows will find the tooling regressive.</p>
<p><strong>Bottom line:</strong> for localization-heavy projects, particularly those involving Arabic, Hebrew, or Farsi and a professional TMS workflow, Sphinx's gettext pipeline offers more standardized, translator-friendly ergonomics.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="themes">Themes<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#themes" class="hash-link" aria-label="Direct link to Themes" title="Direct link to Themes" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sphinx-pip-installable-wide-variety">Sphinx: pip-installable, wide variety<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#sphinx-pip-installable-wide-variety" class="hash-link" aria-label="Direct link to Sphinx: pip-installable, wide variety" title="Direct link to Sphinx: pip-installable, wide variety" translate="no">​</a></h3>
<p>Sphinx themes are Python packages, installed in one line and activated with a single change to <code>conf.py</code>. The most widely used options in 2025 include:</p>
<ul>
<li class=""><strong>Furo</strong> — minimal, three-column, responsive, clean typography; used by pip, Black, and many Python tools. MIT licensed.</li>
<li class=""><strong>PyData Sphinx Theme</strong> — three-column Bootstrap layout used by NumPy, pandas, Matplotlib, and SciPy. BSD licensed.</li>
<li class=""><strong>Sphinx Book Theme</strong> — friendly, book-like layout suited for longer documentation. BSD licensed.</li>
<li class=""><strong>Read the Docs default</strong> — the classic look familiar from thousands of Python project docs. MIT licensed.</li>
</ul>
<p>The full gallery is browsable at <a href="https://sphinx-themes.org/" target="_blank" rel="noopener noreferrer" class="">sphinx-themes.org</a>, which currently lists over 25 themes. Installing any of them is:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># conf.py</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">html_theme </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'furo'</span><br></div></code></pre></div></div>
<p>No JavaScript, no React, no framework knowledge required.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="docusaurus-one-official-theme-customization-via-react">Docusaurus: one official theme, customization via React<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#docusaurus-one-official-theme-customization-via-react" class="hash-link" aria-label="Direct link to Docusaurus: one official theme, customization via React" title="Direct link to Docusaurus: one official theme, customization via React" translate="no">​</a></h3>
<p>Docusaurus ships with a single official theme, <code>@docusaurus/theme-classic</code>, which is MIT licensed. Customization is done through <em>swizzling</em> — ejecting individual React components and replacing them with your own implementations. Changing the navbar, for example, requires understanding and overriding a React component.</p>
<p>Community themes exist and are browsable on sites like builtatlightspeed.com, but the ecosystem is shallower and most offerings are full site templates rather than drop-in style packages. In practice, Docusaurus sites look similar to each other unless significant React customization work is invested.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-sphinx-feels-more-transparent">Why Sphinx feels more transparent<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#why-sphinx-feels-more-transparent" class="hash-link" aria-label="Direct link to Why Sphinx feels more transparent" title="Direct link to Why Sphinx feels more transparent" translate="no">​</a></h2>
<p>The "black box" impression with Docusaurus is real and has a concrete structural explanation.</p>
<p>Sphinx operates as a document processing pipeline: it reads RST or Markdown files, applies transforms, resolves references, and writes output. The build logic is exposed in <code>conf.py</code> — a plain Python file you can inspect and modify at will. Extensions follow a documented API. When something goes wrong, the failure almost always traces to a specific content or configuration file.</p>
<p>Docusaurus operates as a React application that happens to be statically rendered. Under the hood, it runs webpack, Babel, and React's server-side rendering. Errors surface at the compilation layer, not the content layer. Features like theme swizzling, MDX components, and the plugin system are genuinely powerful, but they require understanding multiple layers of the JavaScript build toolchain that have nothing to do with writing documentation.</p>
<p>This is a real tradeoff. Docusaurus rewards teams who are comfortable with React and the Node.js ecosystem. It becomes more opaque for teams whose primary expertise is in content, Python, or toolchains outside the JavaScript world.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-decision-framework">The decision framework<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#the-decision-framework" class="hash-link" aria-label="Direct link to The decision framework" title="Direct link to The decision framework" translate="no">​</a></h2>
<p>Neither tool is universally better. The right choice depends on your project's context.</p>
<p><strong>Choose Sphinx if:</strong></p>
<ul>
<li class="">Your project is in the Python ecosystem, or your team is more comfortable in Python</li>
<li class="">You need cross-references, typed domains, or auto-generated API documentation from docstrings</li>
<li class="">You work with professional translators and need gettext/PO file workflows</li>
<li class="">You want a wide selection of visually distinct themes without writing React</li>
<li class="">You want clear, file-level error reporting during builds</li>
<li class="">Your documentation is large, deeply structured, or highly cross-referenced</li>
</ul>
<p><strong>Choose Docusaurus if:</strong></p>
<ul>
<li class="">Your team is comfortable with React and the JavaScript ecosystem</li>
<li class="">You want built-in versioning with minimal setup and a polished version selector UI</li>
<li class="">You need to embed interactive React components or live code sandboxes in your documentation</li>
<li class="">Your documentation lives alongside a JavaScript project and you want a unified toolchain</li>
<li class="">You're building an open source project website with blog, docs, and a custom landing page</li>
</ul>
<p>For organizations managing large documentation portfolios, multilingual documentation with professional translation workflows, or enterprise API references that need deep cross-linking, Sphinx's maturity and structural richness often make it the more sustainable long-term choice. Docusaurus excels when developer experience, fast setup, and UI flexibility are the primary drivers — and when there's React expertise on the team to manage its complexity.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-farowave">About Farowave<a href="https://ka2in.github.io/guides/enterprise-documentation/docusaurus-vs-sphinx#about-farowave" class="hash-link" aria-label="Direct link to About Farowave" title="Direct link to About Farowave" translate="no">​</a></h2>
<p>At Farowave, we work with both Sphinx and Docusaurus across our documentation engagements — from enterprise API reference builds and docs-as-code retainers to localization governance and TMS workflow design. If you're evaluating documentation tooling for your organization, or need help structuring a documentation system that scales, <a href="https://farowave.com/" target="_blank" rel="noopener noreferrer" class="">get in touch</a>.</p>]]></content>
        <author>
            <name>Faycal Alami-Hassani</name>
            <uri>https://github.com/ka2in</uri>
        </author>
        <category label="sphinx" term="sphinx"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="documentation" term="documentation"/>
        <category label="docs-as-code" term="docs-as-code"/>
        <category label="technical-writing" term="technical-writing"/>
        <category label="i18n" term="i18n"/>
        <category label="localization" term="localization"/>
        <category label="static-site-generators" term="static-site-generators"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Unmet Dependencies in Docusaurus]]></title>
        <id>https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies</id>
        <link href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies"/>
        <updated>2026-03-26T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[This is the first time that I am facing issues with unmet dependencies in Docusaurus after a Yarn Upgrade.]]></summary>
        <content type="html"><![CDATA[<p>This is the first time that I am facing issues with unmet dependencies in Docusaurus after a Yarn Upgrade.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-docusaurus">What is Docusaurus?<a href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies#what-is-docusaurus" class="hash-link" aria-label="Direct link to What is Docusaurus?" title="Direct link to What is Docusaurus?" translate="no">​</a></h3>
<p><strong>Docusaurus</strong> is an SSG (short for <em>Static Site Generator</em>). Static Site Generators are software frameworks that generate static HTML webpages using templates, components, and plain-text files.</p>
<p>The plain-text files make use of markup languages such as Markdown. Since there are neither databases nor dynamic content involved, the static HTML files load pretty fast.</p>
<p>There are many SSGs on the web to choose from. Docusaurus, Hugo, and Eleventy are just some of the most famous ones among them. It is up to you to select the brand that best suits your needs.</p>
<p>Docusaurus offers some very interesting features, including:</p>
<ul>
<li class="">versioning capabilities</li>
<li class="">contextual and faceted navigation based on a integrated search engine</li>
<li class="">built-in support for the standard file format <a href="https://mdxjs.com/docs/what-is-mdx/" target="_blank" rel="noopener noreferrer" class="">MDX</a>, which allows you to embed the JavaScript extension syntax <a href="https://facebook.github.io/jsx/" target="_blank" rel="noopener noreferrer" class="">JSX</a> into your Markdown</li>
</ul>
<p>These and other features make it a great fit to create beautiful static documentation sites.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-yarn">What is Yarn?<a href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies#what-is-yarn" class="hash-link" aria-label="Direct link to What is Yarn?" title="Direct link to What is Yarn?" translate="no">​</a></h3>
<p><strong>Yarn</strong> is a JavaScript package manager that was developed to address some shortcomings of its predecessor <strong>npm</strong>, the default package manager for Node.js.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="packages-manifests-and-package-managers">Packages, Manifests, and Package Managers<a href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies#packages-manifests-and-package-managers" class="hash-link" aria-label="Direct link to Packages, Manifests, and Package Managers" title="Direct link to Packages, Manifests, and Package Managers" translate="no">​</a></h3>
<p>Developers use packages to share their code. In addition to the actual code, each package contains a <code>manifest</code> file, also called <code>package.json</code>.</p>
<p>The manifest file describes the package itself using a lightweight data-interchange format called <a href="https://www.json.org/json-en.html" target="_blank" rel="noopener noreferrer" class="">JSON</a> (JavaScript Object Notation).</p>
<p>Here is an excerpt of a manifest:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"@anycli/manifest-file"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"description"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"base json file wrapper used inside anycli"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"version"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0.3.9"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"author"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Jeff Dickey @jdxcode"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"bugs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"https://github.com/anycli/manifest-file/issues"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"dependencies"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"cli-ux"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^3.3.13"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"debug"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^3.1.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"fs-extra"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^5.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"load-json-file"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^4.0.0"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"lodash"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^4.17.4"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"proper-lockfile"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"^3.0.2"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Source code available on GitHub under: <a href="https://github.com/oclif/manifest-file/blob/master/package.json" target="_blank" rel="noopener noreferrer" class="">oclif / manifest file</a></p>
<p>A package manager relies on the manifest to determine the properties of each specific package, e.g. name, version, and dependencies.</p>
<p>To avoid any future conflicting packages in your project, you are better advised to select a specific package manager right from the start and stick to it.</p>
<p>If you want to learn more about the differences between yarn and npm, I suggest you read the following article: <a href="https://www.whitesourcesoftware.com/free-developer-tools/blog/npm-vs-yarn-which-should-you-choose/" target="_blank" rel="noopener noreferrer" class="">npm vs. yarn: Which Package Manager Should You Choose?</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="troubleshooting-yarn-upgrade-in-docusaurus">Troubleshooting Yarn Upgrade in Docusaurus<a href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies#troubleshooting-yarn-upgrade-in-docusaurus" class="hash-link" aria-label="Direct link to Troubleshooting Yarn Upgrade in Docusaurus" title="Direct link to Troubleshooting Yarn Upgrade in Docusaurus" translate="no">​</a></h3>
<p>Yesterday, I got a warning message prompting me to upgrade Yarn while I was testing Docusaurus on my local development environment.</p>
<p>After running the upgrade with the following command:</p>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ yarn upgrade @docusaurus/core@latest @docusaurus/preset-classic@latest</span><br></div></code></pre></div></div>
<p>I got several warning messages regarding unmet peer dependencies:</p>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ warning "@docusaurus/core &gt; react-dev-utils &gt; fork-ts-checker-webpack-plugin@6.5.0" has unmet peer dependency "typescript@&gt;= 2.7".</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ warning "@docusaurus/preset-classic &gt; @docusaurus/theme-search-algolia &gt; @docsearch/react@3.0.0" has unmet peer dependency "@types/react@&gt;= 16.8.0 &lt; 18.0.0".</span><br></div></code></pre></div></div>
<p>To resolve the issue, I had to install the latest versions of the following packages inside my project:</p>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ yarn add typescript@2.7.2</span><br></div></code></pre></div></div>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ yarn add "@types/react@&gt;= 16.8.0 &lt; 18.0.0"</span><br></div></code></pre></div></div>
<p>Once the installation has completed, I wanted to make sure that the problem was resolved, so I regenerated the content with the command:</p>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ yarn run build</span><br></div></code></pre></div></div>
<p>Then I used the following command to test my build locally:</p>
<div class="language-console codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-console codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">$ yarn run serve</span><br></div></code></pre></div></div>
<p>That's it! Everything is working fine now ☀️.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-farowave">About Farowave<a href="https://ka2in.github.io/guides/enterprise-documentation/resolve-unmet-dependencies#about-farowave" class="hash-link" aria-label="Direct link to About Farowave" title="Direct link to About Farowave" translate="no">​</a></h2>
<p>At Farowave, we work with both Sphinx and Docusaurus across our documentation engagements — from enterprise API reference builds and docs-as-code retainers to localization governance and TMS workflow design. If you're evaluating documentation tooling for your organization, or need help structuring a documentation system that scales, <a href="https://farowave.com/" target="_blank" rel="noopener noreferrer" class="">get in touch</a>.</p>]]></content>
        <author>
            <name>Faycal Alami-Hassani</name>
            <uri>https://github.com/ka2in</uri>
        </author>
        <category label="docusaurus" term="docusaurus"/>
        <category label="v. 2.0.0-beta.18" term="v. 2.0.0-beta.18"/>
        <category label="yarn" term="yarn"/>
        <category label="npm" term="npm"/>
        <category label="ssg" term="ssg"/>
        <category label="dependencies" term="dependencies"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Why You Shouldn’t Copy Your Python Virtual Environment Folder]]></title>
        <id>https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration</id>
        <link href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration"/>
        <updated>2025-08-16T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[Learn why copying your venv folder breaks your Python setup and how to properly recreate environments for Sphinx documentation projects.]]></summary>
        <content type="html"><![CDATA[<p>As a technical writer working extensively with Python-based documentation tools like <strong>Sphinx</strong>, I rely heavily on virtual environments to isolate dependencies and keep my projects reproducible.</p>
<p>One mistake I made early on—and one I see others make too—is trying to <strong>copy or migrate the entire virtual environment folder</strong> (<code>venv</code>) from one location to another.</p>
<p>It seems convenient, but it’s a trap. Let me walk you through why this doesn’t work reliably, what actually breaks, and how to fix it the right way.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-problem-copying-the-venv-folder-breaks-path-bindings">The Problem: Copying the <code>venv</code> Folder Breaks Path Bindings<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#the-problem-copying-the-venv-folder-breaks-path-bindings" class="hash-link" aria-label="Direct link to the-problem-copying-the-venv-folder-breaks-path-bindings" title="Direct link to the-problem-copying-the-venv-folder-breaks-path-bindings" translate="no">​</a></h2>
<p>When you create a virtual environment using:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">python -m venv venv</span><br></div></code></pre></div></div>
<p>Python generates a folder named <code>venv</code> (or whatever name you choose) that contains:</p>
<ul>
<li class="">The Python interpreter binaries</li>
<li class="">Scripts for activating the environment</li>
<li class="">A <code>pyvenv.cfg</code> file with absolute paths</li>
<li class="">Site-packages and installed dependencies</li>
</ul>
<p>The issue? The <code>pyvenv.cfg</code> file and internal scripts contain <strong>hardcoded absolute paths</strong> to the original Python executable and environment location. If you move the folder to a different directory or machine, those paths become invalid.</p>
<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>More about the creation of virtual environments can be found on the <a href="https://docs.python.org/3/library/venv.html" target="_blank" rel="noopener noreferrer" class="">Python official website</a>.</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-pyvenvcfg-contents">Example: <code>pyvenv.cfg</code> Contents<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#example-pyvenvcfg-contents" class="hash-link" aria-label="Direct link to example-pyvenvcfg-contents" title="Direct link to example-pyvenvcfg-contents" translate="no">​</a></h3>
<div class="language-ini codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ini codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">home = C:\Users\Me\AppData\Local\Programs\Python\Python311</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">include-system-site-packages = false</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">version = 3.11.4</span><br></div></code></pre></div></div>
<p>If you copy the <code>venv</code> folder to a new location, say <code>D:\Projects\NewSite\venv</code>, the activation scripts and interpreter references will still point to the old path. On Windows, this often results in cryptic errors when trying to activate or run Python from the environment.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-hack-rewriting-pyvenvcfg">The Hack: Rewriting <code>pyvenv.cfg</code><a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#the-hack-rewriting-pyvenvcfg" class="hash-link" aria-label="Direct link to the-hack-rewriting-pyvenvcfg" title="Direct link to the-hack-rewriting-pyvenvcfg" translate="no">​</a></h2>
<p>Some developers try to manually edit <code>pyvenv.cfg</code> and update the <code>home</code> path. While this can work in <strong>very limited cases</strong>, it’s not reliable. The activation scripts (<code>activate</code>, <code>activate.bat</code>, <code>activate.ps1</code>) and internal symlinks may still break, especially across operating systems or Python versions.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-right-way-recreate-the-virtual-environment">The Right Way: Recreate the Virtual Environment<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#the-right-way-recreate-the-virtual-environment" class="hash-link" aria-label="Direct link to The Right Way: Recreate the Virtual Environment" title="Direct link to The Right Way: Recreate the Virtual Environment" translate="no">​</a></h2>
<p>Here’s the clean, reproducible workflow I now follow for every Sphinx documentation project:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-create-a-requirementstxt-or-pyprojecttoml">1. Create a <code>requirements.txt</code> or <code>pyproject.toml</code><a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#1-create-a-requirementstxt-or-pyprojecttoml" class="hash-link" aria-label="Direct link to 1-create-a-requirementstxt-or-pyprojecttoml" title="Direct link to 1-create-a-requirementstxt-or-pyprojecttoml" translate="no">​</a></h3>
<p>Before migrating, freeze your dependencies:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">pip freeze &gt; requirements.txt</span><br></div></code></pre></div></div>
<p>Or use a modern tool like <a href="https://python-poetry.org/" target="_blank" rel="noopener noreferrer" class="">Poetry</a> to manage dependencies via <code>pyproject.toml</code>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-delete-the-old-venv-folder">2. Delete the Old <code>venv</code> Folder<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#2-delete-the-old-venv-folder" class="hash-link" aria-label="Direct link to 2-delete-the-old-venv-folder" title="Direct link to 2-delete-the-old-venv-folder" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">rm -r venv  # macOS/Linux</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">rmdir /S /Q venv  # Windows</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-recreate-the-environment-in-the-new-location">3. Recreate the Environment in the New Location<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#3-recreate-the-environment-in-the-new-location" class="hash-link" aria-label="Direct link to 3. Recreate the Environment in the New Location" title="Direct link to 3. Recreate the Environment in the New Location" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">python -m venv venv</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-reinstall-dependencies">4. Reinstall Dependencies<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#4-reinstall-dependencies" class="hash-link" aria-label="Direct link to 4. Reinstall Dependencies" title="Direct link to 4. Reinstall Dependencies" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain"># macOS/Linux</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">source venv/bin/activate</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"># Windows</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">venv\Scripts\activate</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">pip install -r requirements.txt</span><br></div></code></pre></div></div>
<p>Or if using Poetry:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">poetry install</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bonus-tip-use-venv-for-consistency">Bonus Tip: Use <code>.venv</code> for Consistency<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#bonus-tip-use-venv-for-consistency" class="hash-link" aria-label="Direct link to bonus-tip-use-venv-for-consistency" title="Direct link to bonus-tip-use-venv-for-consistency" translate="no">​</a></h2>
<p>Many tools (like Poetry and VS Code) look for a <code>.venv</code> folder in the project root. Naming your environment <code>.venv</code> keeps things tidy and avoids cluttering your workspace.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summary">Summary<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#summary" class="hash-link" aria-label="Direct link to Summary" title="Direct link to Summary" translate="no">​</a></h2>
<table><thead><tr><th>❌ Don't Do This</th><th>✅ Do This Instead</th></tr></thead><tbody><tr><td>Copy/paste <code>venv</code> folder</td><td>Recreate it with <code>python -m venv</code></td></tr><tr><td>Manually edit <code>pyvenv.cfg</code></td><td>Use <code>requirements.txt</code> or <code>pyproject.toml</code></td></tr><tr><td>Assume cross-platform compatibility</td><td>Rebuild per system</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="final-thoughts">Final Thoughts<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#final-thoughts" class="hash-link" aria-label="Direct link to Final Thoughts" title="Direct link to Final Thoughts" translate="no">​</a></h2>
<p>Virtual environments are essential for clean Python workflows, especially in documentation projects where reproducibility matters. But they’re not portable in the way you might expect. Rebuilding them is fast, reliable, and avoids the headaches of broken paths and activation errors.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-farowave">About Farowave<a href="https://ka2in.github.io/guides/enterprise-documentation/python-venv-migration#about-farowave" class="hash-link" aria-label="Direct link to About Farowave" title="Direct link to About Farowave" translate="no">​</a></h2>
<p>At Farowave, we work with both Sphinx and Docusaurus across our documentation engagements — from enterprise API reference builds and docs-as-code retainers to localization governance and TMS workflow design. If you're evaluating documentation tooling for your organization, or need help structuring a documentation system that scales, <a href="https://farowave.com/" target="_blank" rel="noopener noreferrer" class="">get in touch</a>.</p>]]></content>
        <author>
            <name>Faycal Alami-Hassani</name>
            <uri>https://github.com/ka2in</uri>
        </author>
        <category label="python" term="python"/>
        <category label="sphinx" term="sphinx"/>
        <category label="virtual-environments" term="virtual-environments"/>
        <category label="documentation" term="documentation"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Entering Text Mode in Fedora]]></title>
        <id>https://ka2in.github.io/guides/enterprise-documentation/text-mode-fedora</id>
        <link href="https://ka2in.github.io/guides/enterprise-documentation/text-mode-fedora"/>
        <updated>2025-03-08T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[The following blog post is a self-reminder for troubleshooting Linux distros when access to graphical mode is not possible.]]></summary>
        <content type="html"><![CDATA[<div class="theme-admonition theme-admonition-note admonition_xJq3 alert alert--secondary"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"></path></svg></span>note</div><div class="admonitionContent_BuS1"><p>The following blog post is a self-reminder for troubleshooting Linux distros when access to <code>graphical mode</code> is not possible.</p></div></div>
<p>I recently messed up the <a href="https://www.redhat.com/en/topics/linux/what-is-selinux" target="_blank" rel="noopener noreferrer" class="">SELinux configuration</a> on a Fedora distro while trying to relabel the filesystem on boot with this command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">fixfiles -B onboot</span><br></div></code></pre></div></div>
<p>After running the command and restarting the OS, the relabeling process was failing repeatedly, leading my system to an infinite reboot loop.</p>
<p><img decoding="async" loading="lazy" alt="Fixing a messed-up SELinux configuration" src="https://ka2in.github.io/assets/images/printer-eff-9e33133a1137e1befd5eb79b09b08511.jpg" width="400" height="200" class="img_ev3q"><br>
Picture by the <a href="https://www.eff.org/" target="_blank" rel="noopener noreferrer" class="">Electronic Frontier Foundation</a> under <a href="https://creativecommons.org/licenses/by/2.0/" target="_blank" rel="noopener noreferrer" class="">CC BY 2.0</a> license</p>
<p>To fix this issue, I had to boot into <strong>text-only mode</strong> and change the SELinux mode temporarily from <code>Enforced</code> to <code>Disabled</code>.</p>
<p>Here are the steps to boot into text-only mode:</p>
<ol>
<li class="">Restart the system to access the GRUB menu.</li>
<li class="">Select the kernel version that you want to boot into, then press the <code>e</code> key instead of <code>Enter</code> to edit the desired version.</li>
<li class="">Scroll down until you reach the <code>quiet</code> parameter. Next, add a white space and number three <code>3</code> just after the quiet parameter (i.e., quiet 3).
Here is a full example:</li>
</ol>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">kernel /vmlinuz-2.6.9-1.667 ro root=LABEL=/ acpi=on rhgb quiet 3</span><br></div></code></pre></div></div>
<ol start="4">
<li class="">Press <code>Ctrl+X</code> to start</li>
<li class="">The system will boot into the new runlevel this time only.</li>
</ol>
<div class="theme-admonition theme-admonition-info admonition_xJq3 alert alert--info"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 14 16"><path fill-rule="evenodd" d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"></path></svg></span>info</div><div class="admonitionContent_BuS1"><p>A runlevel is a number indicating what "mode" you want the system to boot into. For instance, runlevel 3 is text-only mode, while runlevel 5 refers to graphical mode.</p></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-farowave">About Farowave<a href="https://ka2in.github.io/guides/enterprise-documentation/text-mode-fedora#about-farowave" class="hash-link" aria-label="Direct link to About Farowave" title="Direct link to About Farowave" translate="no">​</a></h2>
<p>At Farowave, we work with both Sphinx and Docusaurus across our documentation engagements — from enterprise API reference builds and docs-as-code retainers to localization governance and TMS workflow design. If you're evaluating documentation tooling for your organization, or need help structuring a documentation system that scales, <a href="https://farowave.com/" target="_blank" rel="noopener noreferrer" class="">get in touch</a>.</p>]]></content>
        <author>
            <name>Faycal Alami-Hassani</name>
            <uri>https://github.com/ka2in</uri>
        </author>
        <category label="blog" term="blog"/>
        <category label="docusaurus" term="docusaurus"/>
        <category label="rtl" term="rtl"/>
        <category label="l10n" term="l10n"/>
    </entry>
</feed>