provisioning/docs/src/architecture/adr/ADR-006-provisioning-cli-refactoring.md
2026-01-14 04:53:58 +00:00

12 KiB

ADR-006: Provisioning CLI Refactoring to Modular Architecture

Status: Implemented Date: 2025-09-30 Authors: Infrastructure Team Related: ADR-001 (Project Structure), ADR-004 (Hybrid Architecture)

Context

The main provisioning CLI script (provisioning/core/nulib/provisioning) had grown to 1,329 lines with a massive 1,100+ line match statement handling all commands. This monolithic structure created multiple critical problems:

Problems Identified

  1. Maintainability Crisis

    • 54 command branches in one file
    • Code duplication: Flag handling repeated 50+ times
    • Hard to navigate: Finding specific command logic required scrolling through 1,000+ lines
    • Mixed concerns: Routing, validation, and execution all intertwined
  2. Development Friction

    • Adding new commands required editing massive file
    • Testing was nearly impossible (monolithic, no isolation)
    • High cognitive load for contributors
    • Code review difficult due to file size
  3. Technical Debt

    • 10+ lines of repetitive flag handling per command
    • No separation of concerns
    • Poor code reusability
    • Difficult to test individual command handlers
  4. User Experience Issues

    • No bi-directional help system
    • Inconsistent command shortcuts
    • Help system not fully integrated

Decision

We refactored the monolithic CLI into a modular, domain-driven architecture with the following structure:

provisioning/core/nulib/
├── provisioning (211 lines) ⬅️ 84% reduction
├── main_provisioning/
│   ├── flags.nu (139 lines) ⭐ Centralized flag handling
│   ├── dispatcher.nu (264 lines) ⭐ Command routing
│   ├── mod.nu (updated)
│   └── commands/ ⭐ Domain-focused handlers
│       ├── configuration.nu (316 lines)
│       ├── development.nu (72 lines)
│       ├── generation.nu (78 lines)
│       ├── infrastructure.nu (117 lines)
│       ├── orchestration.nu (64 lines)
│       ├── utilities.nu (157 lines)
│       └── workspace.nu (56 lines)

Key Components

1. Centralized Flag Handling (flags.nu)

Single source of truth for all flag parsing and argument building:

export def parse_common_flags [flags: record]: nothing -> record
export def build_module_args [flags: record, extra: string = ""]: nothing -> string
export def set_debug_env [flags: record]
export def get_debug_flag [flags: record]: nothing -> string

Benefits:

  • Eliminates 50+ instances of duplicate code
  • Single place to add/modify flags
  • Consistent flag handling across all commands
  • Reduced from 10 lines to 3 lines per command handler

2. Command Dispatcher (dispatcher.nu)

Central routing with 80+ command mappings:

export def get_command_registry []: nothing -> record  # 80+ shortcuts
export def dispatch_command [args: list, flags: record]  # Main router

Features:

  • Command registry with shortcuts (ws → workspace, orch → orchestrator, etc.)
  • Bi-directional help support (provisioning ws help works)
  • Domain-based routing (infrastructure, orchestration, development, etc.)
  • Special command handling (create, delete, price, etc.)

