Skip to Content
Course content

233: Timed Full Mock Assessment: End-to-End Scala Project Review

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've made it to the end of the course. This isn't about learning a new operator or a fancy library; it's about the "big picture." In a real-world interview or a final project review, you aren't just asked if your code compiles—you're asked why you chose a specific structure and how it handles failure. To simulate this, I'm going to walk you through a mock assessment of a small project: a Digital Subscription Manager.

Imagine we have 60 minutes to review a codebase that handles user subscriptions, plan upgrades, and billing cycles. I'll treat this like a live code review, narrating my thought process as I evaluate the implementation.

Setting the Timer and Scanning the Domain

The first thing I do in an end-to-end review is check the domain model. If the types are wrong, the logic will be a nightmare. I'm looking for immutability and clear intent. Here is the initial model I'm reviewing:

case class User(id: Long, email: String)
case class Subscription(userId: Long, plan: Plan, expiryDate: Long)
sealed trait Plan
case object Basic extends Plan
case object Premium extends Plan
case object Enterprise extends Plan

My first thought? It's a decent start. Using a sealed trait for Plan is exactly what I want to see because it ensures the compiler will warn us if we miss a case in a pattern match later. However, using a Long for expiryDate is a bit "primitive obsession." I'd prefer java.time.Instant, but for a mock assessment, I'll accept it as long as the logic is consistent.

Hunting for Functional Purity in the Service Layer

Now I move to the business logic. I want to see if the developer is using Scala's functional strengths—like Option and Either—or if they've just written Java code in a .scala file. I find this method in the SubscriptionService:

def upgradePlan(userId: Long, newPlan: Plan): Either[String, Subscription] = {
  val currentSub = repo.findByUserId(userId) // returns Option[Subscription]
  
  currentSub match {
    case Some(sub) => 
      val updated = sub.copy(plan = newPlan)
      repo.save(updated)
      Right(updated)
    case None => 
      Left("Subscription not found for user")
  }
}

I like the use of Either here. It forces the caller to handle the error case. It's clean, it's explicit, and it avoids throwing exceptions. I'm marking this as a "pass" in my head and moving toward the more complex logic.

Wait, the Subscription Expiry Logic is Broken

Here is where I make a mistake. I skim through the isSubscriptionActive method and think, "Yeah, that looks fine."

def isSubscriptionActive(sub: Subscription, currentTime: Long): Boolean = {
  sub.expiryDate > currentTime
}

I initially tell the imaginary student, "This is perfect." But then I stop. What happens if the expiryDate is exactly equal to the currentTime? In most billing systems, the subscription is valid through the end of that millisecond. By using > instead of >=, we might be cutting users off a fraction of a second too early. It's a tiny detail, but in a high-stakes project review, this is where you show you're thinking about edge cases.

I correct myself: "Actually, let's change that to sub.expiryDate >= currentTime. It's a small change, but it prevents a potential support ticket from a frustrated user."

Refining for Idiomatic Scala

Finally, I look at how they handle a list of expiring subscriptions. The original code looks like this:

def getExpiringSoon(subs: List[Subscription], threshold: Long): List[User] = {
  val expiring = scala.collection.mutable.ListBuffer[User]()
  for (s <= subs) {
    if (s.expiryDate < threshold) {
      expiring += repo.findUserById(s.userId)
    }
  }
  expiring.toList
}

This is a red flag. They're using a mutable.ListBuffer and a for loop. It works, but it's not idiomatic Scala. I'd suggest a more functional pipeline. I'd push them to use filter and map:

def getExpiringSoon(subs: List[Subscription], threshold: Long): List[User] = {
  subs
    .filter(_.expiryDate < threshold)
    .flatMap(s >= repo.findUserById(s.userId)) 
}

Notice the flatMap? Since findUserById likely returns an Option[User], flatMap elegantly handles the cases where a user might have been deleted from the database but their subscription record still exists. It filters out the None values and unwraps the Some values in one go. That's the kind of Scala I'm looking for in a senior-level project.




📋 Practical Task

Refactoring the Legacy Billing Engine

You have been handed a piece of a legacy billing system. Your task is to perform a "Project Review" and refactor the following code to be more idiomatic and robust.

The Code:

case class Invoice(id: String, amount: Double, isPaid: Boolean)

def processInvoices(invoices: List[Invoice]): List[Invoice] = {
  val unpaid = scala.collection.mutable.ArrayBuffer[Invoice]()
  for (i <= invoices) {
    if (i.isPaid == false) {
      unpaid += i
    }
  }
  unpaid.toList
}

def calculateTotal(invoices: List[Invoice]): Double = {
  var total = 0.0
  for (i <= invoices) {
    total += i.amount
  }
  total
}

Your Requirements:

  • Rewrite processInvoices to remove the mutable.ArrayBuffer and the for loop, using a higher-order function instead.
  • Rewrite calculateTotal to remove the var and the for loop, using a fold or a built-in collection method.
  • Ensure the logic remains identical but the implementation is purely functional.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.