2026-01-14 03:09:18 +00:00
|
|
|
# Nickel vs KCL: Comprehensive Comparison\n\n**Status**: Reference Guide\n**Last Updated**: 2025-12-15\n**Related**: ADR-011: Migration from KCL to Nickel\n\n---\n\n## Quick Decision Tree\n\n```\nNeed to define infrastructure/schemas?\n├─ New platform schemas → Use Nickel ✅\n├─ New provider extensions → Use Nickel ✅\n├─ Legacy workspace configs → Can use KCL (migrate gradually)\n├─ Need type-safe UIs? → Nickel + TypeDialog ✅\n├─ Application settings? → Use TOML (not KCL/Nickel)\n└─ K8s/CI-CD config? → Use YAML (not KCL/Nickel)\n```\n\n---\n\n## 1. Side-by-Side Code Examples\n\n### Simple Schema: Server Configuration\n\n#### KCL Approach\n\n```\nschema ServerDefaults:\n name: str\n cpu_cores: int = 2\n memory_gb: int = 4\n os: str = "ubuntu"\n\n check:\n cpu_cores > 0, "CPU cores must be positive"\n memory_gb > 0, "Memory must be positive"\n\nserver_defaults: ServerDefaults = {\n name = "web-server",\n cpu_cores = 4,\n memory_gb = 8,\n os = "ubuntu",\n}\n```\n\n**Note**: KCL is deprecated. Use Nickel for new projects.\n\n#### Nickel Approach (Three-File Pattern)\n\n**server_contracts.ncl**:\n\n```\n{\n ServerDefaults = {\n name | String,\n cpu_cores | Number,\n memory_gb | Number,\n os | String,\n },\n}\n```\n\n**server_defaults.ncl**:\n\n```\n{\n server = {\n name = "web-server",\n cpu_cores = 4,\n memory_gb = 8,\n os = "ubuntu",\n },\n}\n```\n\n**server.ncl**:\n\n```\nlet contracts = import "./server_contracts.ncl" in\nlet defaults = import "./server_defaults.ncl" in\n\n{\n defaults = defaults,\n\n make_server | not_exported = fun overrides =>\n defaults.server & overrides,\n\n DefaultServer = defaults.server,\n}\n```\n\n**Usage**:\n\n```\nlet server = import "./server.ncl" in\n\n# Simple override\nmy_server = server.make_server { cpu_cores = 8 }\n\n# With custom field (Nickel allows this!)\nmy_custom = server.defaults.server & {\n cpu_cores = 16,\n custom_monitoring_level = "verbose" # ✅ Works!\n}\n```\n\n**Key Differences**:\n\n- **KCL**: Validation inline, single file, rigid schema\n- **Nickel**: Separated concerns (contracts, defaults, instances), flexible composition\n\n---\n\n### Complex Schema: Provider with Multiple Types\n\n#### KCL (from `provisioning/extensions/providers/upcloud/nickel/` - legacy approach)\n\n```\nschema StorageBackup:\n backup_id: str\n frequency: str\n retention_days: int = 7\n\nschema ServerUpcloud:\n name: str\n plan: str\n zone: str\n storage_backups: [StorageBackup] = []\n\nschema ProvisionUpcloud:\n api_key: str\n api_password: str\n servers: [ServerUpcloud] = []\n\nprovision_upcloud: ProvisionUpcloud = {\n api_key = ""\n api_password = ""\n servers = []\n}\n```\n\n#### Nickel (from `provisioning/extensions/providers/upcloud/nickel/`)\n\n**upcloud_contracts.ncl**:\n\n```\n{\n StorageBackup = {\n backup_id | String,\n frequency | String,\n retention_days | Number,\n },\n\n ServerUpcloud = {\n name | String,\n plan | String,\n zone | String,\n storage_backups | Array,\n },\n\n ProvisionUpcloud = {\n api_key | String,\n api_password | String,\n servers | Array,\n },\n}\n```\n\n**upcloud_defaults.ncl**:\n\n```\n{\n storage_backup = {\n backup_id = "",\n frequency = "daily",\n retention_days = 7,\n },\n\n server_upcloud = {\n name = "",\n plan = "1xCPU-1 GB",\n zone = "us-nyc1",\n storage_backups = [],\n },\n\n provision_upcloud = {\n api_key = "",\n api_password = "",\n servers = [],\n },\n}\n```\n\n**upcloud_main.ncl** (from actual codebase):\n\n```\nlet contracts = import "./upcloud_contracts.ncl" in\nlet defaults = import "./upcloud_defaults.ncl" in\n\n{\n defaults = defaults,\n\n make_storage_backup | not_exported = fun overrides =>\n defaults.storage_backup & overrides,\n\n make_server_upcloud | not_exported = fun overrides =>\n defaults.server_upcloud & overrides,\n\n make_provision_upcloud | not_exported = fun overrides =>\n defaults.provis
|