Does Code Splitting improves start time in Firebase Functions?

Solution 1:

This is a good question.

I think the short answer is "yes". You should optimize functions, their dependencies and any (global) state based on the function's needs.

This gets to the heart of some of the mismatch between "serverless" and reality.

Beneath your Cloud (aka Firebase) Functions there is, of course, a server and a stack of runtimes. The main difference is in how much access you get to the underlying process|runtime. It's important to understand how, what you deploy, is deployed|run.

By way of contrast, App Engine (a pioneering "serverless" platform but one in which you can access more of the runtime) accepts similar units of deployment and, in response to requests, creates processes (instances). The developer is able to conceptualize an instance (of a server runtime), some state (possibly shared across instances) and multiple handlers.

With Cloud (Firebase) Functions, the developer is misled by "serverless" to believe that the service only runs individual functions.

Surprise, it doesn't.

Architecturally, "serverless" really is the same as before. Your code (the unit of what you deploy) is deployed atop the runtime. The difference is that you have less access to the underlying process (instance) and thus less access to the runtime('s capabilities).

IMO Functions-as-a-service is a better term than "serverless" but both mislead.

What does this mean for your question?

When you deploy multiple handlers as a single unit (with varied dependencies), each Cloud (Firebase) Function is an instance of the entire deployment but with only the designated Function exposed.

If you want to optimize, the instance for its designated function, you should minimize the imports per function. The only real consequences of this are that you will create more modules|deployments (one per function including its dependencies).

One important fact is that, given the above, functions can share (global) state. If an instance e.g. creates a Firestore client, or some other shared state, this can be shared across function invocations (across the shared instance).

If you have functions that share global state, it may make sense to keep them combined by instance. For example, if you have a function that gets Documents from Firestore and another function that sets (writes) Documents to Firestore, you could share the Firestore client across multiple (get|set) Functions on the instance. Not only sharing the import but sharing instance state.

You can (and should) prove this to yourself and prove your hypothesis, by splitting your functions across multiple deployments and observing their behavior.