docs(adr): ADR-0016: Implementation Phasing Strategy
@aRustyDev 已经在做这个了。
开始于 2026年1月5日。
评估
这个 Issue 还没有评估数据。
描述
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
- 主要语言
- Rust
- 星标
- 0
- 派生
- 1
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
aRustyDev/mdbook-htmx 的其他 Issue
-
feat(ci): Implement cross-repo-validate compliance for docs sync可能重新可做 @aRustyDev 于 289 天前认领,目前没有进行中的 PR。 未关闭ci enhancement
aRustyDev/mdbook-htmx#58 · 1 条评论 · 已指派 1 人 ·
-
docs(htmx): Explain how HTMX works可能重新可做 @aRustyDev 于 291 天前认领,目前没有进行中的 PR。 未关闭
aRustyDev/mdbook-htmx#48 · 3 条评论 · 已指派 1 人 ·
-
docs(mdbook-htmx): Parent Issue for documentation可能重新可做 @aRustyDev 于 291 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#47 · 已指派 1 人 ·
-
docs(examples): Secure Self-Hosted Meilisearch via Cloudflare可能重新可做 @aRustyDev 于 291 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#46 · 已指派 1 人 ·
-
docs(examples): Kubernetes with Meilisearch可能重新可做 @aRustyDev 于 291 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#45 · 已指派 1 人 ·
查看 aRustyDev/mdbook-htmx 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 85/100
farion1231/cc-switch#8829 ·
维护者通常 1 天内回复
-
bug ci good first issue
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 92/100
longbridge/gpui-kit#3677 ·
维护者通常 1 天内回复
-
E-pr-welcome
难度 2/5 1-3 小时 新手友好度 77/100
seanmonstar/reqwest#3121 · 2 条评论 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 68/100
ilya-zlobintsev/LACT#1235 ·
维护者通常 1 天内回复