Files
summercms/.planning/notes/why-go-not-scala.md
Jakub Zych 8ce164bb0c Initial commit: SummerCMS Go planning docs and research
Records the move from Scala to Go, the compiled-plugin decision, the
Płytarium port as the v1 target, and the Go ecosystem research that
backs the choice. No code yet.
2026-09-16 02:07:04 +02:00

3.4 KiB

title, date, context
title date context
Why Go, not Scala 2026-09-16 Exploration session that opened the summercms.go repository. Records the language decision so it is not relitigated.

Why Go, not Scala

Background

SummerCMS started in February 2026 as a Scala 3 rewrite of WinterCMS. The stack was direct-style Scala on JDK 21 virtual threads (Tapir, Ox, Magnum, Jig). Three infrastructure modules shipped as independent sbt projects: summer-compass (config), summer-phrasebook (i18n), summer-bonfire (console). They live in the meta repo under modules/.

The effort stalled before any application ran. It was building Illuminate-shaped infrastructure bottom-up with no real app pulling requirements.

The deciding test

We gave both ecosystems the same task: an OAuth2 server with full OIDC support.

  • PHP / WinterCMS. The agent took league/oauth2-server as a model, read the existing Golem15 user plugin forks, reused the JWT primitives already in the stack, and produced a plugin. The Płytarium project today runs its own OAuth2 provider on this basis.
  • Scala. The agent searched and found nothing reusable. It proposed a from-scratch implementation. The proposal was rejected as too large to fund.

The Scala language was never the problem. The ecosystem was. A CMS is mostly glue over solved problems, and glue needs the problems to be solved already.

Why Go passes the same test

The Go ecosystem research (see ../research/go-ecosystem.md) found maintained, widely used libraries for every concern in the Illuminate module map, including the one that failed in Scala: zitadel/oidc is an OpenID-certified provider and client library. Go is closer to PHP's situation than Scala is: many small, boring, well-tested libraries, and several Laravel-shaped frameworks and CMSes to read for patterns (Goravel, PocketBase, GoFrame).

What Go changes

Go is statically compiled, so "drop a plugin folder and it autoloads" does not exist. The decision:

  • Primary: compiled plugins. Caddy/xcaddy model. plugins/ is a workspace of Go modules. A generated import list registers them. summer make:plugin scaffolds, summer build rebuilds, and a watch-rebuild loop in dev makes it feel like WinterCMS. Full type safety, plugins extend each other through ordinary Go interfaces, go test works.
  • Later: WASM extension API. A narrow, sandboxed surface for untrusted third-party extensions. Only after the core plugin API is stable. See ../seeds/wasm-extension-api.md.
  • Rejected: stdlib plugin (Linux-only, toolchain lockstep, no unload), yaegi (lags Go releases, partial generics), RPC plugins (interface too wide to marshal for model extension).

What carries over from the Scala work

  • The module naming scheme in IDEA_LIB_NAMES.md (backpack, compass, phrasebook, bonfire, ...) can be reused as Go package names.
  • The design of compass (config layering and plugin namespaces), phrasebook (CLDR plurals, namespaced keys), and bonfire (command registry, rich output) is language-neutral and worth porting.
  • The open ORM question from STACK.md (immutable models vs Eloquent mutation) largely disappears in Go: structs are mutable, and both ent and GORM support Eloquent-like flows.

Non-goals

  • Not chasing JVM performance. Go's baseline is enough and deploys as one binary.
  • Not porting Illuminate module by module. The v1 target app pulls what is needed. See v1-target-plytarium.md.