docs(adr): ADR-0016: Implementation Phasing Strategy
@aRustyDev ci sta già lavorando.
Dal 5/1/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
ADR-0016: Implementation Phasing Strategy
Status
Accepted
Context
mdbook-htmx is a complex project with many features: template rendering, HTMX integration, authorization, search indexing, scope filtering, and asset handling. Implementing everything at once would be risky and make debugging difficult.
This ADR defines a phased implementation strategy that delivers value incrementally while managing complexity.
Decision Drivers
- Early Value - Ship usable output as soon as possible
- Risk Mitigation - Isolate complex features for easier debugging
- Testing Surface - Each phase should be independently testable
- Dependency Order - Features must be built in the right order
- User Feedback - Get feedback before building optional features
Decision
Implement in 5 phases, each with clear deliverables, tests, and release milestones.
Phase Overview
| Phase | Name | Focus | Release |
|---|---|---|---|
| 1 | Core Backend | Basic rendering | v0.1.0 |
| 2 | HTMX Integration | Navigation patterns | v0.2.0 |
| 3 | Authorization | Auth metadata + manifest | v0.3.0 |
| 4 | Search | Index generation | v0.4.0 |
| 5 | Polish | Theming, scopes, assets | v1.0.0 |
Deployment Methods
Three deployment methods evolve alongside the implementation:
| Method | Location | Purpose |
|---|---|---|
| Docker Compose | mdbook-htmx/docker/ |
Local dev, CI testing |
| Helm Chart | aRustyDev/helm-charts/charts/mdbook-htmx |
Kubernetes |
| Cloudflare | mdbook-htmx/workers/ + wrangler.jsonc |
Production edge |
Each phase adds deployment features:
| Phase | Docker Compose | Helm Chart | Cloudflare |
|---|---|---|---|
| v0.1.0 | NGINX static | Static serve | Pages static |
| v0.2.0 | +fragment routing | +Ingress rules | +fragment routing |
| v0.3.0 | +OAuth2 proxy | +auth sidecar | +Workers D1 auth |
| v0.4.0 | +Meilisearch | +Meilisearch | +KV search index |
| v1.0.0 | +cache, metrics | +HPA, PDB | +R2 assets, analytics |
Phase 1: Core Backend (v0.1.0)
Goal
Produce valid HTML output from MDBook's RenderContext.
Deliverables
| Component | Description |
|---|---|
| Entry point | Read RenderContext from stdin |
| Configuration | Parse [output.htmx] from book.toml |
| Template engine | Initialize Tera with default templates |
| Chapter rendering | Render each chapter to HTML |
| Output structure | Create pages/ and fragments/ directories |
| Manifest | Generate basic manifest.json |
Implementation Order
1. Cargo setup + dependencies
2. main.rs entry point
3. Config deserialization
4. Template loading
5. Chapter iteration
6. HTML output
7. Manifest generation
Exit Criteria
-
mdbook buildproduces HTML inbook/htmx/ - Pages render correctly without HTMX attributes
- Manifest contains all pages with paths and titles
- Default templates bundled in binary
- Custom theme override works
Deployment (Phase 1)
| Method | Deliverables |
|---|---|
| Docker Compose | compose.yml, nginx.conf, Dockerfile.build |
| Helm Chart | Chart skeleton with deployment, service, configmap |
| Cloudflare | wrangler.jsonc, basic workers/index.ts |
| CI | test-deployments.yml, deploy-cloudflare.yml |
Test Cases
#[test]
fn test_basic_render() {
let ctx = test_render_context();
let output = render(&ctx).unwrap();
assert!(output.join("pages/index.html").exists());
}
#[test]
fn test_manifest_generation() {
let ctx = test_render_context();
render(&ctx).unwrap();
let manifest: Manifest = serde_json::from_reader(
File::open(ctx.destination.join("manifest.json")).unwrap()
).unwrap();
assert_eq!(manifest.pages.len(), ctx.book.iter().count());
}
Phase 2: HTMX Integration (v0.2.0)
Goal
Enable SPA-like navigation with HTMX attributes.
Deliverables
| Component | Description |
|---|---|
| hx-boost | Add to body element |
| hx-target | Default content target |
| hx-swap | Default swap strategy |
| hx-push-url | URL updates |
| Fragments | Content-only partials |
| OOB swaps | Sidebar + breadcrumb updates |
Implementation Order
1. Update layout template with HTMX body attributes
2. Fragment template (content without layout)
3. OOB partial templates
4. Sidebar active state
5. Breadcrumb generation
6. Navigation links with hx-* attributes
Exit Criteria
- Clicking links loads content without full page reload
- Sidebar highlights active page
- Breadcrumbs update on navigation
- Browser back/forward works correctly
- Fragments served for HX-Request headers
Deployment (Phase 2)
| Method | Additions |
|---|---|
| Docker Compose | NGINX HX-Request header detection, fragment routing |
| Helm Chart | Ingress annotations for fragment routing |
| Cloudflare | Worker routes fragments based on HX-Request header |
Test Cases
#[test]
fn test_htmx_attributes() {
let html = render_page(&chapter).unwrap();
assert!(html.contains(r#"hx-boost="true""#));
assert!(html.contains(r#"hx-target="#content""#));
}
#[test]
fn test_fragment_output() {
let ctx = test_render_context();
render(&ctx).unwrap();
assert!(ctx.destination.join("fragments/chapter-1.html").exists());
let fragment = fs::read_to_string(...).unwrap();
assert!(!fragment.contains("<!DOCTYPE html>"));
}
Phase 3: Authorization (v0.3.0)
Goal
Extract and surface authorization metadata for server-side enforcement.
Deliverables
| Component | Description |
|---|---|
| Frontmatter parsing | Extract auth from YAML frontmatter |
| Manifest auth | Include auth requirements per page |
| Access-denied template | Partial for unauthorized access |
| Signin template | Partial for authentication prompt |
| Validation | Build-time auth config validation |
Implementation Order
1. Frontmatter extractor
2. Auth schema validation
3. Manifest auth fields
4. Access-denied partial
5. Signin partial
6. CLI validation command
Exit Criteria
- Frontmatter
auth:section parsed correctly - Manifest includes auth requirements per page
- Invalid auth config fails build with clear error
- Partials exist for access-denied and signin
- Reference server implementation documented
Deployment (Phase 3)
| Method | Additions |
|---|---|
| Docker Compose | compose.auth.yml overlay with OAuth2 proxy |
| Helm Chart | OAuth2 proxy sidecar, auth secret templates |
| Cloudflare | D1 database for sessions, auth middleware in worker |
Files:
docker/compose.auth.ymlworkers/auth.ts,workers/middleware.tsmigrations/0001_sessions.sql
Test Cases
#[test]
fn test_frontmatter_auth() {
let (fm, _) = extract_frontmatter(r#"---
auth:
access: roles
roles: [admin]
---
# Content"#).unwrap();
assert_eq!(fm.auth.access, AccessLevel::Roles);
assert_eq!(fm.auth.roles, vec!["admin"]);
}
#[test]
fn test_manifest_includes_auth() {
let manifest = generate_manifest(&ctx).unwrap();
let admin_page = manifest.pages.iter()
.find(|p| p.path == "/docs/admin").unwrap();
assert_eq!(admin_page.auth.roles, vec!["admin"]);
}
Phase 4: Search (v0.4.0)
Goal
Generate search indexes for server-side search.
Deliverables
| Component | Description |
|---|---|
| Index generation | Build search-index.json |
| Heading extraction | Index up to H3 by default |
| Body indexing | Full text content |
| Auth filtering | Include auth info for runtime filtering |
| Search partial | Results display template |
Implementation Order
1. Document indexer
2. Heading extractor
3. Body text stripper (remove markdown)
4. JSON index writer
5. Search partial template
6. Index configuration options
Exit Criteria
- search-index.json generated with all pages
- Headings indexed with anchors
- Body text stripped of markdown
- Auth info included for filtering
- Search partial renders results
Deployment (Phase 4)
| Method | Additions |
|---|---|
| Docker Compose | compose.search.yml overlay with Meilisearch, indexer |
| Helm Chart | Meilisearch StatefulSet, PVC, search init job |
| Cloudflare | KV namespace for search index, search handler |
Files:
docker/compose.search.yml,docker/init-meilisearch.shworkers/search.ts,scripts/upload-search-index.ts
Test Cases
#[test]
fn test_search_index() {
let index = generate_search_index(&ctx).unwrap();
assert!(!index.documents.is_empty());
let doc = &index.documents[0];
assert!(!doc.body.contains("```")); // No code blocks
assert!(doc.headings.iter().any(|h| h.level == 2));
}
#[test]
fn test_search_index_includes_auth() {
let index = generate_search_index(&ctx).unwrap();
let admin_doc = index.documents.iter()
.find(|d| d.path == "/docs/admin").unwrap();
assert_eq!(admin_doc.auth.access, "roles");
}
Phase 5: Polish (v1.0.0)
Goal
Production-ready features: theming, scopes, asset handling.
Deliverables
| Component | Description |
|---|---|
| Scope filtering | Per-scope manifests and indexes |
| Asset hashing | Content-based cache busting |
| CSS variables | Theming system |
| Dark mode | Theme switching |
| Minification | Optional HTML/CSS minification |
Implementation Order
1. Scope configuration
2. Scope-filtered manifest generation
3. Scope-filtered search indexes
4. Asset hasher
5. CSS variable system
6. Dark/light theme support
7. Minification (optional)
Exit Criteria
- Scope-filtered manifests and indexes generated
- Asset filenames include content hash
- Theme switching works via CSS variables
- Dark mode respects system preference
- Performance benchmarks documented
Deployment (Phase 5)
| Method | Additions |
|---|---|
| Docker Compose | compose.prod.yml with caching, metrics |
| Helm Chart | HPA, PDB, ServiceMonitor for Prometheus |
| Cloudflare | R2 bucket for assets, analytics, cache optimization |
Files:
docker/compose.prod.yml,docker/nginx-cache.confworkers/analytics.ts
Test Cases
#[test]
fn test_scope_filtering() {
let ctx = test_render_context_with_scopes();
render(&ctx).unwrap();
let dev_manifest: Manifest = serde_json::from_reader(
File::open(ctx.destination.join("manifest.developers.json")).unwrap()
).unwrap();
assert!(dev_manifest.pages.iter().all(|p| p.scopes.contains(&"developers")));
}
#[test]
fn test_asset_hashing() {
let ctx = test_render_context();
render(&ctx).unwrap();
let css_files: Vec<_> = glob("book/htmx/assets/css/*.css").unwrap().collect();
assert!(css_files.iter().any(|f| f.contains("-"))); // Hash in filename
}
Feature Flags
Each phase's features are gated behind configuration:
[output.htmx]
# Phase 1: Always on (core rendering)
# Phase 2: HTMX attributes
boost = true # Can disable for static output
# Phase 3: Authorization
[output.htmx.authz]
enabled = true
# Phase 4: Search
[output.htmx.search]
enabled = true
# Phase 5: Advanced features
[output.htmx.scopes]
enabled = true
[output.htmx.assets]
hash-files = true
Dependency Graph
Phase 1 (Core)
↓
Phase 2 (HTMX) ──────────────────┐
↓ │
Phase 3 (Auth) ────┐ │
↓ ↓ │
Phase 4 (Search) │ │
↓ │ │
Phase 5 (Polish) ←─┴─────────────┘
- Phase 2 depends on Phase 1 (needs basic rendering)
- Phase 3 depends on Phase 1 (needs manifest)
- Phase 4 depends on Phase 1 + 3 (needs auth for filtering)
- Phase 5 depends on all phases
Consequences
Positive
- Early releases provide value
- Each phase is testable in isolation
- Complex features don't block simpler ones
- User feedback informs later phases
Negative
- Multiple releases to track
- Some refactoring between phases
- Users may need to upgrade incrementally
Mitigation
- Semantic versioning with clear changelogs
- Migration guides between versions
- Feature flags for gradual adoption
References
- ADR-0012: MDBook Renderer Trait Implementation
- ADR-0003: Authorization Metadata via Manifest
- ADR-0005: Server-Side Search
- Incremental Delivery (Martin Fowler)
Deployment Examples
- Lingua principale
- Rust
- Stelle
- 0
- Fork
- 1
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di aRustyDev/mdbook-htmx
-
feat(ci): Implement cross-repo-validate compliance for docs syncForse di nuovo libera @aRustyDev l’ha presa 284 giorni fa e non c’è nessuna pull request aperta. Apertaci enhancement
aRustyDev/mdbook-htmx#58 · 1 commento · 1 assegnatario ·
-
docs(htmx): Explain how HTMX worksForse di nuovo libera @aRustyDev l’ha presa 286 giorni fa e non c’è nessuna pull request aperta. Aperta
aRustyDev/mdbook-htmx#48 · 3 commenti · 1 assegnatario ·
-
docs(mdbook-htmx): Parent Issue for documentationForse di nuovo libera @aRustyDev l’ha presa 286 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#47 · 1 assegnatario ·
-
docs(examples): Secure Self-Hosted Meilisearch via CloudflareForse di nuovo libera @aRustyDev l’ha presa 286 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#46 · 1 assegnatario ·
-
docs(examples): Kubernetes with MeilisearchForse di nuovo libera @aRustyDev l’ha presa 286 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#45 · 1 assegnatario ·
Tutte le issue di aRustyDev/mdbook-htmx
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 64/100
I maintainer di solito rispondono entro 3 giorni
-
Nushell launcher failure.Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
-
Zellij doesn't validate hex colors correctly and crashesForse già presa @ymcx l’ha presa oggi. Apertasuspected bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
zellij-org/zellij#5732 ·
I maintainer di solito rispondono entro 1 giorno
-
bug(api): E_DOSE_ATTR_NONFINITE for a modeled D/R still says the value "never reaches you as a NaN"Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 63/100