Q:

How to avoid loops when writing cloud functions? 

When writing event based cloud functions for firebase firestore it's common to update fields in the affected document, for example:
When a document of users collection is updated a function will trigger, let's say we want to determine the user info state and we have a completeInfo: boolean property, the function will have to perform another update so that the trigger will fire again, if we don't use a flag like needsUpdate: boolean to determine if excecuting the function we will have an infinite loop. 
Is there any other way to approach this behavior?  Or the situation is a consequence of how the database is designed?  How could we avoid ending up in such scenario? 

A:

I have a few common approaches to Cloud Functions that transform the data:

Write the transformed data to a different document than the one that triggers the Cloud Function.  This is by far the easier approach, since there is no additional code needed - and thus I can't make any mistakes in it.  It also means there is no additional trigger, so you're not paying for that extra invocation. 
Use granular triggers to ensure my Cloud Function only gets called when it needs to actually do some work.  For example, many of my functions only need to run when the document gets created, so by using an onCreate trigger I ensure my code only gets run once, even if it then ends up updating the newly created document. 
Write the transformed data into the existing document.  In that case I make sure to have the checks for whether the transformation is needed in place before I write the actual code for the transformation.  I prefer to not add flag fields, but use the existing data for this check. 
A recent example is where I update an amount in a document, which then needs to be fanned out to all users:
exports.fanoutAmount = functions.firestore.document('users/{uid}').onWrite((change, context) => {
  let old_amount = change.before && change.before.data() && change.before.data().amount ?  change.before.data().amount : 0;
  let new_amount = change.after.data().amount;
  if (old_amount ! == new_amount) {
    // TODO: fan out to all documents in the collection
  }
});

