<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mintlify</title>
    <description>The latest articles on DEV Community by Mintlify (@mintlify).</description>
    <link>https://dev.to/mintlify</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F9824%2F8eec3073-9584-407a-b98f-91bf8fa1b81d.jpg</url>
      <title>DEV Community: Mintlify</title>
      <link>https://dev.to/mintlify</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mintlify"/>
    <language>en</language>
    <item>
      <title>Debugging a mysterious HTTP streaming issue</title>
      <dc:creator>Nick K</dc:creator>
      <pubDate>Tue, 05 Aug 2025 17:52:40 +0000</pubDate>
      <link>https://dev.to/mintlify/debugging-a-mysterious-http-streaming-issue-3o69</link>
      <guid>https://dev.to/mintlify/debugging-a-mysterious-http-streaming-issue-3o69</guid>
      <description>&lt;p&gt;We recently encountered a frustrating issue with HTTP response streaming at Mintlify. Our system uses the AI SDK with the Node stream API to forward streams, and suddenly things stopped working properly. The symptoms were confusing: streaming worked perfectly with cURL and Postman, but failed completely with &lt;code&gt;node-fetch&lt;/code&gt; and browser &lt;code&gt;fetch&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Investigation
&lt;/h2&gt;

&lt;p&gt;Our first hypothesis centered around stream compatibility issues. We suspected the problem might be related to how the AI SDK responds using web streams versus Node streams, particularly around HTTP/2 compliance. Web streams have known issues with HTTP/2, while Node streams are compliant.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cloudflare Worker Bandaid
&lt;/h3&gt;

&lt;p&gt;While investigating, we discovered an odd workaround. We set up a Cloudflare worker as middleware between our server and clients:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Cloudflare Worker that acts as a CORS-enabled streaming proxy&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle preflight CORS requests&lt;/span&gt;
    &lt;span class="c1"&gt;// ....&lt;/span&gt;

    &lt;span class="c1"&gt;// Extract request body for non-GET requests&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Create headers that mimic a real browser request&lt;/span&gt;
    &lt;span class="c1"&gt;// ....&lt;/span&gt;

    &lt;span class="c1"&gt;// Forward the request to the target URL&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;proxyHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// Copy response headers and enable CORS&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;responseHeaders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;responseHeaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Create streaming response to handle large payloads&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;readable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TransformStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;pipeTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;responseHeaders&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This worker simply received the stream and responded with it. Somehow, this completely fixed the issue, which made no sense to us at the time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ruling Out HTTP Versions
&lt;/h3&gt;

&lt;p&gt;We quickly invalidated our HTTP/1 vs HTTP/2 suspicion by using cURL with the &lt;code&gt;--http1.1&lt;/code&gt; flag. Even when making HTTP/1 requests, things streamed properly. Throughout the entire debugging process, cURL consistently worked as expected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Header Stripping Suspicions
&lt;/h3&gt;

&lt;p&gt;One thing that stuck out was that our egress systems (ALB and Cloudflare) were stripping these headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive',
'Content-Encoding': 'none',
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since browsers frequently have issues with these headers, and given that Postman and cURL worked while &lt;code&gt;node-fetch&lt;/code&gt; and browser &lt;code&gt;fetch&lt;/code&gt; didn't, we thought this might be the culprit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning About Dynamic Responses
&lt;/h3&gt;

&lt;p&gt;During this investigation, I learned more about "dynamic" responses and HTTP streaming in general. We explored whether an egress proxy was buffering the entire response to assign a &lt;code&gt;content-length&lt;/code&gt; header, but that wasn't happening. It was valuable to develop better intuition around what HTTP streaming actually means.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Breakthrough
&lt;/h2&gt;

&lt;p&gt;After about 3 hours of making no progress, only Lucas made any headway. His previous Cloudflare experience gave him the intuition that a worker might fix the issue, which led to our temporary solution.&lt;/p&gt;

&lt;p&gt;We implemented the patch fix and went to lunch, but had to deal with another issue: changing the IP to the Cloudflare worker triggered our IP-based rate limiter. After fixing that, we called it a day around 8pm.&lt;/p&gt;

