$ npx rulesync-cli pull✓ Wrote CLAUDE.md (2 rulesets)# Coding Standards- Always use async/await- Prefer named exports
Rule Writing

AI Coding Rules for Scala Teams

Scala's multi-paradigm flexibility confuses AI assistants. Rules for functional-first patterns, case classes, effects systems, and idiomatic Scala 3.

8 min read·September 15, 2025

Scala's multi-paradigm power needs rules to stay consistent

Functional-first patterns, case classes, effects systems, and Scala 3 idioms

Why Scala Needs Specific AI Coding Rules

Scala is the most multi-paradigm language in mainstream use — you can write pure functional code, Java-style OOP, or anything in between. This flexibility is Scala's superpower for experienced teams and a minefield for AI assistants. Without rules, the AI generates a chaotic mix of styles: mutable variables in one function, pure functions in the next, Java collections in one class, Scala collections in another.

The most common AI failures in Scala: using var where val would work, generating Java-style loops instead of map/flatMap/filter, ignoring case classes in favor of regular classes, mixing Java and Scala collections, using null instead of Option, and generating Scala 2 syntax when the project uses Scala 3.

Scala teams typically have strong opinions about their preferred style — functional-first with Cats, ZIO-based effects, or pragmatic Scala. Your CLAUDE.md encodes that choice so the AI generates code that matches your team's philosophy, not a random mix.

Rule 1: Functional-First by Default

The rule: 'Write functional-first Scala. Use val exclusively — never var unless there is a measured performance reason. All functions should be pure where possible — no side effects, no mutation, no throwing exceptions. Use immutable collections by default (List, Vector, Map from scala.collection.immutable). Express errors as values (Either, Option) not exceptions.'

For side effects: 'Isolate side effects at the edges of the application (main, HTTP handlers, database access). Use an effects system (Cats Effect IO, ZIO) to make side effects explicit and composable. Pure business logic should never import IO or perform I/O directly.'

This rule sets the foundational philosophy. Everything else follows from it — if the AI knows you want functional-first, it makes different decisions at every level.

💡 val Only

Use val exclusively — never var. This single rule forces the AI to think functionally: immutable data, pure transformations, no mutation. The code becomes easier to reason about and test.

Rule 2: Case Classes and Algebraic Data Types

The rule: 'Use case class for all data types — never regular class with manual equals/hashCode/toString. Use sealed trait (Scala 2) or enum (Scala 3) for algebraic data types. Use Option instead of null — never assign or return null. Use Either[Error, A] for operations that can fail. Pattern match on ADTs exhaustively — the compiler should warn on missing cases.'

For Scala 3 specifically: 'Use enum for simple ADTs: enum Color { case Red, Green, Blue }. Use enum with parameters for complex ADTs: enum Result[+A] { case Ok(value: A); case Err(message: String) }. Use opaque type for zero-cost type wrappers: opaque type UserId = Long.'

Case classes and ADTs are Scala's core modeling tools. AI assistants that don't use them are generating Java code in Scala syntax — verbose, unsafe, and missing the compiler's exhaustiveness checking.

Rule 3: Effects System (Cats Effect or ZIO)

If your team uses an effects system — and most serious Scala teams do — the AI needs explicit rules about which one and how to use it. The two dominant options have very different APIs and philosophies.

For Cats Effect: 'Use IO[A] for all effectful operations. Use Resource[IO, A] for lifecycle-managed resources (database connections, HTTP clients). Compose effects with for-comprehension (flatMap/map). Use cats.syntax.all.* for extension methods. Never use Future — convert at the boundary if interfacing with Future-based libraries.'

For ZIO: 'Use ZIO[R, E, A] for all effectful operations. Use ZLayer for dependency injection. Use ZIO.serviceWithZIO for accessing services. Compose with for-comprehensions. Define error types as sealed traits — never use Throwable as the error channel except at boundaries.'

  • Cats Effect: IO[A] for effects, Resource for lifecycle, for-comp for composition
  • ZIO: ZIO[R, E, A] for effects, ZLayer for DI, sealed traits for errors
  • Never mix Future with IO/ZIO — convert at application boundaries only
  • Pure business logic has no IO/ZIO imports — effects at the edges only
  • Use the team's chosen library consistently — never mix Cats and ZIO
⚠️ Pick One Effects Library

Never mix Cats Effect and ZIO in the same project. They have fundamentally different approaches to dependency injection and error handling. Pick one and use it consistently.

Rule 4: Scala 3 Syntax and Features

Scala 3 introduced significant syntax changes that AI assistants don't consistently use because their training data is predominantly Scala 2. Without explicit rules, you get Scala 2 syntax that works in Scala 3 but misses the improved readability.

The rule: 'Use Scala 3 syntax: significant indentation (no braces for control structures and class bodies). Use given/using instead of implicit. Use extension methods instead of implicit classes. Use export clauses instead of inheritance for delegation. Use transparent/inline for metaprogramming instead of macros.'

For imports: 'Use Scala 3 wildcard import syntax (import foo.*) instead of Scala 2 (import foo._). Use import foo.{given, *} to import givens. Use as for import renaming: import java.util.{List as JList}.'

ℹ️ Scala 3 Syntax

AI defaults to Scala 2 syntax (import foo._, implicit). Specify 'Scala 3 syntax' to get indentation-based formatting, given/using, and extension methods — dramatically cleaner code.

Rule 5: Testing with ScalaTest or MUnit

The rule: 'Use ScalaTest with FunSuite or AnyFlatSpec style — be consistent across the project, never mix styles. Use ScalaCheck for property-based testing. For effects-based code, use munit-cats-effect or zio-test depending on your effects library. Never test side effects directly — test through the effects system.'

For integration tests: 'Use Testcontainers-Scala for database and service integration tests. Use embedded Kafka/Redis for streaming tests. Never mock the database — use a real test instance with transactions for isolation.'

Test structure: 'Group tests by behavior, not by method. Use descriptive test names as full sentences. Use fixtures via beforeAll/beforeEach for setup. For property-based tests, define custom generators for domain types.'

Complete Scala Rules Template

Consolidated template for Scala 3 teams. Adjust the effects system section for your chosen library (Cats Effect or ZIO).

  • Functional-first: val only, pure functions, immutable collections, no null
  • case class for all data — sealed trait/enum for ADTs — Option over null
  • Either[Error, A] for failures — never throw exceptions in business logic
  • Cats Effect IO or ZIO for side effects — pure logic has no effect imports
  • Scala 3 syntax: indentation-based, given/using, extension methods, export
  • Pattern matching exhaustively — compiler warnings on missing cases
  • ScalaTest/MUnit + ScalaCheck — property-based tests for core logic
  • sbt or scala-cli — scalafmt for formatting, scalafix for linting