Blame

1b33c4 Melisha Dsouza 2026-01-15 17:28:50 1
# **Scala**
2
3
### **1. General Principles**
4
#### **1.1 Purpose**
5
This document describes Scala coding standards to ensure code consistency, readability, and maintainability. Following these standards helps reduce errors.
6
#### **1.2 Consistency**
7
- In case of discrepancies between this standard and client standards, client standards take priority
8
- Any deviation from the standard must be documented in comments with justification
9
10
### **2. Coding Style**
11
#### **2.1 Line Length**
12
**Rule**: Line length should not exceed 120 characters
13
- Break long expressions across multiple lines
14
- Use appropriate break points (after operators, commas)
15
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 16
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 17
// Good
18
val result = longMethodName(parameter1, parameter2, parameter3) +
19
anotherMethodCall() +
20
yetAnotherCall()
21
22
// Bad
23
val result = longMethodName(parameter1, parameter2, parameter3) + anotherMethodCall() + yetAnotherCall()
24
```
25
#### **2.2 Indentation**
26
**Rule**:
27
- Use 2 spaces for indentation. Tabs are prohibited.
28
- Configure your editor accordingly
29
30
#### **2.3 Braces**
31
**Rule**:
32
- Opening brace stays on the same line as the expression
33
- Closing brace on a separate line
34
**Examples:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 35
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 36
// Good
37
if (condition) {
38
doSomething()
39
}
40
41
try {
42
operation()
43
} catch {
44
case e: Exception => handleError(e)
45
} finally {
46
cleanup()
47
}
48
49
// For single-line expressions without side effects, braces can be omitted
50
if (condition) doSomething()
51
52
// For multi-line or complex expressions - always use braces
53
if (condition) {
54
val result = calculate()
55
process(result)
56
}
57
```
58
#### **2.4 Whitespace**
59
**Rule**:
60
- Operators are surrounded by spaces
61
- Space after commas
62
- No space before colon in type ascription
63
64
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 65
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 66
// Good
67
val sum = a + b
68
val list = List(1, 2, 3)
69
val x: Int = 42
70
71
// Bad
72
val sum=a+b
73
val list = List(1,2,3)
74
val x : Int = 42
75
```
76
77
### **3. Naming**
78
#### **3.1 Naming Styles**
79
**Rules**:
80
- `camelCase` for variables, methods, parameters
81
- `PascalCase` (UpperCamelCase) for classes, traits, objects
82
- `CONSTANT_CASE` for constants (final val)
83
- `snake_case` for filenames and configuration keys
84
85
**Examples**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 86
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 87
// Classes and traits
88
class UserRepository
89
trait DatabaseService
90
91
// Methods and variables
92
def getUserById(id: Long): Future[Option[User]]
93
val maxRetryCount = 3
94
95
// Constants
96
final val DEFAULT_TIMEOUT = 30.seconds
97
final val DATABASE_URL = "jdbc:postgresql://localhost/db"
98
99
// Packages
100
package com.company.project.module
101
```
102
#### **3.2 Method Names**
103
**Recommendations**:
104
- Use verbs for methods that perform actions
105
- Use nouns for methods that return values
106
- Getter methods start with `get` only if there is a corresponding setter
107
- Avoid single-letter names except for conventional ones (`i`, `j`, `k` for indexes)
108
**Examples**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 109
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 110
// Good
111
def calculateTotal(): BigDecimal
112
def save(user: User): Future[User]
113
def isValid: Boolean
114
def user: User // instead of getUser
115
116
// Bad
117
def totalCalculator(): BigDecimal
118
def userSaver(user: User): Future[User]
119
def checkIfValid: Boolean
120
```
121
122
### **4. Code Organization**
123
#### **4.1 File Structure**
124
**Rules**:
125
- One main class/trait/object per file
126
- Filename should match the main class name (PascalCase)
127
- Try to keep file size within 500-1000 lines
128
129
#### **4.2 Declaration Order**
130
**Recommendations**:
131
1. package declaration
132
2. Imports
133
3. Class/trait/object
134
4. Companion objects
135
5. Nested classes
136
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 137
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 138
package com.company.service
139
140
import scala.concurrent.{Future, ExecutionContext}
141
import java.time.LocalDateTime
142
143
class UserService {
144
// fields
145
// methods
146
}
147
148
object UserService {
149
// static methods
150
}
151
```
152
153
#### **4.3 Imports**
154
**Rules**:
155
- Group imports: first Scala standard library, then Java, then third-party libraries, then own packages
156
- Use curly braces for importing multiple elements from the same package
157
- Avoid wildcard imports (`import package._`) except in tests
158
159
**Example:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 160
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 161
import scala.concurrent.{Future, ExecutionContext}
162
import scala.util.{Try, Success, Failure}
163
164
import java.time.LocalDateTime
165
import java.util.concurrent.TimeUnit
166
167
import akka.actor.ActorSystem
168
import com.typesafe.scalalogging.LazyLogging
169
170
import model.User
171
import repository.UserRepository
172
```
173
174
### **5. Types and Variables**
175
#### **5.1 Variable Declaration**
176
**Rules**:
177
- Use `val` by default, `var` only when necessary
178
- Explicitly specify types for public API
179
- Local variables can use type inference
180
181
**Examples:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 182
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 183
// Good
184
val userName: String = "John"
185
val count = calculateCount() // type inference acceptable for local variables
186
187
// Only when necessary
188
var retryCount = 0
189
190
// Bad
191
var temporaryValue = compute()
192
```
193
194
#### **5.2 Null Safety**
195
**Rule**:
196
- Avoid using `null`. Use `Option` for nullable values.
197
198
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 199
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 200
// Good
201
def findUser(id: Long): Option[User]
202
val maybeUser: Option[User] = findUser(123)
203
204
// Bad
205
def findUser(id: Long): User = {
206
// may return null
207
}
208
```
209
210
### **6. Functions and Methods**
211
#### **6.1 Method Signatures**
212
**Recommendations**:
213
- Limit number of parameters (maximum 5-7)
214
- Use case classes for grouping related parameters
215
- Explicitly specify return types for public methods
216
217
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 218
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 219
// Good
220
case class SearchCriteria(query: String, limit: Int, offset: Int)
221
222
def searchUsers(criteria: SearchCriteria): Future[Seq[User]]
223
224
// Bad
225
def searchUsers(query: String, limit: Int, offset: Int,
226
sortBy: String, ascending: Boolean,
227
includeInactive: Boolean): Future[Seq[User]]
228
```
229
230
#### **6.2 Functional Style**
231
**Rules**:
232
- Prefer immutable collections
233
- Use `map`, `flatMap`, `filter` instead of loops
234
- Avoid side effects in pure functions
235
236
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 237
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 238
// Good
239
val activeUsers = users.filter(_.isActive).map(_.toDto)
240
241
// Bad
242
var activeUsers = List.empty[UserDto]
243
for (user <- users) {
244
if (user.isActive) {
245
activeUsers = activeUsers :+ user.toDto
246
}
247
}
248
```
249
250
### **7. Error Handling**
251
#### **7.1 Using Try/Either/Future**
252
**Rules**:
253
- Use `Try` for synchronous operations that may throw exceptions
254
- Use `Either` for operations with domain-specific errors
255
- Use `Future` for asynchronous operations
256
257
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 258
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 259
// With Try
260
def parseNumber(s: String): Try[Int] = Try(s.toInt)
261
262
// With Either
263
def validateUser(user: User): Either[String, User] = {
264
if (user.name.isEmpty) Left("Name cannot be empty")
265
else Right(user)
266
}
267
268
// With Future
269
def fetchUser(id: Long): Future[User] = {
270
// asynchronous operation
271
}
272
```
273
274
#### **7.2 Exceptions**
275
**Rules**:
276
- Use specific exception types for domain errors
277
- Log exceptions only at the level where they are handled
278
- Preserve stack trace when wrapping exceptions
279
280
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 281
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 282
class UserNotFoundException(id: Long)
283
extends RuntimeException(s"User with id $id not found")
284
285
try {
286
userRepository.findById(userId)
287
} catch {
288
case e: UserNotFoundException =>
289
logger.error(s"User not found: $userId", e)
290
Left(ResponseStatus.UserNotFound)
291
case NonFatal(e) =>
292
logger.error("Unexpected error", e)
293
Left(ResponseStatus.InternalServerError)
294
}
295
```
296
297
### **8. Comments**
298
#### **8.1 Documentation Comments**
299
**Rules**:
300
- Use Scaladoc for public API
301
- Comment complex business logic
302
- Avoid comments that duplicate code
303
304
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 305
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 306
/**
307
* Repository for working with users.
308
*
309
* @param db database connection
310
* @param ec execution context
311
*/
312
class UserRepository(db: Database)(implicit ec: ExecutionContext) {
313
314
/**
315
* Finds user by identifier.
316
*
317
* @param id user identifier
318
* @return Future with optional user
319
*/
320
def findById(id: Long): Future[Option[User]] = {
321
// implementation
322
}
323
}
324
```
325
#### **8.2 TODO and FIXME**
326
**Recommendations**:
327
- Use standard tags: `TODO`, `FIXME`, `NOTE`
328
- Include context and date
329
330
**Example**:
331
```
332
// TODO: Optimize query for large number of users (2024-01-15)
333
// FIXME: Handle edge case with empty search string
334
// NOTE: This method is used in reports, change with caution
335
```
336
337
### **9. Testing**
338
#### **9.1 Test Structure**
339
**Rules**:
340
- Test classes are named as `ClassNameSpec` or `ClassNameTest`
341
- Use readable test names
342
- Group related tests using `describe`/`it`
343
344
**Example**:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 345
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 346
class UserServiceSpec extends AnyFlatSpec with Matchers {
347
348
"UserService" should "return user by id" in {
349
// test
350
}
351
352
it should "return None for non-existent user" in {
353
// test
354
}
355
356
"save method" should "persist user to database" in {
357
// test
358
}
359
}
360
```
361
#### **9.2 Mocking**
362
**Recommendations**:
363
- Use mocks only for external dependencies
364
- Prefer real implementations where possible
365
- Use dependency injection for testability
366
367
### **10. Performance and Optimization**
368
#### **10.1 Principles**
369
**Rule**:
370
- First write readable and correct code, optimize only when performance issues are proven.
371
372
#### **10.2 Common Pitfalls**
373
**Avoid**:
374
- Excessive copying of large structures
375
- N+1 database queries
376
- Blocking operations in asynchronous context
377
378
### **11. Tools and Automation**
379
#### **11.1 Formatting**
380
Use Scalafmt for automatic code formatting. Configuration `.scalafmt.conf`:
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 381
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 382
version = "3.7.14"
383
maxColumn = 120
384
continuationIndent.defnSite = 2
385
align.preset = some
386
rewrite.rules = [SortImports]
387
```
388
389
#### **11.2 Static Analysis**
390
**Configure the following tools:**
391
- Scalafix for refactoring
392
- WartRemover for checking best practices
393
- Scapegoat for identifying potential issues
394
395
### **Appendix A: Examples**
396
#### **A.1 Good Practices from the Code**
397
**Using dependency injection:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 398
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 399
class FuelGaugesRoutes @Inject()(
400
system: ActorSystem,
401
userRepository: UserRepository,
402
fuelGaugeRepository: FuelGaugeRepository,
403
@Named("restActor") apiActor: ActorRef,
404
config: Config
405
)(implicit @Named("global") executionContext: ExecutionContext)
406
```
407
**Using type classes:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 408
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 409
// Type class as trait with parameter
410
trait JsonWriter[A] {
411
def write(value: A): Json
412
}
413
414
// simple introduction JSON
415
sealed trait Json
416
case class JsString(value: String) extends Json
417
case class JsNumber(value: Double) extends Json
418
case class JsObject(fields: Map[String, Json]) extends Json
419
420
object JsonWriterInstances {
421
// Instance for String
422
implicit val stringWriter: JsonWriter[String] =
423
(value: String) => JsString(value)
424
425
// Instance for Int
426
implicit val intWriter: JsonWriter[Int] =
427
(value: Int) => JsNumber(value.toDouble)
428
429
// Instance for Person
430
case class Person(name: String, age: Int)
431
432
implicit val personWriter: JsonWriter[Person] =
433
(person: Person) => JsObject(Map(
434
"name" -> JsString(person.name),
435
"age" -> JsNumber(person.age)
436
))
437
438
// Instance for Option (recursion instance)
439
implicit def optionWriter[A](implicit writer: JsonWriter[A]): JsonWriter[Option[A]] =
440
(option: Option[A]) => option match {
441
case Some(value) => writer.write(value)
442
case None => JsString("null")
443
}
444
}
445
446
object Json {
447
def toJson[A](value: A)(implicit writer: JsonWriter[A]): Json =
448
writer.write(value)
449
}
450
451
// alternative syntax with context bound
452
object JsonSyntax {
453
implicit class JsonWriterOps[A](value: A) {
454
def toJson(implicit writer: JsonWriter[A]): Json =
455
writer.write(value)
456
}
457
}
458
```
459
460
**Structuring configuration:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 461
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 462
lazy val commonSettings = Seq[Def.SettingsDefinition](
463
scalaVersion := "2.13.16",
464
version := releaseVersion,
465
organization := "dataroot"
466
)
467
```
468
469
#### **A.2 Areas for Improvement**
470
**Avoid overly long lines:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 471
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 472
// Instead of:
473
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]))
474
475
// Break into:
476
val all = endpoint(
477
request(
478
Get,
479
root /? (qs[Option[String]]("query") &
480
qs[String]("sort_by") &
481
qs[String]("asc") &
482
qs[Int]("pageNumber") &
483
qs[Int]("rowsNumber") /*& optQs[String]("filterParams")*/)
484
),
485
ok(jsonResponse[ResponseStatus Either GetResponse])
486
)
487
488
```
489
**Use explicit return types for public API:**
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 490
```Scala
1b33c4 Melisha Dsouza 2026-01-15 17:28:50 491
// Instead of:
492
def findAll2(user: User, parameters: GetParameters) = {
493
// implementation
494
}
495
496
// Use:
497
def findAll2(user: User, parameters: GetParameters): Future[ResponseStatus Either GetResponse] = {
498
// implementation
499
}
500
501
```
502
503
**Appendix B: Code Review Checklist**
504
- Compliance with naming standards
505
- Line length does not exceed 120 characters
506
- Correct indentation (2 spaces)
507
- Explicit types for public API
508
- No `null` and minimal use of `var`
509
- Error handling via `Try`/`Either`/`Future`
510
- Tests for new functionality
511
- No commented-out code
512
- Current TODO/FIXME with context
513
- Correct Scaladoc comments for public API
514
- Compliance with functional programming principles where appropriate
515
- No blocking operations in asynchronous context
516
- Efficient use of collections (without excessive copying)
8f5cc2 Melisha Dsouza 2026-01-16 10:12:49 517
518
**Document** - [Scala](https://docs.google.com/document/d/1gxq5pJsQ4cc5iDnf3h4ynMcpyM4A_sEUeKn9sNRjUec/edit?tab=t.0#heading=h.coq5eefrj3q)