&lt;p&gt;Later that night, Lucas had an epiphany. Thinking about why cURL worked but fetch didn't, he consulted Claude about the differences between these tools. Claude walked him through a crucial insight: when Lucas examined the request headers, he noticed that cURL wasn't sending an &lt;code&gt;Accept-Encoding&lt;/code&gt; header by default, while browsers always do. This missing header was the red flag that led Claude to explain that "cURL doesn't allow compressed responses without the &lt;code&gt;--compressed&lt;/code&gt; flag."&lt;/p&gt;

&lt;p&gt;As Claude pointed out, this behavior difference is key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Browser requests include &lt;code&gt;Accept-Encoding: gzip, deflate, br&lt;/code&gt; by default&lt;/li&gt;
&lt;li&gt;cURL requests omit &lt;code&gt;Accept-Encoding&lt;/code&gt; unless you use the &lt;code&gt;--compressed&lt;/code&gt; flag&lt;/li&gt;
&lt;li&gt;Without the &lt;code&gt;Accept-Encoding&lt;/code&gt; header, servers won't compress the response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bingo. The problem was compression.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Lucas went into the Cloudflare dashboard, disabled compression, and everything worked perfectly. A simple solution to what had been a very annoying problem causing 10+ second latency on requests.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The Embarrassing Realization: A Familiar Problem in Disguise
&lt;/h2&gt;

&lt;p&gt;This was embarrassingly familiar. Denzell and I had actually dealt with this exact issue before at Trieve, fixing it in PR &lt;a href="https://github.com/devflowinc/trieve/pull/2002/files" rel="noopener noreferrer"&gt;#2002: remove compression for chat routes&lt;/a&gt;. The reason we didn't immediately recognize it was what I call "magic obfuscation" - when you've fixed something before but the context makes it unrecognizable.&lt;/p&gt;

&lt;p&gt;In fact, Mayank, another team member at Mintlify who worked with Trieve from the customer side, had discovered a similar streaming issue with this message:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;hey folks! were there any changes to the message apis yesterday? streaming doesn't seem to be working as intended. maybe &lt;a href="https://github.com/devflowinc/trieve/pull/1999/files" rel="noopener noreferrer"&gt;github.com/devflowinc/trieve/pull/1999/files&lt;/a&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The key difference was visibility. Since Trieve is source available, Mayank could look at recent commits and quickly identify the issue. In our current Mintlify case, we had no such visibility into what changed, which made the debugging process exponentially more difficult.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Cloudflare's Magic Becomes a Nightmare
&lt;/h2&gt;

&lt;p&gt;The strangest part is that streaming worked correctly one day and stopped working the next, without any changes on our end. ALB configuration stayed the same, Cloudflare wasn't touched, and none of our code changed. Somehow, Cloudflare had silently enabled compression, and we have no idea why.&lt;/p&gt;

&lt;p&gt;This highlights a fundamental issue with Cloudflare's approach to infrastructure management. While their "intelligent" defaults and automatic optimizations can be helpful for simple use cases, they create serious problems for production systems that require predictable behavior. The fact that a critical setting like compression can change without explicit configuration updates makes debugging nearly impossible.&lt;/p&gt;

&lt;p&gt;In a properly designed infrastructure-as-code environment, this issue would have been &lt;em&gt;immediately&lt;/em&gt; obvious. A git diff would show exactly when compression was enabled, by whom, and why. Instead, we spent hours chasing ghosts because Cloudflare's dashboard-driven configuration model obscures the audit trail of changes.&lt;/p&gt;

