Commit 1b33c4

2026-01-15 17:28:50 Melisha Dsouza: Scala
/dev/null .. CMMI/Guidelines/Coding Standard/Scala.md
@@ 0,0 1,516 @@
+ # **Scala**
+
+ ### **1. General Principles**
+ #### **1.1 Purpose**
+ This document describes Scala coding standards to ensure code consistency, readability, and maintainability. Following these standards helps reduce errors.
+ #### **1.2 Consistency**
+ - In case of discrepancies between this standard and client standards, client standards take priority
+ - Any deviation from the standard must be documented in comments with justification
+
+ ### **2. Coding Style**
+ #### **2.1 Line Length**
+ **Rule**: Line length should not exceed 120 characters
+ - Break long expressions across multiple lines
+ - Use appropriate break points (after operators, commas)
+ **Example**:
+ ```
+ // Good
+ val result = longMethodName(parameter1, parameter2, parameter3) +
+ anotherMethodCall() +
+ yetAnotherCall()
+
+ // Bad
+ val result = longMethodName(parameter1, parameter2, parameter3) + anotherMethodCall() + yetAnotherCall()
+ ```
+ #### **2.2 Indentation**
+ **Rule**:
+ - Use 2 spaces for indentation. Tabs are prohibited.
+ - Configure your editor accordingly
+
+ #### **2.3 Braces**
+ **Rule**:
+ - Opening brace stays on the same line as the expression
+ - Closing brace on a separate line
+ **Examples:**
+ ```
+ // Good
+ if (condition) {
+ doSomething()
+ }
+
+ try {
+ operation()
+ } catch {
+ case e: Exception => handleError(e)
+ } finally {
+ cleanup()
+ }
+
+ // For single-line expressions without side effects, braces can be omitted
+ if (condition) doSomething()
+
+ // For multi-line or complex expressions - always use braces
+ if (condition) {
+ val result = calculate()
+ process(result)
+ }
+ ```
+ #### **2.4 Whitespace**
+ **Rule**:
+ - Operators are surrounded by spaces
+ - Space after commas
+ - No space before colon in type ascription
+
+ **Example**:
+ ```
+ // Good
+ val sum = a + b
+ val list = List(1, 2, 3)
+ val x: Int = 42
+
+ // Bad
+ val sum=a+b
+ val list = List(1,2,3)
+ val x : Int = 42
+ ```
+
+ ### **3. Naming**
+ #### **3.1 Naming Styles**
+ **Rules**:
+ - `camelCase` for variables, methods, parameters
+ - `PascalCase` (UpperCamelCase) for classes, traits, objects
+ - `CONSTANT_CASE` for constants (final val)
+ - `snake_case` for filenames and configuration keys
+
+ **Examples**:
+ ```
+ // Classes and traits
+ class UserRepository
+ trait DatabaseService
+
+ // Methods and variables
+ def getUserById(id: Long): Future[Option[User]]
+ val maxRetryCount = 3
+
+ // Constants
+ final val DEFAULT_TIMEOUT = 30.seconds
+ final val DATABASE_URL = "jdbc:postgresql://localhost/db"
+
+ // Packages
+ package com.company.project.module
+ ```
+ #### **3.2 Method Names**
+ **Recommendations**:
+ - Use verbs for methods that perform actions
+ - Use nouns for methods that return values
+ - Getter methods start with `get` only if there is a corresponding setter
+ - Avoid single-letter names except for conventional ones (`i`, `j`, `k` for indexes)
+ **Examples**:
+ ```
+ // Good
+ def calculateTotal(): BigDecimal
+ def save(user: User): Future[User]
+ def isValid: Boolean
+ def user: User // instead of getUser
+
+ // Bad
+ def totalCalculator(): BigDecimal
+ def userSaver(user: User): Future[User]
+ def checkIfValid: Boolean
+ ```
+
+ ### **4. Code Organization**
+ #### **4.1 File Structure**
+ **Rules**:
+ - One main class/trait/object per file
+ - Filename should match the main class name (PascalCase)
+ - Try to keep file size within 500-1000 lines
+
+ #### **4.2 Declaration Order**
+ **Recommendations**:
+ 1. package declaration
+ 2. Imports
+ 3. Class/trait/object
+ 4. Companion objects
+ 5. Nested classes
+ **Example**:
+ ```
+ package com.company.service
+
+ import scala.concurrent.{Future, ExecutionContext}
+ import java.time.LocalDateTime
+
+ class UserService {
+ // fields
+ // methods
+ }
+
+ object UserService {
+ // static methods
+ }
+ ```
+
+ #### **4.3 Imports**
+ **Rules**:
+ - Group imports: first Scala standard library, then Java, then third-party libraries, then own packages
+ - Use curly braces for importing multiple elements from the same package
+ - Avoid wildcard imports (`import package._`) except in tests
+
+ **Example:**
+ ```
+ import scala.concurrent.{Future, ExecutionContext}
+ import scala.util.{Try, Success, Failure}
+
+ import java.time.LocalDateTime
+ import java.util.concurrent.TimeUnit
+
+ import akka.actor.ActorSystem
+ import com.typesafe.scalalogging.LazyLogging
+
+ import model.User
+ import repository.UserRepository
+ ```
+
+ ### **5. Types and Variables**
+ #### **5.1 Variable Declaration**
+ **Rules**:
+ - Use `val` by default, `var` only when necessary
+ - Explicitly specify types for public API
+ - Local variables can use type inference
+
+ **Examples:**
+ ```
+ // Good
+ val userName: String = "John"
+ val count = calculateCount() // type inference acceptable for local variables
+
+ // Only when necessary
+ var retryCount = 0
+
+ // Bad
+ var temporaryValue = compute()
+ ```
+
+ #### **5.2 Null Safety**
+ **Rule**:
+ - Avoid using `null`. Use `Option` for nullable values.
+
+ **Example**:
+ ```
+ // Good
+ def findUser(id: Long): Option[User]
+ val maybeUser: Option[User] = findUser(123)
+
+ // Bad
+ def findUser(id: Long): User = {
+ // may return null
+ }
+ ```
+
+ ### **6. Functions and Methods**
+ #### **6.1 Method Signatures**
+ **Recommendations**:
+ - Limit number of parameters (maximum 5-7)
+ - Use case classes for grouping related parameters
+ - Explicitly specify return types for public methods
+
+ **Example**:
+ ```
+ // Good
+ case class SearchCriteria(query: String, limit: Int, offset: Int)
+
+ def searchUsers(criteria: SearchCriteria): Future[Seq[User]]
+
+ // Bad
+ def searchUsers(query: String, limit: Int, offset: Int,
+ sortBy: String, ascending: Boolean,
+ includeInactive: Boolean): Future[Seq[User]]
+ ```
+
+ #### **6.2 Functional Style**
+ **Rules**:
+ - Prefer immutable collections
+ - Use `map`, `flatMap`, `filter` instead of loops
+ - Avoid side effects in pure functions
+
+ **Example**:
+ ```
+ // Good
+ val activeUsers = users.filter(_.isActive).map(_.toDto)
+
+ // Bad
+ var activeUsers = List.empty[UserDto]
+ for (user <- users) {
+ if (user.isActive) {
+ activeUsers = activeUsers :+ user.toDto
+ }
+ }
+ ```
+
+ ### **7. Error Handling**
+ #### **7.1 Using Try/Either/Future**
+ **Rules**:
+ - Use `Try` for synchronous operations that may throw exceptions
+ - Use `Either` for operations with domain-specific errors
+ - Use `Future` for asynchronous operations
+
+ **Example**:
+ ```
+ // With Try
+ def parseNumber(s: String): Try[Int] = Try(s.toInt)
+
+ // With Either
+ def validateUser(user: User): Either[String, User] = {
+ if (user.name.isEmpty) Left("Name cannot be empty")
+ else Right(user)
+ }
+
+ // With Future
+ def fetchUser(id: Long): Future[User] = {
+ // asynchronous operation
+ }
+ ```
+
+ #### **7.2 Exceptions**
+ **Rules**:
+ - Use specific exception types for domain errors
+ - Log exceptions only at the level where they are handled
+ - Preserve stack trace when wrapping exceptions
+
+ **Example**:
+ ```
+ class UserNotFoundException(id: Long)
+ extends RuntimeException(s"User with id $id not found")
+
+ try {
+ userRepository.findById(userId)
+ } catch {
+ case e: UserNotFoundException =>
+ logger.error(s"User not found: $userId", e)
+ Left(ResponseStatus.UserNotFound)
+ case NonFatal(e) =>
+ logger.error("Unexpected error", e)
+ Left(ResponseStatus.InternalServerError)
+ }
+ ```
+
+ ### **8. Comments**
+ #### **8.1 Documentation Comments**
+ **Rules**:
+ - Use Scaladoc for public API
+ - Comment complex business logic
+ - Avoid comments that duplicate code
+
+ **Example**:
+ ```
+ /**
+ * Repository for working with users.
+ *
+ * @param db database connection
+ * @param ec execution context
+ */
+ class UserRepository(db: Database)(implicit ec: ExecutionContext) {
+
+ /**
+ * Finds user by identifier.
+ *
+ * @param id user identifier
+ * @return Future with optional user
+ */
+ def findById(id: Long): Future[Option[User]] = {
+ // implementation
+ }
+ }
+ ```
+ #### **8.2 TODO and FIXME**
+ **Recommendations**:
+ - Use standard tags: `TODO`, `FIXME`, `NOTE`
+ - Include context and date
+
+ **Example**:
+ ```
+ // TODO: Optimize query for large number of users (2024-01-15)
+ // FIXME: Handle edge case with empty search string
+ // NOTE: This method is used in reports, change with caution
+ ```
+
+ ### **9. Testing**
+ #### **9.1 Test Structure**
+ **Rules**:
+ - Test classes are named as `ClassNameSpec` or `ClassNameTest`
+ - Use readable test names
+ - Group related tests using `describe`/`it`
+
+ **Example**:
+ ```
+ class UserServiceSpec extends AnyFlatSpec with Matchers {
+
+ "UserService" should "return user by id" in {
+ // test
+ }
+
+ it should "return None for non-existent user" in {
+ // test
+ }
+
+ "save method" should "persist user to database" in {
+ // test
+ }
+ }
+ ```
+ #### **9.2 Mocking**
+ **Recommendations**:
+ - Use mocks only for external dependencies
+ - Prefer real implementations where possible
+ - Use dependency injection for testability
+
+ ### **10. Performance and Optimization**
+ #### **10.1 Principles**
+ **Rule**:
+ - First write readable and correct code, optimize only when performance issues are proven.
+
+ #### **10.2 Common Pitfalls**
+ **Avoid**:
+ - Excessive copying of large structures
+ - N+1 database queries
+ - Blocking operations in asynchronous context
+
+ ### **11. Tools and Automation**
+ #### **11.1 Formatting**
+ Use Scalafmt for automatic code formatting. Configuration `.scalafmt.conf`:
+ ```
+ version = "3.7.14"
+ maxColumn = 120
+ continuationIndent.defnSite = 2
+ align.preset = some
+ rewrite.rules = [SortImports]
+ ```
+
+ #### **11.2 Static Analysis**
+ **Configure the following tools:**
+ - Scalafix for refactoring
+ - WartRemover for checking best practices
+ - Scapegoat for identifying potential issues
+
+ ### **Appendix A: Examples**
+ #### **A.1 Good Practices from the Code**
+ **Using dependency injection:**
+ ```
+ class FuelGaugesRoutes @Inject()(
+ system: ActorSystem,
+ userRepository: UserRepository,
+ fuelGaugeRepository: FuelGaugeRepository,
+ @Named("restActor") apiActor: ActorRef,
+ config: Config
+ )(implicit @Named("global") executionContext: ExecutionContext)
+ ```
+ **Using type classes:**
+ ```
+ // Type class as trait with parameter
+ trait JsonWriter[A] {
+ def write(value: A): Json
+ }
+
+ // simple introduction JSON
+ sealed trait Json
+ case class JsString(value: String) extends Json
+ case class JsNumber(value: Double) extends Json
+ case class JsObject(fields: Map[String, Json]) extends Json
+
+ object JsonWriterInstances {
+ // Instance for String
+ implicit val stringWriter: JsonWriter[String] =
+ (value: String) => JsString(value)
+
+ // Instance for Int
+ implicit val intWriter: JsonWriter[Int] =
+ (value: Int) => JsNumber(value.toDouble)
+
+ // Instance for Person
+ case class Person(name: String, age: Int)
+
+ implicit val personWriter: JsonWriter[Person] =
+ (person: Person) => JsObject(Map(
+ "name" -> JsString(person.name),
+ "age" -> JsNumber(person.age)
+ ))
+
+ // Instance for Option (recursion instance)
+ implicit def optionWriter[A](implicit writer: JsonWriter[A]): JsonWriter[Option[A]] =
+ (option: Option[A]) => option match {
+ case Some(value) => writer.write(value)
+ case None => JsString("null")
+ }
+ }
+
+ object Json {
+ def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
+ writer.write(value)
+ }
+
+ // alternative syntax with context bound
+ object JsonSyntax {
+ implicit class JsonWriterOps[A](value: A) {
+ def toJson(implicit writer: JsonWriter[A]): Json =
+ writer.write(value)
+ }
+ }
+ ```
+
+ **Structuring configuration:**
+ ```
+ lazy val commonSettings = Seq[Def.SettingsDefinition](
+ scalaVersion := "2.13.16",
+ version := releaseVersion,
+ organization := "dataroot"
+ )
+ ```
+
+ #### **A.2 Areas for Improvement**
+ **Avoid overly long lines:**
+ ```
+ // Instead of:
+ val all = endpoint(request(Get, root /? (qs[Option[String]]("query") & qs[String]("sort_by") & qs[String]("asc") & qs[Int]("pageNumber") & qs[Int]("rowsNumber") /*& optQs [String]("filterParams")*/)), ok(jsonResponse[ResponseStatus Either GetResponse]))
+
+ // Break into:
+ val all = endpoint(
+ request(
+ Get,
+ root /? (qs[Option[String]]("query") &
+ qs[String]("sort_by") &
+ qs[String]("asc") &
+ qs[Int]("pageNumber") &
+ qs[Int]("rowsNumber") /*& optQs[String]("filterParams")*/)
+ ),
+ ok(jsonResponse[ResponseStatus Either GetResponse])
+ )
+
+ ```
+ **Use explicit return types for public API:**
+ ```
+ // Instead of:
+ def findAll2(user: User, parameters: GetParameters) = {
+ // implementation
+ }
+
+ // Use:
+ def findAll2(user: User, parameters: GetParameters): Future[ResponseStatus Either GetResponse] = {
+ // implementation
+ }
+
+ ```
+
+ **Appendix B: Code Review Checklist**
+ - Compliance with naming standards
+ - Line length does not exceed 120 characters
+ - Correct indentation (2 spaces)
+ - Explicit types for public API
+ - No `null` and minimal use of `var`
+ - Error handling via `Try`/`Either`/`Future`
+ - Tests for new functionality
+ - No commented-out code
+ - Current TODO/FIXME with context
+ - Correct Scaladoc comments for public API
+ - Compliance with functional programming principles where appropriate
+ - No blocking operations in asynchronous context
+ - Efficient use of collections (without excessive copying)
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9