How granular in purpose should FaaS functions be? For clarification, when I say "FaaS" I am referring to serverless offerings such as AWS Lambda and Google Cloud Functions. 

I have a specific case in mind that I'll describe but I would very much like to hear about any guiding principles in this area as well.

I have a Google Cloud Function that is currently doing some image processing. The flow looks like this: 


JSON file containing several base64 encoded images is uploaded to storage bucket
Cloud Fucntion downloads file
Converts and merges all base64 strings into image file
Uploads image file to storage bucket
Calls Google Cloud Vision API once file is uploaded


This works, but it has me wondering: how much should a single function do? Most discussions I see regarding single purpose vs. monolithic functions are centered around monolithic functions having logic branches e.g. "if-else". In the above case, this is an example of doing a lot, but, it's conceptually a single unit of "work". 

Were I to refactor this into true single purpose functions, I imagine I'd have two functions -- one for downloading and merging images and the other for calling the Vision API. This splits what I consider "image processing" into two sub-units. I have no problem doing this but I'm curious to know whats a good rule-of-thumb for "splitting" FaaS function?

Edit: if anyone is wondering why the image file gets uploaded back into a storage bucket and then the Vision API is called, this due to a limitation of that API where inputs can only be storage bucket URIs and not local files
In my point of view, you can have a future gain separating the function into multiple parts because it will allow to use another combining system/upload/whatever as input to the processing part and another system to process the images later.

My personal rule of thumb for separation is the main concern:


Step 1 is giving the input, it's already somethhing it itself.
Steps 3 and 4 are, basically, take an input, do the work andgive an output. It waits base64 strings as input and output an image file to a bucket, it should be autonomous in its processing
Step 5 is an api call, it waits image as input and "whatever vision API does" as result/output, that's a task in itself.


I did omit step 2 as it's the trigger of step 3 and 4.

The main interest into this separation of duty is the possibility to work with contracts between the functions (mostly bullet 2 and 3): 2 is expected to output images in a bucket, 3 expects to find images in a bucket, you can then replace 2 or 3 with whatever you want as long as this contract is fulfilled.

Now for when to do this split and is it necessary, if you don't expect things to change in a near future and there's no hard limit approaching, like the time to do the whole work is approaching the max execution time, then there's no need to over anticipate problems in my humble opinion, the whole step 3 to 5 can be seen as a single task unit as you said and this make sense.

My take would be to measure the time within the function to see if there's an interest (for parallel processing for exemple) in taking the combine and transform to image to its own unit.

In brief to answer the question title: there's no silver bullet or hard rule for the granularity, it's always a matter of weighting the benefits toward the cost of slicing the process.