Don't Dread Documentation
I have two scenarios and we've all experienced one of both of them. Imagine you start a new job, or on a new team, or even take over an existing codebase and you are trying to learn what the heck the app does or what the code is even doing. If only someone had documented...anything! So much time is wasted fumbling through files and code just to figure it out. Hopefully the prior devs had good naming conventions at least. Haven't experienced that? Consider yourself lucky.
However, maybe you got lucky and was able to start a project from scratch. There really isn't anything like it. Then after you spend all those weeks and months getting it done, you're asked to document it. You mean you didn't document your code as you did it? Bummer.
I have lived both scenarios and am actually in the middle of the second one rewriting a mobile app and changing technologies from a NativeScript (gross 🤢) app to pure native with Kotlin/Jetpack Compose and SwiftUI. After living the first scenario most of my career, I never want to experience that again or when I pass the project on have someone else experience it.
In steps a perfectly good use for AI and I don't think I will ever go back to writing documentation myself again. It really didn't take long for me to train Gemini to give me the results I wanted and now it is a simple as me pasting an entire file into the agent to get results. Here is an example based on a component on my website.
Start with a general prompt
Document this code
import type { LinkProps } from 'next/link'
import NextLink from 'next/link'
import type { AnchorHTMLAttributes } from 'react'
export function Link({ href, ...rest }: LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>) {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
if (isInternalLink) {
return <NextLink className="break-words" href={href} {...rest} />
}
if (isAnchorLink) {
return (
<a className="break-words" href={href} {...rest}>
{rest.children}
</a>
)
}
return (
<a className="break-words" target="_blank" rel="noopener noreferrer" href={href} {...rest}>
{rest.children || 'External Link'}
</a>
)
}
Now this provided a good starting point and documented just about every line of code. You could stop here but this is way too much for me. I mean, you should be writing code that is as readable as possible.
import type { LinkProps } from 'next/link'
import NextLink from 'next/link'
import type { AnchorHTMLAttributes } from 'react'
/**
* @typedef {object} LinkComponentProps
* @extends {LinkProps} From 'next/link' for Next.js internal routing properties (e.g., 'href').
* @extends {AnchorHTMLAttributes<HTMLAnchorElement>} Standard HTML anchor tag attributes (e.g., 'target', 'rel', 'className').
*/
export type LinkComponentProps = LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>;
/**
* A custom Link component that intelligently handles internal, anchor, and external links.
*
* - **Internal Links (starts with '/'):** Renders as a Next.js `NextLink` component for client-side routing.
* - **Anchor Links (starts with '#'):** Renders as a standard `<a>` tag for on-page navigation.
* - **External Links (otherwise):** Renders as a standard `<a>` tag, opening in a new tab with
* `target="_blank"` and security attributes (`rel="noopener noreferrer"`).
*
* All link types include the `className="break-words"` utility class.
* External links default to the text "External Link" if no children are provided.
*
* @param {LinkComponentProps} props - The properties for the link component.
* @param {string} props.href - The URL the link points to.
* @param {React.ReactNode} props.children - The content to be rendered inside the link.
* @returns {JSX.Element} The appropriate link component.
*/
export function Link({ href, ...rest }: LinkComponentProps) {
// Determine if the link is internal to the application (starts with /)
const isInternalLink = href && href.startsWith('/')
// Determine if the link is an anchor link within the current page (starts with #)
const isAnchorLink = href && href.startsWith('#')
// Case 1: Internal Link (Next.js routing)
if (isInternalLink) {
// Use NextLink for client-side routing performance
return <NextLink className="break-words" href={href} {...rest} />
}
// Case 2: Anchor Link (on-page navigation)
if (isAnchorLink) {
// Use standard <a> tag
return (
<a className="break-words" href={href} {...rest}>
{rest.children}
</a>
)
}
// Case 3: External Link (open in new tab)
return (
<a
className="break-words"
target="_blank" // Opens link in a new tab
rel="noopener noreferrer" // Security best practices for links opening in a new tab
href={href}
{...rest}
>
{/* Fallback text if no children are provided */}
{rest.children || 'External Link'}
</a>
)
}
Refine your prompt to match your style
Update documentation to only document class and methods
import type { LinkProps } from 'next/link'
import NextLink from 'next/link'
import type { AnchorHTMLAttributes } from 'react'
/**
* @typedef {object} LinkComponentProps
* @extends {LinkProps} Properties for Next.js internal routing (e.g., 'href', 'replace').
* @extends {AnchorHTMLAttributes<HTMLAnchorElement>} Standard HTML anchor tag properties (e.g., 'target', 'rel', 'className', 'children').
*/
export type LinkComponentProps = LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>;
/**
* @function Link
* @description A custom React component that acts as a unified link handler, intelligently rendering
* either a Next.js `NextLink` for internal navigation or a standard `<a>` tag for anchor and external links.
*
* @param {LinkComponentProps} props - The properties for the link component.
* @returns {JSX.Element} The rendered link element (NextLink or <a>).
*
* @method Link
* @description The core logic determines the link type based on the `href` prop:
*
* 1. **Internal Link:** If `href` starts with `/`, it renders a `NextLink` for client-side routing.
* ```javascript
* const isInternalLink = href && href.startsWith('/')
* if (isInternalLink) { return <NextLink ... /> }
* ```
*
* 2. **Anchor Link:** If `href` starts with `#`, it renders a standard `<a>` tag for on-page navigation.
* ```javascript
* const isAnchorLink = href && href.startsWith('#')
* if (isAnchorLink) { return <a ... /> }
* ```
*
* 3. **External Link (Default):** Otherwise, it renders a standard `<a>` tag, forcing it to open in a new tab (`target="_blank"`) and applying security attributes (`rel="noopener noreferrer"`). A fallback text of "External Link" is used if no children are provided.
* ```javascript
* return <a target="_blank" rel="noopener noreferrer" ... />
* ```
*
* **Note:** The `className="break-words"` utility is consistently applied across all link types.
*/
export function Link({ href, ...rest }: LinkComponentProps) {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
if (isInternalLink) {
return <NextLink className="break-words" href={href} {...rest} />
}
if (isAnchorLink) {
return (
<a className="break-words" href={href} {...rest}>
{rest.children}
</a>
)
}
return (
<a className="break-words" target="_blank" rel="noopener noreferrer" href={href} {...rest}>
{rest.children || 'External Link'}
</a>
)
}
Now we are getting there. Again, you could stop here and you would probably have better documentation than most projects you'll ever work on. However, the documentation is still pretty long-winded for my taste.
Time for perfection
Update documentation to provide more shorter, concise documentation
import type { LinkProps } from 'next/link'
import NextLink from 'next/link'
import type { AnchorHTMLAttributes } from 'react'
/**
* Type alias combining Next.js routing props and standard anchor attributes.
*/
export type LinkComponentProps = LinkProps & AnchorHTMLAttributes<HTMLAnchorElement>;
/**
* @function Link
* @description A versatile link component that automatically handles routing based on the `href`.
* It selects the best element for navigation: Next.js `NextLink`, an on-page anchor `<a>`, or an external secure `<a>`.
*
* @param {LinkComponentProps} props - The link properties, including `href` and standard HTML attributes.
* @returns {JSX.Element} The appropriate link element.
*
* @method Link
*
* **Logic Flow:**
* 1. **Internal:** If `href` starts with `/`, uses **`NextLink`** for client-side routing.
* 2. **Anchor:** If `href` starts with `#`, uses a standard **`<a>`** for on-page jump links.
* 3. **External (Default):** Otherwise, uses a secure **`<a>`** with `target="_blank"` and `rel="noopener noreferrer"`.
*
* **Note:** All links include the `break-words` class. External links default to "External Link" text if no children are provided.
*/
export function Link({ href, ...rest }: LinkComponentProps) {
const isInternalLink = href && href.startsWith('/')
const isAnchorLink = href && href.startsWith('#')
if (isInternalLink) {
return <NextLink className="break-words" href={href} {...rest} />
}
if (isAnchorLink) {
return (
<a className="break-words" href={href} {...rest}>
{rest.children}
</a>
)
}
return (
<a className="break-words" target="_blank" rel="noopener noreferrer" href={href} {...rest}>
{rest.children || 'External Link'}
</a>
)
}
Now this...this is what I am looking for and you have now trained you AI agent to understand what type of documentation you are looking to add. You can even do this using the free versions of Gemini or ChatGPT. Just save or pin the conversation. Just for good measure, the next few files I would send it, I would ask to create documentation just like the last file and it works perfectly. After a few more, you should just be able to paste a full file without any direction and it will just spit out what you need.
Obviously with anything AI, it is best to double check its work. I haven't found too many mistakes but you just never know. I can't calculate how much time it has saved me from having to type out documentation and let's be honest, it did a better job than what I probably would have come up with. Plus, how helpful is this going to be for that new hire or the person taking over this app down the road? Invaluable!
—Jason