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
158: Protocol Buffer Code Generation in sbt
I've noticed a recurring pattern when engineers first start integrating Protocol Buffers into their Scala projects: they treat the protoc compiler as a separate, manual chore. You'll see them writing bash scripts or adding "Pre-build" steps in their IDE to run the compiler, then committing the generated Java/Scala source files directly into version control.
The "Manual Compilation" Trap
The misconception here is that Protobuf generation is an external dependency that happens before the build. If you're doing this, your workflow probably looks like this: you change a field in user.proto, you remember to run protoc --scala_out=..., and then you run sbt compile. If you forget that first step, you spend ten minutes scratching your head wondering why your Scala code is referencing a version of the message that doesn't exist yet.
Committing generated code is even worse. It litters your pull requests with thousands of lines of machine-generated boilerplate that no human wants to review, and it inevitably leads to merge conflicts when two people update the schema simultaneously. We want the build tool to handle this, not the developer.
Letting sbt Own the Generation Lifecycle
In a professional Scala setup, we use the sbt-protoc plugin. This integrates the Protocol Buffer compiler directly into the sbt task graph. When you run compile, sbt checks if your .proto files have changed. If they have, it triggers the generator and adds the output directory to your project's source path—all before the Scala compiler even starts looking at your code.
Let's look at a concrete example. Imagine we're building a User Profile service. First, we define our schema in src/main/protobuf/user_profile.proto:
syntax = "proto3";
option java_package = "com.example.user";
option java_outer_classname = "UserProfileProto";
message UserProfile {
int32 id = 1;
string email = 2;
string display_name = 3;
enum Status {
ACTIVE = 0;
SUSPENDED = 1;
DELETED = 2;
}
Status status = 4;
}
To make this work automatically, you first need to add the plugin to your project/plugins.sbt file. I usually stick to the latest stable version:
addSbtPlugin("com.github.sbt" % "sbt-protoc" % "1.0.6")
Now, in your main build.sbt, you don't need to write any complex shell commands. You just need to tell sbt which Protobuf library you're using (usually protobuf-java or scala-pb). If you're using scala-pb for idiomatic Scala code, your configuration looks like this:
libraryDependencies ++= Seq(
"com.thesamet.scalapb" %% "scalapb-runtime" % "0.11.0"
)
// The plugin handles the generation automatically based on
// the presence of files in src/main/protobuf
I'll be honest: the first time you run this, it feels like magic. You run sbt compile, and suddenly the UserProfile class is available in your IDE via autocomplete, even though you never wrote a single line of Scala for it. If you change display_name to full_name in the .proto file and hit compile again, the Scala class updates instantly. No scripts, no manual triggers, and absolutely no generated code in your git history.
📋 Practical Task
Automating the Order Tracking Schema Generation
You are tasked with adding a new OrderTracking schema to an existing sbt project. Instead of generating the code manually, you must configure the project so that the Scala classes are produced automatically during the compilation phase.
- Step 1: Create a file at
src/main/protobuf/order.proto. Define a message calledOrderUpdatewith the following fields:order_id(string),timestamp(int64), andstatus(an enum containingPLACED,SHIPPED, andDELIVERED). - Step 2: Add the
sbt-protocplugin toproject/plugins.sbt. - Step 3: Add the
scalapb-runtimedependency to yourbuild.sbt. - Step 4: Run
sbt compilefrom your terminal. - Step 5: Create a small Scala object in
src/main/scala/Main.scalathat instantiates anOrderUpdatemessage to verify that the generated class is accessible to the Scala compiler.
Success Criteria: The project must compile successfully using only the sbt compile command, without the use of any external protoc shell commands or manually copied source files.
There are no comments for now.