&lt;p&gt;This isn't just about compression—it's about the broader pattern of "helpful" automation that removes visibility and control from engineering teams. When debugging production issues, the last thing you want is uncertainty about whether your infrastructure configuration matches what you deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Compression breaks HTTP streaming&lt;/strong&gt; - This is now permanently etched in my brain&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;cURL vs fetch behavior differences&lt;/strong&gt; - cURL doesn't allow compressed responses without &lt;code&gt;--compressed&lt;/code&gt; flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloudflare workers as debugging tools&lt;/strong&gt; - Sometimes adding middleware can help isolate problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source availability matters&lt;/strong&gt; - Having access to change logs makes debugging much easier&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team knowledge sharing&lt;/strong&gt; - Lucas's Cloudflare experience was crucial to finding both the workaround and the real solution&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If I'm ever on a team using Cloudflare again, we'll make sure compression is disabled for any HTTP streaming endpoints from day one.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>ai</category>
    </item>
    <item>
      <title>Breaking down common writing mistakes in documentation</title>
      <dc:creator>Tiffany Chen</dc:creator>
      <pubDate>Fri, 24 Jan 2025 19:16:27 +0000</pubDate>
      <link>https://dev.to/mintlify/breaking-down-common-documentation-mistakes-f87</link>
      <guid>https://dev.to/mintlify/breaking-down-common-documentation-mistakes-f87</guid>
      <description>&lt;p&gt;Good documentation just feels right, while bad docs leave you frustrated—but pinpointing what went wrong can be tough. &lt;/p&gt;

&lt;p&gt;We spoke with &lt;a href="https://www.linkedin.com/in/brodyklapko/" rel="noopener noreferrer"&gt;Brody Klapko&lt;/a&gt;, Technical Writer at &lt;a href="http://dev.stash.gg/" rel="noopener noreferrer"&gt;Stash&lt;/a&gt;, to discuss some common pitfalls that distinguish great technical docs from the rest, and how to avoid them. With over 10 years of experience in the industry, Brody's background includes roles as an early technical writing hire at Uber and Stripe, and more recently at Stash.&lt;/p&gt;

&lt;p&gt;Here are common pitfalls that impact your docs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mixing content types&lt;/li&gt;
&lt;li&gt;Unclear audience&lt;/li&gt;
&lt;li&gt;Fundamental quality issues like spelling errors&lt;/li&gt;
&lt;li&gt;Using docs as a band-aid for product&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dig into each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clarify purpose with the Diataxis framework
&lt;/h2&gt;

&lt;p&gt;Mixing content types is a hidden killer. It’s when you find a lengthy paragraph in an API reference guide, or stumble upon a complex diagram when you're just trying to set up an SDK.&lt;/p&gt;

&lt;p&gt;Documentation needs to be organized around the specific goal you're trying to help the user achieve. If they're looking to complete a task, long paragraphs of context just slow them down.&lt;/p&gt;

&lt;p&gt;To focus your content, leverage the &lt;a href="https://diataxis.fr" rel="noopener noreferrer"&gt;Diátaxis framework&lt;/a&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  What is the Diataxis framework?
&lt;/h3&gt;

&lt;p&gt;The Diátaxis framework categorizes documentation into four distinct types, helping you align content with user needs and goals. &lt;/p&gt;

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

&lt;h3&gt;
  
  
  Why and how to apply the framework
&lt;/h3&gt;

&lt;p&gt;Defining content types helps you plan documentation with a clear purpose and makes it easier for users to find what they need. &lt;/p&gt;

&lt;p&gt;Start by assigning each page a specific type—tutorial, how-to guide, explanation, or reference—before writing. This clarity ensures that your content matches your audience's goals. For multi-product companies, consider organizing by product at the top level and by content type within each product. &lt;/p&gt;

&lt;p&gt;A key principle of the framework is learning through practice. You don’t need to fully understand it to start using it—try applying an idea that fits your current work, see how it works, and adjust as needed. &lt;/p&gt;

&lt;h2&gt;
  
  
  Always know who you’re writing for
&lt;/h2&gt;

&lt;p&gt;Similar to defining content types, you should always know the intended audience of your documentation.  Trying to write a single set of docs for multiple audiences—like technical and non-technical users—often leads to compromises that don’t fully satisfy either group.&lt;/p&gt;

&lt;p&gt;Identify your top-priority audience and use that to guide your writing. For example, if your primary audience is developers, you might prioritize detailed API references and code samples over step-by-step guides. Conversely, if your audience includes less technical users, you might focus on high-level overviews and visual aids to convey concepts clearly. &lt;/p&gt;

