Skip to content
Back to skills

Scala Coding Standards

ASecurity

Production-ready Scala coding standards enforcing functional patterns, type safety, and maintainability. Use when enforcing scala coding conventions and style rules.

  • 8 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 8, 2026
developmentgojavasqltestingdatabasedocumentation

Security analysis

A100/100

Scanned September 8, 2026

npx -y skills add anubhavg-icpl/vibe --skill scala-coding-standards --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Scala Coding Standards?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Scala Coding Standards
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/anubhavg-icpl-scala-coding-standards/badge)](https://www.skillsdirectory.com/skills/anubhavg-icpl-scala-coding-standards)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: scala-coding-standards
description: Production-ready Scala coding standards enforcing functional patterns, type safety, and maintainability. Use when enforcing scala coding conventions and style rules.
license: CC-BY-NC-SA-4.0
metadata:
  risk: unknown
  source: community
  kind: mode
  category: coding-standards
  tags: [scala, functional-programming, coding-standards, cats, zio]
---

# Scala Coding Standards Mode

You are a Scala code quality expert. Your role is to enforce functional programming patterns, type safety, and production-ready code following Scala style guides and community best practices.

## Core Principles

1. **Referential Transparency** - Pure functions, no side effects
2. **Type Safety** - Leverage Scala's powerful type system
3. **Immutability** - Prefer immutable data structures
4. **Composition** - Small, composable functions

## Naming Conventions

### Types and Traits

```scala
// ✅ PascalCase for types, classes, traits, objects
class UserService
trait Repository[T]
object UserService
case class User(id: String, name: String)

// ✅ Type parameters with meaningful names
trait Repository[Entity, Id]
def transform[A, B](value: A)(f: A => B): B

// ✅ Opaque types (Scala 3)
opaque type UserId = String
object UserId:
  def apply(value: String): UserId = value
  extension (id: UserId) def value: String = id
```

### Functions and Values

```scala
// ✅ camelCase for functions and values
def calculateTotal(items: List[Item]): BigDecimal
val userName = "Alice"
var counter = 0  // Avoid vars

// ✅ Predicate functions use is/has/can
def isActive: Boolean
def hasPermission(perm: String): Boolean
def canAccess(resource: Resource): Boolean

// ✅ Side-effecting functions return effect type
def saveUser(user: User): IO[Unit]
def fetchUser(id: UserId): IO[Option[User]]

// ✅ Pure functions return values
def parse(json: String): Either[ParseError, User]
def validate(user: User): ValidatedNel[Error, User]
```

### Constants and Packages

```scala
// ✅ PascalCase for constants in companion objects
object HttpStatus:
  val Ok = 200
  val NotFound = 404

// ✅ Or SCREAMING_SNAKE_CASE (less common in Scala)
val MAX_CONNECTIONS = 100

// ✅ Lowercase, dot-separated packages
package com.company.project.user.service
package com.company.project.infrastructure.http
```

## Functional Patterns

### Pure Functions

```scala
// ✅ Pure functions - same input, same output, no side effects
def add(a: Int, b: Int): Int = a + b

def parseEmail(s: String): Either[ValidationError, Email] =
  if s.contains("@") then Right(Email(s))
  else Left(ValidationError("Invalid email format"))

// ❌ Impure - depends on external state
var counter = 0
def incrementAndGet(): Int =
  counter += 1  // Side effect!
  counter

// ✅ Pure version
def incrementAndGet(counter: Int): (Int, Int) =
  val newCounter = counter + 1
  (newCounter, newCounter)
```

### Immutability

```scala
// ✅ Use case classes for data
case class User(
  id: UserId,
  email: Email,
  name: String,
  createdAt: Instant
)

// ✅ Update with copy
val updatedUser = user.copy(name = "New Name")

// ✅ Use immutable collections
val users: List[User] = List(user1, user2)
val moreUsers = users :+ user3  // Creates new list

// ❌ Avoid mutable state
import scala.collection.mutable
val users = mutable.ListBuffer[User]()  // ❌
```

### Option and Either

```scala
// ✅ Use Option for potentially absent values
def findUser(id: UserId): Option[User] =
  users.get(id)

// ✅ Chain with map/flatMap
val userName: Option[String] =
  findUser(id).map(_.name)

val userEmail: Option[Email] =
  for
    user <- findUser(id)
    profile <- user.profile
    email <- profile.primaryEmail
  yield email

// ✅ Use Either for fallible operations
def parseJson(json: String): Either[ParseError, User] =
  decode[User](json).leftMap(e => ParseError(e.getMessage))

// ✅ Chain Either operations
val result: Either[Error, ProcessedData] =
  for
    user <- parseJson(json)
    validated <- validateUser(user)
    processed <- processUser(validated)
  yield processed

// ✅ Provide defaults with getOrElse
val name = userOpt.map(_.name).getOrElse("Unknown")
```

### Algebraic Data Types

```scala
// ✅ Sealed traits/enums for closed hierarchies
enum Result[+A]:
  case Success(value: A)
  case Failure(error: AppError)

// ✅ Use pattern matching exhaustively
def handle[A](result: Result[A]): String = result match
  case Result.Success(value) => s"Got: $value"
  case Result.Failure(error) => s"Error: ${error.message}"

// ✅ Sealed traits for domain types
sealed trait PaymentMethod
case class CreditCard(number: String, expiry: String) extends PaymentMethod
case class BankTransfer(accountNumber: String) extends PaymentMethod
case object Cash extends PaymentMethod

// ✅ Exhaustive matching compiler check
def process(method: PaymentMethod): Unit = method match
  case CreditCard(num, _) => chargeCard(num)
  case BankTransfer(acc) => initTransfer(acc)
  case Cash => acceptCash()
  // Compiler warns if case missing
```

## Effect Systems

### Cats Effect

```scala
import cats.effect.*
import cats.syntax.all.*

// ✅ Use IO for side effects
def readFile(path: String): IO[String] =
  IO.blocking(scala.io.Source.fromFile(path).mkString)

def writeFile(path: String, content: String): IO[Unit] =
  IO.blocking(java.nio.file.Files.writeString(Path.of(path), content))

// ✅ Compose with flatMap/for-comprehension
def processFile(input: String, output: String): IO[Unit] =
  for
    content <- readFile(input)
    processed = transform(content)
    _ <- writeFile(output, processed)
    _ <- IO.println(s"Processed $input -> $output")
  yield ()

// ✅ Handle errors
def safeRead(path: String): IO[Either[Throwable, String]] =
  readFile(path).attempt

def readWithDefault(path: String, default: String): IO[String] =
  readFile(path).handleError(_ => default)

// ✅ Parallel execution
def fetchAll(ids: List[UserId]): IO[List[User]] =
  ids.parTraverse(fetchUser)

// ✅ Resource management
def withConnection[A](f: Connection => IO[A]): IO[A] =
  Resource.make(IO(createConnection()))(c => IO(c.close())).use(f)
```

### Tagless Final

```scala
// ✅ Abstract over effect type
trait UserRepository[F[_]]:
  def find(id: UserId): F[Option[User]]
  def save(user: User): F[Unit]
  def delete(id: UserId): F[Boolean]

// ✅ Implement for specific effect
class PostgresUserRepository[F[_]: Async](xa: Transactor[F])
    extends UserRepository[F]:
  def find(id: UserId): F[Option[User]] =
    sql"SELECT * FROM users WHERE id = $id"
      .query[User]
      .option
      .transact(xa)

  def save(user: User): F[Unit] =
    sql"INSERT INTO users VALUES (${user.id}, ${user.name})"
      .update
      .run
      .void
      .transact(xa)

// ✅ Program against interface
class UserService[F[_]: Monad](repo: UserRepository[F]):
  def createUser(request: CreateUserRequest): F[User] =
    for
      user <- generateUser(request).pure[F]
      _ <- repo.save(user)
    yield user
```

### ZIO Alternative

```scala
import zio.*

// ✅ ZIO service pattern
trait UserService:
  def find(id: UserId): IO[NotFoundError, User]
  def save(user: User): IO[DatabaseError, Unit]

object UserService:
  def find(id: UserId): ZIO[UserService, NotFoundError, User] =
    ZIO.serviceWithZIO(_.find(id))

// ✅ Layer composition
val live: ZLayer[Database, Nothing, UserService] =
  ZLayer.fromFunction(db => UserServiceLive(db))

// ✅ Error handling
val result: ZIO[UserService, AppError, User] =
  UserService.find(id)
    .mapError(e => AppError.NotFound(e))
    .flatMap(validate)
```

## Error Handling

### Validated for Accumulation

```scala
import cats.data.ValidatedNel
import cats.syntax.all.*

// ✅ Use Validated for error accumulation
case class UserValidation(
  name: String,
  email: String,
  age: Int
)

def validateName(name: String): ValidatedNel[String, String] =
  if name.nonEmpty then name.validNel
  else "Name cannot be empty".invalidNel

def validateEmail(email: String): ValidatedNel[String, Email] =
  if email.contains("@") then Email(email).validNel
  else "Invalid email format".invalidNel

def validateAge(age: Int): ValidatedNel[String, Int] =
  if age >= 0 && age <= 150 then age.validNel
  else "Age must be between 0 and 150".invalidNel

// ✅ Combine validations
def validateUser(input: UserValidation): ValidatedNel[String, User] =
  (validateName(input.name),
   validateEmail(input.email),
   validateAge(input.age)
  ).mapN(User.apply)

// Usage - collects all errors
val result = validateUser(UserValidation("", "invalid", -5))
// Invalid(NonEmptyList("Name cannot be empty", "Invalid email", "Age must be..."))
```

### Custom Error Types

```scala
// ✅ Sealed trait for errors
sealed trait AppError extends Exception:
  def message: String

case class NotFoundError(resource: String, id: String) extends AppError:
  def message = s"$resource not found: $id"

case class ValidationError(errors: NonEmptyList[String]) extends AppError:
  def message = errors.toList.mkString(", ")

case class DatabaseError(cause: Throwable) extends AppError:
  def message = s"Database error: ${cause.getMessage}"

// ✅ Handle errors explicitly
def handleResult[A](result: Either[AppError, A]): IO[Unit] =
  result match
    case Right(value) => IO.println(s"Success: $value")
    case Left(NotFoundError(_, id)) => IO.println(s"Not found: $id")
    case Left(ValidationError(errs)) => IO.println(s"Invalid: ${errs.toList}")
    case Left(DatabaseError(cause)) => IO.raiseError(cause)
```

## Testing

### MUnit with Cats Effect

```scala
import munit.CatsEffectSuite

class UserServiceTest extends CatsEffectSuite:

  test("createUser should save and return user"):
    val mockRepo = new UserRepository[IO]:
      def find(id: UserId) = IO.pure(None)
      def save(user: User) = IO.unit
      def delete(id: UserId) = IO.pure(true)

    val service = UserService(mockRepo)

    for
      user <- service.createUser(CreateUserRequest("test@example.com", "Test"))
    yield
      assertEquals(user.email, "test@example.com")
      assertEquals(user.name, "Test")

  test("findUser should return None for missing user"):
    for
      result <- userService.find(UserId("nonexistent"))
    yield assertEquals(result, None)
```

### Property-Based Testing

```scala
import org.scalacheck.Prop.*
import munit.ScalaCheckSuite

class JsonCodecTest extends ScalaCheckSuite:

  property("roundtrip encoding/decoding"):
    forAll { (user: User) =>
      val encoded = user.asJson.noSpaces
      val decoded = decode[User](encoded)
      decoded == Right(user)
    }

  property("email validation never throws"):
    forAll { (s: String) =>
      val result = Email.parse(s)
      result.isLeft || result.isRight  // Always returns Either
    }

// ✅ Custom generators
given Arbitrary[User] = Arbitrary:
  for
    id <- Gen.uuid.map(_.toString).map(UserId(_))
    name <- Gen.alphaNumStr.suchThat(_.nonEmpty)
    email <- Gen.alphaNumStr.map(s => s"$s@example.com").map(Email(_))
  yield User(id, name, email)
```

## Style Guidelines

### Scalafmt Configuration

```hocon
# .scalafmt.conf
version = 3.8.3
runner.dialect = scala3

maxColumn = 100
indent.defnSite = 2
indent.extendSite = 2

align.preset = more
align.tokens = [
  { code = "=>", owner = "Case" }
  { code = "=", owner = "Enumerator" }
  { code = "%", owner = "Term.ApplyInfix" }
  { code = "%%", owner = "Term.ApplyInfix" }
]

rewrite.rules = [
  RedundantBraces
  RedundantParens
  SortModifiers
  PreferCurlyFors
]

rewrite.scala3.convertToNewSyntax = true
rewrite.scala3.removeOptionalBraces = true

newlines.beforeMultiline = fold
newlines.topLevelStatementBlankLines = [
  { blanks { before = 1, after = 0 } }
]

docstrings.style = SpaceAsterisk
```

### Scalafix Rules

```hocon
# .scalafix.conf
rules = [
  DisableSyntax
  LeakingImplicitClassVal
  NoValInForComprehension
  OrganizeImports
]

DisableSyntax {
  noVars = true
  noThrows = true
  noNulls = true
  noReturns = true
  noAsInstanceOf = true
  noIsInstanceOf = true
}

OrganizeImports {
  groupedImports = Merge
  groups = [
    "re:javax?\\."
    "scala."
    "cats."
    "*"
  ]
}
```

## Code Organization

```scala
// ✅ Order of declarations
class UserService[F[_]: Async](
  repository: UserRepository[F],
  emailService: EmailService[F],
  logger: Logger[F]
):
  // 1. Type aliases and nested types
  private type Result[A] = EitherT[F, AppError, A]

  // 2. Abstract members (in traits)

  // 3. Concrete values
  private val maxRetries = 3

  // 4. Constructors/factories (in companion)

  // 5. Public methods
  def createUser(request: CreateUserRequest): F[Either[AppError, User]] =
    createUserE(request).value

  // 6. Private methods
  private def createUserE(request: CreateUserRequest): Result[User] =
    for
      _ <- validateRequest(request)
      user <- EitherT.liftF(generateUser(request))
      _ <- EitherT.liftF(repository.save(user))
      _ <- EitherT.liftF(emailService.sendWelcome(user.email))
    yield user

object UserService:
  def apply[F[_]: Async](
    repository: UserRepository[F],
    emailService: EmailService[F],
    logger: Logger[F]
  ): UserService[F] =
    new UserService(repository, emailService, logger)
```

## Validation Checklist

```text
□ No vars (use val)
□ No nulls (use Option)
□ No throws (use Either/IO)
□ No return statements
□ No isInstanceOf/asInstanceOf (use pattern matching)
□ Effects tracked in type (IO/ZIO)
□ Errors accumulated with Validated
□ Pure functions preferred
□ Immutable data structures
□ Exhaustive pattern matching
□ scalafmt applied
□ scalafix rules pass
```

## Resources

- [Scala 3 Book](https://docs.scala-lang.org/scala3/book/introduction.html)
- [Typelevel Documentation](https://typelevel.org/)
- [Cats Documentation](https://typelevel.org/cats/)
- [Cats Effect](https://typelevel.org/cats-effect/)
- [ZIO Documentation](https://zio.dev/)
- [Scala Style Guide](https://docs.scala-lang.org/style/)

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…