Scala Development
Build Tool Selection
Prefer devtool when it is available — it auto-detects the project and wraps compile/lint/test, so you don't pick the runner by hand or risk using the wrong one:
devtool check — compile + lint + test (run before committing)
devtool compile — compile only
devtool test [pattern] — run tests, optional filter
Drop to the underlying tool only when devtool is absent. Use bloop (faster incremental) if a .bloop directory exists, otherwise sbt; module name is root for non-modular projects:
bloop compile <module-name>
bloop test <module-name> -o "*<filename>*"
sbt compile
sbt "testOnly *SpecName*"
Design Opinions
These reflect Channing's preferred style and may differ from what you'd produce by default:
Encapsulation over transparency: prefer class with private val (or opaque types) over case class when a type has internal structure that shouldn't be part of its public API (e.g. a Map tracking counts, a buffer, an index). Reserve case class for value types where every field is meaningful to callers (e.g. Book(title, author), Config(host, port)). The instinct to reach for case class by default leads to types that leak implementation details.
Typelevel ecosystem: prefer cats, cats-effect, and fs2 over alternatives (e.g. ZIO, Akka). This is a codebase consistency choice, not a judgement call — mixing effect systems creates friction.
Red Flags
- Referencing a domain type's fields without confirming the shape — even with the source in context. Assuming
Answer.Money.value.amount when it was Money(MonetaryAmount) (.amount) cost a compile-fail cycle. Confirm the constructor/field shape from source before writing assertions or pattern matches.
- A deliberate semantic divergence with no discriminating test. When behaviour intentionally differs from the mirrored precedent or the happy path (scale-insensitive
BigDecimal equality, instant-based ZonedDateTime, extension-stack resolution in a repeating row), write the test that fails if the divergence regresses. The reviewer caught these every time; the implementer did not write them first.