ADR-002: Choice of Implementation Language

Status

Accepted

Context

Bausteinsicht needs an implementation language for its CLI tool. The tool must:

  • Parse and generate JSON (architecture model, decided in ADR-001)

  • Parse and generate draw.io XML (mxGraph format)

  • Provide bidirectional synchronization between JSON model and draw.io XML

  • Offer CLI commands and a watch mode

  • Be easy for LLMs to generate code for (agentic coding)

  • Be easy to distribute to developers and architects

Constraints

  • JavaScript/Node.js is excluded due to security concerns with npm package managers

  • Target audience: developers and software architects with mixed language backgrounds

  • LLM-assisted development (agentic coding) is a key workflow

Evaluated Options

Option A: Go — Reference

A statically compiled language producing single-binary CLIs. Rich ecosystem for CLI tools (Cobra), XML processing (beevik/etree), and even dedicated mxGraph support (emicklei/mxgraph).

  • Single binary deployment — no runtime required

  • Cobra CLI framework (used by kubectl, gh, terraform, hugo)

  • encoding/json with compile-time type safety via struct tags

  • beevik/etree for flexible XML manipulation (similar API to Python’s ElementTree)

  • emicklei/mxgraph — dedicated Go package for writing mxGraph XML

  • fsnotify for cross-platform file watching

  • Near-instant startup (< 10ms)

  • No install-time code execution in Go modules (better supply chain security)

  • LLMs generate consistent Go code due to language simplicity and explicit patterns

  • Downside: more verbose than Python, learning curve for new Go developers

Option B: Python

A dynamically typed scripting language with batteries-included standard library.

  • xml.etree.ElementTree in standard library for XML processing

  • json in standard library, Pydantic for schema validation

  • Typer/Click for CLI framework

  • watchdog for file watching

  • Large LLM training corpus

  • Downside: requires Python runtime installation

  • Downside: packaging and distribution complexity (pipx, uv, virtual environments)

  • Downside: 50-300ms startup overhead

  • Downside: PyPI allows post-install script execution (supply chain attack surface)

  • Downside: no compile-time type checking without additional tooling

Option C: Kotlin/JVM

A JVM language with excellent XML/JSON tooling and potential Structurizr library integration.

  • Most mature XML/JSON libraries in the ecosystem (Jackson, JAXB, DOM4J)

  • Clikt CLI framework with idiomatic Kotlin DSL

  • Could directly import Structurizr’s Java library

  • Downside: JVM cold-start 200-500ms minimum

  • Downside: GraalVM native image needed for acceptable CLI UX (complex build, 90s+ compile)

  • Downside: GraalVM may fail with OOM on standard CI runners

  • Downside: split development/production model (JVM for dev, native for distribution)

Weighted Pugh Matrix

Rating scale: -1 = worse than reference, 0 = same as reference, +1 = better than reference

Reference option: A (Go)

Criterion Weight A: Go (Ref) B: Python C: Kotlin/JVM

Distribution simplicity

5

0

-1

-1

LLM code generation quality

5

0

0

-1

JSON processing (typed)

4

0

-1

0

XML processing (draw.io)

4

0

0

+1

CLI framework maturity

4

0

0

0

Startup time

3

0

-1

-1

Supply chain security

3

0

-1

0

Learning curve

3

0

+1

0

File watching

2

0

0

0

Existing library ecosystem

2

0

0

+1

Weighted Results

Criterion A: Go (Ref) B: Python C: Kotlin/JVM

Distribution simplicity (×5)

0

-5

-5

LLM code generation quality (×5)

0

0

-5

JSON processing (×4)

0

-4

0

XML processing (×4)

0

0

+4

CLI framework maturity (×4)

0

0

0

Startup time (×3)

0

-3

-3

Supply chain security (×3)

0

-3

0

Learning curve (×3)

0

+3

0

File watching (×2)

0

0

0

Existing library ecosystem (×2)

0

0

+2

Total

0

-12

-7

Decision

Option A: Go

Go provides the best overall fit for Bausteinsicht:

  1. Single binary distribution: Developers download one file and are productive. No runtime installation, no package manager, no virtual environments.

  2. LLM-friendly: Go’s simplicity (small language spec, explicit error handling, one way to do things) produces consistent, reviewable code when generated by LLMs. This directly supports the agentic coding workflow.

  3. Compile-time type safety for JSON: The architecture model schema is enforced by Go structs. Schema changes are caught at compile time, not at runtime.

  4. Dedicated mxGraph library: emicklei/mxgraph provides purpose-built Go support for the exact XML format we need.

  5. Proven CLI ecosystem: Cobra powers kubectl, gh, terraform, and hugo. The pattern is battle-tested and well-understood.

  6. Supply chain security: Go modules never execute code at install time, reducing the attack surface compared to pip/npm.

The learning curve for a developer new to Go is an acceptable trade-off. Go’s simplicity means LLMs can assist effectively during the learning process itself.

Consequences

Positive

  • Near-instant CLI startup enables responsive interactive workflows

  • Cross-compilation to all platforms from a single build machine

  • Strong ecosystem of developer CLI tools as reference implementations

  • Go’s explicit style produces self-documenting code

  • go test provides built-in testing framework with no additional dependencies

Negative

  • More verbose code compared to Python (mitigated by LLM assistance)

  • Go’s error handling requires explicit if err != nil patterns (verbose but clear)

  • Team members unfamiliar with Go need onboarding time

  • Go’s XML standard library (encoding/xml) has known limitations for dynamic attributes (mitigated by using beevik/etree)

Risks

  • If the Go mxGraph library (emicklei/mxgraph) proves insufficient, we fall back to beevik/etree for raw XML manipulation — no vendor lock-in

  • If Go’s learning curve proves too steep, the JSON-based architecture (ADR-001) means the CLI could be reimplemented in another language without changing the model format