3. Domain Command Handlers (commands/*.nu)

Seven focused modules organized by domain:

Module Lines Responsibility
infrastructure.nu 117 Server, taskserv, cluster, infra
orchestration.nu 64 Workflow, batch, orchestrator
development.nu 72 Module, layer, version, pack
workspace.nu 56 Workspace, template
generation.nu 78 Generate commands
utilities.nu 157 SSH, SOPS, cache, providers
configuration.nu 316 Env, show, init, validate

Each handler:

  • Exports handle_<domain>_command function
  • Uses shared flag handling
  • Provides error messages with usage hints
  • Isolated and testable

Architecture Principles

1. Separation of Concerns

  • Routingdispatcher.nu
  • Flag parsingflags.nu
  • Business logiccommands/*.nu
  • Help systemhelp_system.nu (existing)

2. Single Responsibility

Each module has ONE clear purpose:

  • Command handlers execute specific domains
  • Dispatcher routes to correct handler
  • Flags module normalizes all inputs

3. DRY (Don't Repeat Yourself)

Eliminated repetition:

  • Flag handling: 50+ instances → 1 function
  • Command routing: Scattered logic → Command registry
  • Error handling: Consistent across all domains

4. Open/Closed Principle

  • Open for extension: Add new handlers easily
  • Closed for modification: Core routing unchanged

5. Dependency Inversion

All handlers depend on abstractions (flag records, not concrete flags):

# Handler signature
export def handle_infrastructure_command [
  command: string
  ops: string
  flags: record  # ⬅️ Abstraction, not concrete flags
]

Implementation Details

Migration Path (Completed in 2 Phases)

Phase 1: Foundation

  1. Created commands/ directory structure
  2. Created flags.nu with common flag handling
  3. Created initial command handlers (infrastructure, utilities, configuration)
  4. Created dispatcher.nu with routing logic
  5. Refactored main file (1,329 → 211 lines)
  6. Tested basic functionality

Phase 2: Completion

  1. Fixed bi-directional help (provisioning ws help now works)
  2. Created remaining handlers (orchestration, development, workspace, generation)
  3. Removed duplicate code from dispatcher
  4. Added comprehensive test suite
  5. Verified all shortcuts work

Bi-directional Help System

Users can now access help in multiple ways:

# All these work equivalently:
provisioning help workspace
provisioning workspace help  # ⬅️ NEW: Bi-directional
provisioning ws help         # ⬅️ NEW: With shortcuts
provisioning help ws         # ⬅️ NEW: Shortcut in help

Implementation:

# Intercept "command help" → "help command"
let first_op = if ($ops_list | length) > 0 { ($ops_list | get 0) } else { "" }
if $first_op in ["help" "h"] {
  exec $"($env.PROVISIONING_NAME)" help $task --notitles
}

Command Shortcuts

Comprehensive shortcut system with 30+ mappings:

Infrastructure:

  • sserver
  • t, tasktaskserv
  • clcluster
  • iinfra

Orchestration:

  • wf, flowworkflow
  • batbatch
  • orchorchestrator

Development:

  • modmodule
  • lyrlayer

Workspace:

  • wsworkspace
  • tpl, tmpltemplate

Testing

Comprehensive test suite created (tests/test_provisioning_refactor.nu):

Test Coverage

  • Main help display
  • Category help (infrastructure, orchestration, development, workspace)
  • Bi-directional help routing
  • All command shortcuts
  • Category shortcut help
  • Command routing to correct handlers

Test Results

📋 Testing main help... ✅
📋 Testing category help... ✅
🔄 Testing bi-directional help... ✅
⚡ Testing command shortcuts... ✅
📚 Testing category shortcut help... ✅
🎯 Testing command routing... ✅

📊 TEST RESULTS: 6 passed, 0 failed

Results

Quantitative Improvements

Metric Before After Improvement
Main file size 1,329 lines 211 lines 84% reduction
Command handler 1 massive match (1,100+ lines) 7 focused modules Domain separation
Flag handling Repeated 50+ times 1 function 98% duplication removal
Code per command 10 lines 3 lines 70% reduction
Modules count 1 monolith 9 modules Modular architecture
Test coverage None 6 test groups Comprehensive testing

Qualitative Improvements

Maintainability

  • Easy to find specific command logic
  • Clear separation of concerns
  • Self-documenting structure
  • Focused modules (< 320 lines each)

Extensibility

  • Add new commands: Just update appropriate handler
  • Add new flags: Single function update
  • Add new shortcuts: Update command registry
  • No massive file edits required

Testability

  • Isolated command handlers
  • Mockable dependencies
  • Test individual domains
  • Fast test execution

Developer Experience

  • Lower cognitive load
  • Faster onboarding
  • Easier code review
  • Better IDE navigation

Trade-offs

Advantages

  1. Dramatically reduced complexity: 84% smaller main file
  2. Better organization: Domain-focused modules
  3. Easier testing: Isolated, testable units
  4. Improved maintainability: Clear structure, less duplication
  5. Enhanced UX: Bi-directional help, shortcuts
  6. Future-proof: Easy to extend

Disadvantages

  1. More files: 1 file → 9 files (but smaller, focused)
  2. Module imports: Need to import multiple modules (automated via mod.nu)
  3. Learning curve: New structure requires documentation (this ADR)

Decision: Advantages significantly outweigh disadvantages.

Examples

Before: Repetitive Flag Handling

"server" => {
  let use_check = if $check { "--check "} else { "" }
  let use_yes = if $yes { "--yes" } else { "" }
  let use_wait = if $wait { "--wait" } else { "" }
  let use_keepstorage = if $keepstorage { "--keepstorage "} else { "" }
  let str_infra = if $infra != null  { $"--infra ($infra) "} else { "" }
  let str_outfile = if $outfile != null  { $"--outfile ($outfile) "} else { "" }
  let str_out = if $out != null  { $"--out ($out) "} else { "" }
  let arg_include_notuse = if $include_notuse { $"--include_notuse "} else { "" }
  run_module $"($str_ops) ($str_infra) ($use_check)..." "server" --exec
}

After: Clean, Reusable

def handle_server [ops: string, flags: record] {
  let args = build_module_args $flags $ops
  run_module $args "server" --exec
}

Reduction: 10 lines → 3 lines (70% reduction)

Future Considerations

Potential Enhancements

  1. Unit test expansion: Add tests for each command handler
  2. Integration tests: End-to-end workflow tests
  3. Performance profiling: Measure routing overhead (expected to be negligible)
  4. Documentation generation: Auto-generate docs from handlers
  5. Plugin architecture: Allow third-party command extensions

Migration Guide for Contributors

See docs/development/COMMAND_HANDLER_GUIDE.md for:

  • How to add new commands
  • How to modify existing handlers
  • How to add new shortcuts
  • Testing guidelines
  • Architecture Overview: docs/architecture/system-overview.md
  • Developer Guide: docs/development/COMMAND_HANDLER_GUIDE.md
  • Main Project Docs: CLAUDE.md (updated with new structure)
  • Test Suite: tests/test_provisioning_refactor.nu

Conclusion

This refactoring transforms the provisioning CLI from a monolithic, hard-to-maintain script into a modular, well-organized system following software engineering best practices. The 84% reduction in main file size, elimination of code duplication, and comprehensive test coverage position the project for sustainable long-term growth.

The new architecture enables:

  • Faster development: Add commands in minutes, not hours
  • Better quality: Isolated testing catches bugs early
  • Easier maintenance: Clear structure reduces cognitive load
  • Enhanced UX: Shortcuts and bi-directional help improve usability

Status: Successfully implemented and tested. All commands operational. Ready for production use.


This ADR documents a major architectural improvement completed on 2025-09-30.