In modern microservices architectures, documentation often becomes fragmented across dozens of repositories. Each service maintains its own docs, developers struggle to find information, and onboarding new team members becomes a scavenger hunt. I recently tackled this challenge by building a centralized documentation system that automatically aggregates TypeDoc-generated API documentation alongside custom user guides into a single, searchable Docusaurus site.
The Problem with Scattered Documentation
Our microservices ecosystem had grown organically over time. Each repository contained its own technical documentation, but accessing it meant knowing which repo to look in, cloning it locally, and navigating through README files. For new developers, this was overwhelming. For AI training purposes, it was nearly impossible to feed the system a comprehensive view of our architecture.
We needed a solution that would:
- Automatically generate and centralize API documentation from TypeScript codebases
- Include custom user documentation alongside code-level docs
- Require minimal maintenance overhead
- Update automatically as code changed
- Provide a single point of access for all documentation
The Architecture
The solution leverages three key technologies working in concert:
TypeDoc generates comprehensive API documentation from TypeScript source code, complete with interfaces, classes, methods, and inline comments. Each microservice repository uses TypeDoc to create its own documentation snapshot.
GitLab CI handles the entire process through scheduled pipelines. Rather than committing generated documentation to each service repository (which would bloat the repos and create noise in commit histories), we use CI pipelines to push docs to a central location.
Docusaurus serves as the presentation layer, building a fast, static documentation site that aggregates content from all services into a unified, searchable interface. The static site is then deployed using GitLab Pages.
How It Works: The Pipeline Flow
Step 1: Service-Level Documentation Generation
Each microservice repository contains a simple yarn script that generates documentation:
{
"scripts": {
"docs": "typedoc --out docs src/"
}
}
Crucially, the generated documentation is not committed to the service repository. Instead, it exists only temporarily during pipeline execution.
Step 2: Scheduled Documentation Sync
Each service repository has a scheduled GitLab CI pipeline (typically running nightly or on-demand) that executes these steps:
-
Generate: Run
yarn docsto create fresh TypeDoc documentation - Delete and Replace: Remove the service's previous documentation directory entirely and replace it with the newly generated docs. Since the source repository is the source of truth, there's no point in trying to merge or preserve old documentation - we simply overwrite it.
-
Handle Race Conditions: To prevent conflicts when multiple pipelines run simultaneously, the pipeline does a
git pullbefore pushing, with a few seconds interval between attempts if needed - Push: Commit and push the documentation to the main documentation repository in a directory named after the service
- Trigger: The push automatically triggers the main documentation repository's rebuild pipeline
This approach keeps service repositories clean while ensuring documentation stays current without manual intervention.
Step 3: Central Documentation Build
The main documentation repository houses the Docusaurus configuration and custom user documentation. Its pipeline triggers on every commit:
- Docusaurus rebuilds the entire site, incorporating all service-specific TypeDoc documentation
- The static site is deployed to GitLab Pages
- Developers access a single URL for all documentation needs
Why Docusaurus specifically? The answer is beautifully simple: TypeDoc generates documentation that lands directly in a /docs directory, and Docusaurus expects content in a /docs directory. It's a perfect match with zero configuration needed.
Custom Documentation Support
Beyond auto-generated API docs, we extended the system to support hand-written user guides, architectural overviews, and onboarding materials. These live directly in the main documentation repository as standard Markdown files, organized by topic rather than by service.
To make the generated documentation even more useful, I created a custom TypeDoc plugin that preserves the original project structure. Instead of TypeDoc's default behavior of reorganizing files by type (classes, interfaces, etc.), the plugin keeps the directory structure exactly as it exists in the source repository. This means if you have src/auth/services/UserService.ts in your repo, the documentation appears in the same path. Finding specific documentation becomes intuitive-developers already know where files live in the codebase, so they know where to find the docs.
This hybrid approach gives us the best of both worlds: automated technical accuracy from TypeDoc and human-curated explanations where needed.
The Benefits
For AI Training
One of our primary motivations was creating a comprehensive knowledge base for AI-assisted development. By consolidating all documentation into a single repository with consistent formatting, we could easily feed the entire codebase's context to language models. This dramatically improved the quality of AI-generated code suggestions and explanations.
For Developer Onboarding
New team members now have a single bookmark. Instead of asking "which repo has the authentication docs?", they search the documentation site. Onboarding time decreased noticeably as developers could explore the system architecture and API contracts without cloning dozens of repositories.
For Cross-Team Collaboration
When teams need to integrate services or understand dependencies, they no longer need to dive into source code immediately. The centralized docs provide interface contracts, usage examples, and architectural context at a glance.
For Maintenance
Documentation updates happen automatically. When a developer adds JSDoc comments to their code, those comments appear in the central documentation after the next scheduled pipeline run. No manual documentation updates required.
Lessons Learned
Conflicts are simpler than you think: You might worry about merge conflicts when multiple services push documentation simultaneously. The solution is straightforward - just delete the entire previous documentation directory and replace it with the new one. Since the source repository is the source of truth, there's no reason to preserve or merge old documentation. Fresh documentation from the source always wins.
Race conditions need simple handling: When scheduled pipelines run at the same time, they can conflict when pushing to the main repository. We solved this by adding a git pull before each push, with a retry mechanism that waits a few seconds if the push fails. It's not elegant, but it works reliably.
Choose tools that fit together: Docusaurus was the obvious choice because TypeDoc outputs to /docs and Docusaurus reads from /docs. Sometimes the best technical decision is the one that requires the least configuration.
Structure matters for usability: The custom TypeDoc plugin that preserves repository directory structure made a huge difference. Developers don't need to learn a new organization system - the docs are where they expect them to be.
Static sites with GitLab Pages are effortless: Deploying to GitLab Pages means no server management, no hosting costs, and automatic HTTPS. The static site builds in under two minutes and serves instantly.
Conclusion
Building centralized documentation doesn't require complex tooling or expensive platforms. With GitLab CI pipelines, TypeDoc, Docusaurus, and GitLab Pages, we created a system that automatically maintains comprehensive, searchable documentation across our entire microservices architecture.
The investment paid off immediately in faster onboarding, better AI assistance, and reduced developer frustration. The custom TypeDoc plugin that preserves directory structure made the documentation intuitive to navigate. The simple approach to conflicts - delete and replace - eliminated complexity without any downsides. Most importantly, the system maintains itself - documentation stays current without becoming a burden on the development team.
If you're struggling with fragmented documentation across microservices, this approach offers a practical, maintainable solution that scales with your architecture.
Top comments (0)