Files
go-kata/18-embedfs-dev-prod-switch/README.md
2026-01-04 20:40:07 +01:00

2.0 KiB
Raw Blame History

Kata 18: embed.FS Dev/Prod Switch Without Handler Forks

Target Idioms: embed, io/fs, Build Tags, fs.Sub, Same Handler Code Path
Difficulty: 🟡 Intermediate

🧠 The "Why"

Embedding assets is great for production (single binary), but terrible for frontend iteration if every CSS tweak needs a rebuild. Idiomatic Go solves this with:

  • compile-time selection via build tags
  • a shared fs.FS abstraction so handler code doesnt branch on “dev/prod”.

🎯 The Scenario

You run a small internal dashboard:

  • Prod: ship a single binary (assets embedded).
  • Dev: designers update static/ and templates/ live without recompiling.

🛠 The Challenge

Create a server that serves:

  • templates from templates/
  • static assets from static/

1. Functional Requirements

  • GET / renders an HTML template.
  • GET /static/... serves static files.
  • Dev mode serves from disk; prod mode serves embedded.
  • Handler code is identical in both modes.

2. The "Idiomatic" Constraints (Pass/Fail Criteria)

  • Build tags: two files:
    • assets_dev.go with //go:build dev
    • assets_prod.go with //go:build !dev
  • Return fs.FS: func Assets() (templates fs.FS, static fs.FS, err error)
  • Use fs.Sub: exported FS must have clean roots (no static/static/... path bugs).
  • No runtime env checks in handlers: mode selection must be compile-time.
  • Single http.FileServer setup: no duplicated handler logic for dev vs prod.

🧪 Self-Correction (Test Yourself)

  1. Live Reload

    • Build with -tags dev.
    • Modify a CSS file and refresh.
    • Pass: change shows without rebuild.
  2. Binary Portability

    • Build without tags.
    • Delete static/ and templates/ from disk.
    • Pass: server still serves assets/templates.
  3. Prefix Correctness

    • Request /static/app.css.
    • Pass: works in both modes (no 404 due to prefix mismatch).

📚 Resources