🛸 Hitchhiker's Guide — Phase 12: Scala & Functional Data Engineering
Read this if: you want the Scala/Cats/ZIO/FS2 toolkit fast and to pass Round 4 ("design a type-safe Scala SDK"). Skim, read WARMUP, build the SDK.
0. The 30-second mental model
This is a Scala/JVM role, and the leverage is building platform libraries other engineers can't misuse. You do that with: types that make illegal states uncompilable, Cats abstractions (Validated/Either/Resource), and effect systems (Cats Effect/ZIO/FS2) that make effects values you can compose, retry, and test. One sentence: make the happy path the only path that compiles.
1. Type-level modeling
newtype: OrderId(String) ≠ UserId(String) → can't swap args (won't compile)
smart constructor: Amount.of(cents): Validated → an Amount can NEVER be negative
ADT (sealed trait): closed set of cases → exhaustive pattern match (compiler enforces)
→ whole bug classes moved from runtime to COMPILE time
2. The typeclass ladder
Functor map F[A] => (A=>B) => F[B] transform inside a context
Applicative mapN F[A], F[B] => F[(A,B)] INDEPENDENT combine → can ACCUMULATE
Monad flatMap F[A] => (A=>F[B]) => F[B] DEPENDENT sequence → SHORT-CIRCUITS
Traverse traverse List[F[A]] => F[List[A]] effectful map over a collection
Applicative = independent + accumulate. Monad = dependent + fail-fast. Pick by independence.
3. Error modeling
Validated (applicative) → collect ALL errors (data contracts, P03) [ValidatedNel + mapN]
Either / EitherT (monad) → fail-fast (dependent business logic)
errors as a sealed-trait ADT, NOT strings/exceptions → typed, exhaustive
4. Resource safety
bracket(acquire)(use)(release): release ALWAYS runs (even on failure/cancel)
Resource composes monadically → releases LIFO (open A,B → close B,A)
if you HAVE a Resource, the only way to its value is .use → you CAN'T forget to close
5. Effect systems (the big idea)
IO[A] / ZIO[R,E,A] = a VALUE describing an effect; building it does NOTHING; run() performs it
referential transparency → effects are values: pass around, retry, combine, TEST without running
ZIO[R,E,A]: R=deps, E=typed error, A=value. Cats Effect IO[A]: simpler (Throwable errors)
structured concurrency: fibers, parMapN, race, cancellation-safe; Ref/Deferred/Queue/Semaphore
6. FS2 vs Akka Streams (in-service streaming)
FS2 → pure, lazy, back-pressured streams on Cats Effect; parEvalMap(n) = bounded concurrency;
resource-safe by construction
Akka → actor model (isolated state, supervision) + Reactive-Streams graphs (Source→Flow→Sink)
(note: Akka relicensed → Pekko is the Apache fork)
both give P01's backpressure as a typed first-class construct
7. JVM tuning levers
GC: G1 (default) vs ZGC/Shenandoah (low-pause, big heaps) ← GC pauses stall Flink checkpoints (P04)
big state → off-heap/RocksDB (keep heap small) ; spark.memory.fraction (P06)
serialization: Kryo (registered classes) ≫ Java serialization ; a hidden shuffle/checkpoint tax
never block the compute pool with I/O → use a separate blocking pool
8. SDK design (what Round 4 grades)
type-safe API + typed errors + Resource safety + sensible defaults
binary compatibility (MiMa) + semver → upgrading never breaks consumers
ergonomics: good errors, discoverable API, templates with observability baked in
goal: people CANNOT misuse it (the JD's "prevent entire classes of bugs")
9. Beginner mistakes that mark you
Either/monad for validation → fail-fast when you wanted all errors (useValidated).- Stringly-typed everything → swapped-arg bugs; no newtypes/ADTs.
- Eager side effects / Futures-that-run-on-creation instead of lazy
IO. try/finallyresource handling that someone eventually forgets (useResource).- Blocking I/O on the compute thread pool → starvation/deadlock.
- Breaking binary compatibility in a "minor" SDK release → downstream breakage.
- Java serialization in Spark → slow shuffles (register Kryo).
10. How this phase pays off later
Validated= P03 contract validator (real).Resource/IO/FS2 = P02 ingestion sidecar, P05 CDC connector.- Type-safe SDK = the "internal developer platform" of P13/P15 + JD deliverable #2.
- JVM/GC tuning = P04 checkpoint stalls, P06 spill.
Read WARMUP, build the SDK, read reference.scala, then P13: orchestration, reliability, and
observability — running all of this in production with SLOs.