Monolithic vs. Modular Deployment
📋 Table of Contents
- 📖 Overview
- 🏗️ Monolithic vs. Modular Deployment
- 🔧 Modular Architecture Strategies
- 📊 Architecture Comparison Matrix
- 🗺️ Implementation Roadmap
- ⚙️ Technical Implementation Details
- ⚖️ Benefits and Trade-offs Analysis
- 💡 Recommendations
- 📖 References and Further Reading
- 📊 Appendix A: Detailed Analysis
📖 Overview
This document focuses specifically on deployment architecture strategies for Quarto sites, exploring how to move from monolithic deployment to modular approaches where static site infrastructure and content pages can be deployed independently.
Note: For understanding how Quarto’s core architecture and rendering works, see 001.001 Architecture - How quarto works.md.
Key Questions Addressed:
- Can individual pages be built independently from the static site logic?
- How can content and navigation be deployed separately?
- What are the performance implications of different deployment approaches?
- When should you consider modular vs. monolithic deployment strategies?
Real-World Context:
Based on analysis of the Learn repository implementation, this document provides practical strategies for scaling Quarto sites while maintaining the benefits of static generation.
🏗️ Monolithic vs. Modular Deployment
Default Monolithic Approach
Quarto’s default deployment follows a monolithic pattern where all components deploy together:
graph TD
A[Source Content] --> B[_quarto.yml Config]
B --> C[Pre-render Hooks]
C --> D[Navigation Generation]
D --> E[Static HTML Generation]
E --> F[Complete Site Bundle]
F --> G[Single Deployment]
A1[Page 1.md] --> A
A2[Page 2.md] --> A
A3[Page N.md] --> A
F --> G1[All HTML Pages]
F --> G2[All Navigation]
F --> G3[All Assets]
Monolithic Characteristics:
- Atomic Builds: deployment is slower, all pages are rendered at each deployment
- Consistent State: Navigation always matches available content
- Cross-References: Pages can reference each other during build
- Single Deployment Unit: Entire site deploys as one package
Example from Learn Repository:
# _quarto.yml - Everything builds together
project:
type: website
output-dir: docs
pre-render:
- powershell -ExecutionPolicy Bypass -File scripts/generate-navigation.ps1
render:
- "*.qmd"
- "*.md"
- "**/README.md" # All pages must exist during buildWhen Sites Grow: Modular Alternatives
As Quarto sites grow in size and complexity, the monolithic approach can become a bottleneck. For large sites, consider modular deployment strategies:
Key Modular Concepts:
- Separate Navigation from Content: Build the site shell (navigation, layout, search) independently from content pages
- Split Content Rendering: Organize content into logical blocks that can be built and deployed separately
- Individual Page Rendering: In extreme cases, render and deploy individual pages independently
When to Consider Modular Approaches:
- Build times consistently exceed 10-15 minutes
- Multiple teams contributing content simultaneously
- High-frequency updates (multiple times per day)
- Large content volume (500+ pages)
📊 See Appendix A: Detailed Analysis for comprehensive metrics, decision frameworks, and implementation strategies.
Development Workflow Impact
Current Workflow:
# Typical development cycle
git pull origin main
# Edit content file
quarto render # Full site rebuild (2-5 minutes)
git add .
git commit -m "Update content"
git push origin main # Triggers GitHub Actions (3-5 minutes)Team Collaboration Issues:
- Queue Conflicts: Multiple developers triggering builds
- Feedback Delay: Long cycles reduce iteration speed
- Resource Contention: GitHub Actions runner limitations
🔧 Modular Architecture Strategies
Strategy 2: Micro-Frontend Architecture
Concept: Deploy individual pages as independent micro-sites with shared navigation.
# content/brk101/_quarto.yml - Standalone page configuration
project:
type: website
output-dir: ../../deploy/pages/brk101
website:
title: "BRK101: .NET Modernization"
navbar: false # No local navigation
sidebar: false # No local sidebar
format:
html:
theme: cosmo
template-partials:
- ../../shared/navigation.html
standalone: false # Allows shared resources
embed-resources: falseClient-Side Composition:
// Micro-frontend loader
class MicroFrontendLoader {
async loadPage(pageId) {
const [navigation, content] = await Promise.all([
this.loadNavigation(),
this.loadPageContent(pageId)
]);
this.renderShell(navigation);
this.renderContent(content);
}
async loadNavigation() {
// Cache navigation for 1 hour
const cached = this.getFromCache('navigation', 3600000);
if (cached) return cached;
const nav = await fetch('/api/navigation').then(r => r.json());
this.setCache('navigation', nav);
return nav;
}
}Deployment Pattern:
Main Site: https://darioairoldi.github.io/Learn/
Individual: https://darioairoldi.github.io/Learn/pages/brk101.html
https://darioairoldi.github.io/Learn/pages/azure-naming.html
Strategy 3: Headless Content with Dynamic Shell
Concept: Generate content-only pages and compose with dynamic navigation at runtime.
# Headless content configuration
format:
html:
theme: none
template: headless-content.html
page-layout: article
embed-resources: false<!-- headless-content.html template -->
<article class="content-only" data-page-id="{{page-id}}">
<header>
<h1>{{title}}</h1>
<meta name="description" content="{{description}}">
</header>
<main>
{{content}}
</main>
<footer>
<meta name="last-modified" content="{{date-modified}}">
</footer>
</article>Dynamic Shell Integration:
// Shell composition at runtime
async function composePage() {
const shell = await fetch('/shell/template.html');
const content = await fetch(window.location.pathname + '?content-only=true');
const navigation = await fetch('/api/navigation.json');
document.body.innerHTML = await this.compose(shell, content, navigation);
}Strategy 4: Hybrid Build Pipeline
Concept: Combine benefits of static generation with selective rebuilding.
# Hybrid pipeline configuration
project:
type: hybrid
output-dir: deploy/hybrid
# Content builds (independent)
content-pipelines:
- name: "build-2025"
source: "202506 Build 2025/**/*.md"
output: "content/build-2025/"
triggers:
- "202506 Build 2025/**"
- name: "azure-topics"
source: "2025*Azure*/**/*.md"
output: "content/azure/"
triggers:
- "2025*Azure*/**"
# Navigation builds (centralized)
navigation-pipeline:
source: "_quarto.yml"
output: "shell/"
triggers:
- "_quarto.yml"
- "navigation/**"
# Composition (automatic)
compose-triggers:
- content-pipelines
- navigation-pipeline📊 Architecture Comparison Matrix
| Aspect | Monolithic (Current) | Content-Navigation Split | Micro-Frontend | Headless + Dynamic | Hybrid Pipeline |
|---|---|---|---|---|---|
| Build Speed | ?? | ???? | ????? | ??? | ???? |
| Deployment Speed | ?? | ???? | ????? | ???? | ???? |
| Complexity | ????? | ??? | ?? | ?? | ??? |
| Consistency | ????? | ???? | ??? | ??? | ???? |
| SEO Performance | ????? | ????? | ??? | ?? | ???? |
| Development DX | ?? | ???? | ????? | ??? | ???? |
| Maintenance | ????? | ??? | ?? | ?? | ??? |
| Rollback Safety | ??? | ????? | ????? | ???? | ???? |
🗺️ Implementation Roadmap
Phase 1: Assessment and Planning
Duration: 1-2 weeks
Objectives:
- Analyze current content update patterns
- Identify high-frequency vs. low-frequency content
- Map team workflows and pain points
- Define success metrics
Deliverables:
assessment-report.md
??? Current performance baseline
??? Content categorization matrix
??? Team workflow analysis
??? Technical debt assessment
??? ROI projections for each strategy
Implementation Steps:
- Performance Baseline: Measure current build times across different scenarios
- Content Analysis: Categorize content by update frequency and dependencies
- Team Survey: Gather feedback on current development workflow pain points
- Technical Assessment: Evaluate infrastructure requirements for each strategy
Phase 2: Proof of Concept
Duration: 2-3 weeks
Objectives:
- Implement minimal viable version of chosen strategy
- Validate technical assumptions
- Measure performance improvements
- Identify integration challenges
Recommended POC: Content-Navigation Separation (Strategy 1)
POC Implementation:
# POC directory structure
Learn-POC/
??? original/ # Current monolithic setup (baseline)
??? modular/ # New modular approach
? ??? content/
? ? ??? build-2025/ # Single content section for testing
? ??? navigation/ # Shell infrastructure
? ??? deploy/ # Composition layer
??? comparison/ # Performance metrics and analysis
Success Criteria:
- Build Time Reduction: >50% for content-only changes
- Deploy Time Improvement: >40% for individual content updates
- Maintained Functionality: All current features work correctly
- Team Adoption: Positive feedback from development team
Phase 3: Production Implementation
Duration: 3-4 weeks
Objectives:
- Migrate production workload to new architecture
- Implement monitoring and alerting
- Train team on new workflows
- Establish rollback procedures
Migration Strategy:
graph LR
A[Current Monolithic] --> B[Hybrid Coexistence]
B --> C[Gradual Migration]
C --> D[Full Modular]
B1[Keep existing for critical pages]
B2[Migrate low-risk content first]
B3[Validate performance]
B --> B1
B --> B2
B --> B3
⚙️ Technical Implementation Details
Content-Only Build Configuration
# content/build-2025/_quarto.yml
project:
type: website
output-dir: ../../deploy/content/build-2025
# Minimal render scope
render:
- "*.md"
- "**/*.md"
# Simplified format - no navigation overhead
format:
html:
theme: cosmo
template-partials:
- ../../shared/content-template.html
toc: true
embed-resources: false
minimal-dependencies: true
# Content-specific metadata
website:
title: "Build 2025 Sessions"
description: "Microsoft Build 2025 conference session notes"
# No navigation components
navbar: false
sidebar: false
# Content-only features
search: false # Handled by shell
reader-mode: trueDeployment Orchestration
# orchestration/deploy-modular.ps1
param(
[string]$ContentScope = "all", # "all", "build-2025", "azure-topics"
[switch]$NavigationOnly,
[switch]$DryRun
)
function Deploy-ModularSite {
Write-Host "?? Starting modular deployment..."
# Step 1: Determine what needs rebuilding
$changes = Get-ChangedContent -Scope $ContentScope
Write-Host "?? Changes detected: $($changes.Count) sections"
# Step 2: Build content sections (parallel)
if ($changes.Content) {
Write-Host "?? Building content sections..."
$contentJobs = @()
foreach ($section in $changes.Content) {
$job = Start-Job -ScriptBlock {
param($sectionPath)
Set-Location $sectionPath
quarto render --quiet
} -ArgumentList $section.Path
$contentJobs += $job
}
# Wait for content builds (with timeout)
$contentJobs | Wait-Job -Timeout 300 | Receive-Job
}
# Step 3: Build navigation shell (if needed)
if ($changes.Navigation -or $NavigationOnly) {
Write-Host "?? Building navigation shell..."
Set-Location "navigation"
quarto render --quiet
# Generate API endpoints
& ".\generate-api-endpoints.ps1"
}
# Step 4: Compose final deployment
Write-Host "?? Composing final site..."
& ".\compose-deployment.ps1" -ContentSections $changes.Content
# Step 5: Deploy to target
if (-not $DryRun) {
Write-Host "?? Deploying to GitHub Pages..."
& ".\deploy-to-github-pages.ps1"
}
Write-Host "? Modular deployment completed successfully"
}
function Get-ChangedContent {
param([string]$Scope)
# Smart change detection based on git diff
$gitChanges = git diff --name-only HEAD~1 HEAD
$changes = @{
Content = @()
Navigation = $false
}
foreach ($file in $gitChanges) {
if ($file -match "^content/([^/]+)/") {
$section = $Matches[1]
if ($Scope -eq "all" -or $Scope -eq $section) {
$changes.Content += @{ Name = $section; Path = "content/$section" }
}
}
elseif ($file -match "^(navigation/|_quarto\.yml)") {
$changes.Navigation = $true
}
}
return $changes
}
# Execute deployment
Deploy-ModularSite -ContentScope $ContentScope -NavigationOnly:$NavigationOnly -DryRun:$DryRun⚖️ Benefits and Trade-offs Analysis
Benefits of Modular Architecture
Performance Improvements:
- ? Faster content updates: 30-60 seconds vs. 2-5 minutes
- ? Parallel builds: Multiple content sections build simultaneously
- ? Selective deployment: Only changed content gets deployed
- ? Cached navigation: Shell infrastructure cached longer
Development Experience:
- ? Faster iteration: Immediate feedback on content changes
- ? Team independence: Content teams don’t block each other
- ? Reduced conflicts: Separate build pipelines reduce merge conflicts
- ? Granular rollbacks: Rollback individual content sections safely
Operational Benefits:
- ? Resource efficiency: Smaller builds use fewer resources
- ? Better monitoring: Track performance per content section
- ? Easier debugging: Issues isolated to specific components
- ? Scalable growth: Architecture grows with content volume
Trade-offs and Challenges
Increased Complexity:
- ?? Multiple build pipelines to maintain and monitor
- ?? Coordination overhead between content and navigation teams
- ?? Testing complexity for integration scenarios
- ?? Deployment orchestration requires sophisticated tooling
Consistency Challenges:
- ?? Cross-reference validation becomes more complex
- ?? Navigation sync between content updates and menu structure
- ?? Version mismatches between shell and content components
- ?? Asset management across multiple build outputs
Infrastructure Requirements:
- ?? Additional storage for intermediate build artifacts
- ?? More CI/CD complexity with multiple pipelines
- ?? Monitoring overhead for multiple deployment targets
- ?? Backup and recovery procedures become more complex
💡 Recommendations
For the Learn Repository Implementation
Based on the current setup and content patterns, here are the specific recommendations:
Recommendation 1: Stay with Enhanced Monolithic (Immediate - 0-1 weeks)
Why: The current setup already implements smart optimizations that provide most benefits without complexity.
Optimizations to Add:
# Enhanced _quarto.yml
project:
type: website
output-dir: docs
# Smart pre-render with caching (already implemented)
pre-render:
- powershell -ExecutionPolicy Bypass -File scripts/generate-navigation.ps1
# Add selective rendering for development
profiles:
development:
format:
html:
minimal: true
embed-resources: false
render:
- "working-draft/**/*.md" # Only render what you're working on
production:
format:
html:
embed-resources: true
minimal: false
render:
- "*.qmd"
- "**/*.md"Immediate Benefits:
- ? Zero migration risk
- ? Faster development cycles with selective rendering
- ? Enhanced caching with existing PowerShell approach
- ? Better resource utilization with development profiles
Recommendation 2: Hybrid Transition (Medium-term - 2-3 months)
Why: Gradual migration reduces risk while providing benefits for high-change content.
Implementation Path:
- Phase 1: Separate high-change content (Build 2025 sessions)
- Phase 2: Migrate content creation workflows
- Phase 3: Implement full modular architecture for new content
- Phase 4: Migrate remaining content sections
Migration Strategy:
# Gradual migration approach
Learn/
??? legacy/ # Current monolithic (stable content)
? ??? _quarto.yml
? ??? [existing stable content]
??? modular/ # New modular approach (active content)
? ??? content/
? ? ??? build-2025/ # High-frequency updates
? ? ??? current-topics/
? ??? navigation/
? ??? deploy/
??? orchestration/ # Build coordination
??? hybrid-build.ps1
Recommendation 3: Advanced Implementation (Long-term - 6+ months)
When to Consider:
- Content volume exceeds 200+ pages
- Multiple teams contributing content
- Update frequency exceeds daily changes
- Build times consistently exceed 10+ minutes
Target Architecture: Strategy 4: Hybrid Build Pipeline
Why This Approach:
- ? Best balance of performance and complexity
- ? Proven patterns from enterprise content management
- ? Scalable growth path for future expansion
- ? Maintains SEO and static site benefits
Decision Framework
Use this framework to determine the right approach:
graph TD
A[Current Build Time] --> B{> 10 minutes?}
B -->|No| C[Stay Monolithic + Optimizations]
B -->|Yes| D[Team Size]
D --> E{Multiple Teams?}
E -->|No| F[Content-Navigation Split]
E -->|Yes| G[Content Volume]
G --> H{> 500 pages?}
H -->|No| I[Hybrid Pipeline]
H -->|Yes| J[Micro-Frontend Architecture]
C --> C1[? Simple maintenance]
F --> F1[? Faster builds]
I --> I1[? Scalable + SEO]
J --> J1[? Maximum performance]
📖 References and Further Reading
Modular Architecture Patterns
- Micro-Frontend Architecture: Principles of modular frontend development
- JAMstack Deployment Strategies: Static site deployment optimization
- Static Site Generation Patterns: Modern web architecture patterns
DevOps and CI/CD
- GitHub Actions Best Practices: CI/CD optimization strategies
- PowerShell for DevOps: Advanced scripting patterns
- Multi-Environment Deployments: Environment-specific deployment strategies
Performance and Monitoring
- Static Site Performance: Core web vitals optimization
- CDN and Caching Strategies: Global content delivery optimization
- Build Performance Monitoring: CI/CD performance tracking
Case Studies and Examples
- GitLab Documentation Architecture: Enterprise documentation workflows
- Kubernetes Documentation System: Large-scale collaborative documentation
- Docusaurus Deployment Strategies: Static site deployment patterns
Implementation Tools
- yq YAML Processing: YAML manipulation and data extraction
- GitHub Actions Workflows: Automated deployment pipelines
- Docker for Build Environments: Containerized build processes
📊 Appendix A: Detailed Analysis
This appendix provides comprehensive technical details for teams considering the transition from monolithic to modular deployment strategies.
A.1 Performance and Scalability Metrics
Current Performance Profile (based on Learn repository):
- Small Changes: 2-3 minutes for navigation updates
- Content Updates: 30-60 seconds for single page changes
- Full Rebuild: 5-10 minutes for complete site
- Pre-render Hook: 10-30 seconds for navigation.json generation
Smart Optimization in Current Setup:
# From scripts/generate-navigation.ps1
if ($quartoModified -gt $navModified) {
Write-Host "navigation.json is older than _quarto.yml - will regenerate"
$shouldGenerate = $true
} else {
Write-Host "navigation.json is up to date - skipping generation"
$shouldGenerate = $false
}Scalability Bottlenecks:
- Linear Growth: Build time increases with content volume
- All-or-Nothing: Small changes trigger complete rebuilds
- Memory Usage: All content loaded during build process
- Developer Workflow: Waiting for complete builds during development
Projected Growth Impact:
| Content Volume | Build Time | Developer Impact |
|---|---|---|
| Current (~50 pages) | 2-5 minutes | Acceptable |
| Medium (~200 pages) | 8-15 minutes | Frustrating |
| Large (~500 pages) | 20-45 minutes | Blocking |
A.2 Decision Framework
Detailed Indicators for Modular Deployment:
| Metric | Monolithic OK | Consider Modular | Requires Modular |
|---|---|---|---|
| Content Volume | < 100 pages | 100-500 pages | > 500 pages |
| Build Time | < 5 minutes | 5-15 minutes | > 15 minutes |
| Team Size | 1-2 people | 3-5 people | > 5 people |
| Update Frequency | Weekly | Daily | Multiple/day |
| Content Types | Homogeneous | Mixed | Highly varied |
Deployment Coupling Analysis:
| Component | Dependency | Impact |
|---|---|---|
| Individual Pages | Complete site structure | Cannot deploy single page |
| Navigation Menu | All content files | Menu changes require full rebuild |
| Cross-References | Target page existence | Broken if target not built |
| Related Pages | Navigation.json | Dynamic features need complete data |
A.3 Modular Architecture Strategies
Strategy 1: Content-Navigation Separation
Split content generation from navigation/shell infrastructure:
Current Monolithic:
┌─────────────────────────────┐
│ Monolithic Build │
│ ┌─────────────────────────┐ │
│ │Content │ Nav │ Shell │ │
│ │ Pages │ Menu │ Layout │ │
│ └─────────────────────────┘ │
└─────────────────────────────┘
Separated Architecture:
┌───────┐ ┌───────┐ ┌───────┐
│ Content │ │ Navigation │ │ Shell │
│ Build │ │ Build │ │ Build │
│ │ │ │ │ │
└───────┘ └───────┘ └───────┘
│ │ │
└────────────────┼────────────────┘
│
┌───────┐
│ Deployment │
│ Composition │
└───────┘
Strategy 2: Micro-Frontend Architecture
Deploy individual pages as independent micro-sites with shared navigation.
Strategy 3: Headless Content with Dynamic Shell
Generate content-only pages and compose with dynamic navigation at runtime.
Strategy 4: Hybrid Build Pipeline
Combine benefits of static generation with selective rebuilding.
A.4 Implementation Roadmap
Phase 1: Assessment (1-2 weeks)
- Analyze current content update patterns
- Identify high-frequency vs. low-frequency content
- Map team workflows and pain points
- Define success metrics
Phase 2: Proof of Concept (2-3 weeks)
- Implement minimal viable version of chosen strategy
- Validate technical assumptions
- Measure performance improvements
- Identify integration challenges
Phase 3: Production Implementation (3-4 weeks)
- Migrate production workload to new architecture
- Implement monitoring and alerting
- Train team on new workflows
- Establish rollback procedures
A.5 Benefits and Trade-offs
Benefits of Modular Architecture:
- ✅ Faster content updates: 30-60 seconds vs. 2-5 minutes
- ✅ Parallel builds: Multiple content sections build simultaneously
- ✅ Selective deployment: Only changed content gets deployed
- ✅ Team independence: Content teams don’t block each other
Trade-offs and Challenges:
- ⚠️ Multiple build pipelines to maintain and monitor
- ⚠️ Coordination overhead between content and navigation teams
- ⚠️ Testing complexity for integration scenarios
- ⚠️ Version mismatches between shell and content components
Document Status: ✅ Complete | Last Updated: 2025-01-29 | Version: 2.0
This deployment architecture analysis provides a comprehensive foundation for making informed decisions about Quarto site scaling strategies. The recommendations prioritize practical implementation while maintaining the flexibility to evolve as documentation needs grow.
Key Takeaways:
- Individual pages CAN be built independently but require architectural changes
- Content and navigation separation is possible but adds complexity
- Current monolithic approach is recommended for most use cases
- Gradual migration path available for future scaling needs