&lt;p&gt;Communicating these priorities internally is equally important. When everyone understands which audience is prioritized, it reduces friction in decision-making. Audiences can also shift as the product evolves, especially at startups, so it’s important to regularly revisit and align on who your primary users are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attention to quality pays off
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Spelcheck please&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Grammatical and spelling errors may seem small, but they’re the biggest indicator of quality. Mistakes can undermine trust, leaving readers with doubts about both your documentation and the product. &lt;/p&gt;

&lt;p&gt;Also, avoid exclamation points. While not grammatically wrong, they can undermine professionalism and introduce unintended emotion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use screenshots sparingly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While screenshots can be helpful, use them sparingly—they quickly become outdated and erode user trust. Limit screenshots to showing users the starting point of a flow, letting your product UI guide the rest. &lt;/p&gt;

&lt;p&gt;In some cases, demos with tools like &lt;a href="https://www.arcade.software/" rel="noopener noreferrer"&gt;Arcade&lt;/a&gt; are more effective. They provide clearer guidance by showing the GUI in action and offering context that static screenshots can't. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design really matters&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While readability is key, the overall look and feel can significantly impact user experience. &lt;/p&gt;

&lt;p&gt;A clean sidebar, readable color schemes, and a consistent layout make a world of difference in user experience. If the design feels cluttered or hard to read, even the best content will lose its impact. &lt;/p&gt;

&lt;p&gt;Beautiful documentation not only improves usability but also instills confidence in your docs and brand. &lt;/p&gt;

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

&lt;h2&gt;
  
  
  Docs are an accelerant, not a crutch
&lt;/h2&gt;

&lt;p&gt;While great docs can help users navigate complexity, they aren’t a substitute for thoughtful design. If a workflow in your GUI takes 13 steps to complete, your docs can’t reduce that to three—they can only explain the process as it exists. In fact, documentation often highlights product complexity, exposing shortcomings in a very public way.&lt;/p&gt;

&lt;p&gt;Over-reliance on documentation to “fix” a product can lead to frustration for both users and the teams tasked with maintaining the docs. That’s also why it’s crucial to align expectations internally with product &amp;amp; engineering—being on the same page about what documentation can and cannot solve helps set realistic goals and ultimately provide a better customer experience. &lt;/p&gt;

&lt;p&gt;Treat documentation as a complement to a well-designed product, not a patch for its challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure success with qualitative and quantitative feedback
&lt;/h2&gt;

&lt;p&gt;Once you've worked to avoid common documentation mistakes, you'll want to measure your success. However, success depends on the context of your product and docs.&lt;/p&gt;

&lt;p&gt;People typically track metrics like support tickets, page views, or time on page, but it might not paint the full picture. More page views may indicate users are running into errors and seeking help. Similarly, longer time on page might suggest users are struggling to find answers. &lt;/p&gt;

&lt;p&gt;To assess your documentation's effectiveness, here are some questions to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where are users getting stuck? Is it an issue with the docs or the product?&lt;/li&gt;
&lt;li&gt;How long does it take users to go from registering to making their first API call in test or prod?&lt;/li&gt;
&lt;li&gt;What are users searching for in your docs? Do relevant pages exist for these searches?&lt;/li&gt;
&lt;li&gt;What do users want more of in your docs?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To track documentation success, balance quantitative data with qualitative feedback. Collect input from users via feedback tools or customer-facing teams to guide future improvements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Never forget, docs sell your product
&lt;/h2&gt;

&lt;p&gt;Docs play a vital role in sales, adding credibility to your product and influencing decisions. &lt;/p&gt;

&lt;p&gt;Even though technical docs aren’t written like sales materials, they’re often a key touchpoint for prospective users. Strong documentation builds trust and supports sales and marketing by allowing users to explore what’s possible. Go-to-market and documentation teams should collaborate and review how documentation can better support your company’s growth.&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>writing</category>
      <category>devrel</category>
    </item>
  </channel>
</rss>
