Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
84: sbt Build Definition In Depth
Wait, is build.sbt actually a Scala file or just a config file?
This is the first thing that trips everyone up. It looks like a properties file, but it's actually a Scala DSL. When sbt starts up, it evaluates the build.sbt file as Scala code to determine the build graph. This is why you can use variables, loops, and even conditional logic inside your build definition.
I've seen a lot of developers hard-code the same version number in five different places. Since this is just Scala, don't do that. Define a variable at the top. It makes upgrading your dependencies a one-line change instead of a search-and-replace nightmare.
val akkaVersion = "2.6.20" libraryDependencies ++= Seq( "com.typesafe.akka" %% "akka-actor-typed" % akkaVersion, "com.typesafe.akka" %% "akka-stream" % akkaVersion )How do I split my project into separate modules without creating five different repos?
As your project grows—say you're building a payment processing system—you don't want your API logic mixed in with your database schemas and your core domain models. You want a multi-project build. This allows you to keep everything in one repository but maintain strict boundaries between components.
You define these as
projectobjects. The key here is the.dependsOn()method, which tells sbt exactly which modules need to be compiled first. Ifpayment-apidepends onpayment-core, sbt is smart enough to handle the ordering.lazy val core = (project in file("core")) .settings( name := "payment-core", libraryDependencies += "org.typelevel" %% "cats-core" % "2.9.0" ) lazy val api = (project in file("api")) .dependsOn(core) .settings( name := "payment-api", libraryDependencies += "com.typesafe.play" %% "play-json" % "2.9.0" ) lazy val root = (project in file(".")) .aggregate(core, api)Note that
aggregateis different fromdependsOn. Aggregation just means "when I runcompileon the root, also compile these other projects," but it doesn't create a classpath dependency.I keep seeing "Settings" and "Tasks" mentioned—what's the actual difference?
I like to think of it this way: a Setting is a value; a Task is an action. Settings are generally static (or calculated once), whereas Tasks are things you actually "run" from the sbt shell.
scalaVersion := "2.13.12"is a Setting. You're telling sbt what version to use.compileis a Task. You're telling sbt to do the act of compiling.
The confusing part is that they both use the := and += operators. If you're ever unsure, ask yourself: "Am I describing a property of my project, or am I triggering a process?" If it's a process, it's a Task.
Can I write my own sbt commands to automate boring stuff?
Absolutely. This is where sbt becomes powerful. You can define custom tasks to handle things like generating documentation, cleaning up specific temp folders, or validating a config file before deployment. To do this, you define a TaskKey and then assign a block of code to it.
Here is a quick example of a task that checks if a required secrets.conf file exists in your project root. It's a simple check, but it saves you from a runtime crash in CI/CD.
val validateConfig = taskKey[Unit]("Checks if secrets.conf exists") validateConfig := { val file = file("secrets.conf") if (file.exists()) { println("Config found. Ready to go!") } else { sys.error("Missing secrets.conf! Please create it from secrets.conf.example") } }Now, you can just type
validateConfigin your sbt shell, and it'll run that logic. It's a huge time-saver when onboarding new developers to a team.
📋 Practical Task
Exercise: Implementing a Multi-Module Build for a Payment Gateway
You are tasked with restructuring a monolithic payment application into a multi-project build to improve build times and separation of concerns. Create a build.sbt file that satisfies the following requirements:
- Define a root project that aggregates two sub-projects:
domainandinfrastructure. - The
domainproject should be located in thedomain/directory and have a dependency onorg.typelevel %% "cats-core" % "2.9.0". - The
infrastructureproject should be located in theinfrastructure/directory, depend on thedomainproject, and have a dependency oncom.typesafe.akka %% "akka-actor-typed" % "2.6.20". - Define a custom task called
checkBuildHealththat simply prints "Build structure is valid" to the console.
There are no comments for now.