Finagle, Algebra, and Effects: a Type-Safe Recipe for RPC at Scale
Turning Your Service Chain Into Fifty Shades of RPC: a Love Affair Between Cats, Futures, and Filters!
Twitter/X Finagle turns distributed calls into ordinary function calls that compose. It does this with a small set of abstractions and a disciplined layering model that you can pair with algebraic data types, Cats, and Cats-Effect to build RPC that typechecks and scales. In this article I show Finagle’s basics, a minimal HTTP server and client, and a typed Thrift service, which we use here as running examples.
Distributed systems have a reputation for being cold and clinical, all latency numbers and retry budgets. Finagle proves otherwise by showing that RPC can actually be fun. When you squint at a Service[Req, Rep], it looks less like a dusty interface definition and more like a speed dating event for packets: requests slide across the table, futures wink back, and filters play the role of nosey chaperones deciding who gets another round. If you have ever wondered why your requests ghost you, blame the timeout filter - it is the one swiping left on your slow calls.
Finagle’s core idea in one line
A server is a function from request to a future of response, and a client is the same function you call from the other side. The slides highlight this with Service[Req, Rep], Filter, and Twitter Future, then show tiny HTTP server and client snippets that already carry Finagle’s contract. This “your server as a function” framing is the design anchor. Consider the following example.
// Server
import com.twitter.finagle.{Http, Service}
import com.twitter.finagle.http
import com.twitter.util.{Await, Future}
object Server extends App {
val service = new Service[http.Request, http.Response] {
def apply(req: http.Request): Future[http.Response] =
Future.value(http.Response(req.version, http.Status.Ok))
}
Await.ready(Http.serve(”:8080”, service))
}
// Client
object Client extends App {
val client: Service[http.Request, http.Response] = Http.newService(”www.uic.edu:80”)
val req = http.Request(http.Method.Get, “/”); req.host = “www.uic.edu”
val repF: Future[http.Response] = client(req)
Await.result(repF.onSuccess { rep => println(”GET success: “ + rep) })
}

