In Salesforce, [flows are bulkified][1]. The documentation explains it best, so I'll copy it here:

> When multiple interviews for the same flow run in one transaction, each interview runs until it reaches a bulkifiable element. Salesforce takes all the interviews that stopped at the same element and intelligently executes those operations together. If other interviews are at a different element, Salesforce then intelligently executes those operations together. Salesforce repeats this process until all the interviews finish.

For clarity, a Flow Interview is a [running instance of a flow](https://help.salesforce.com/s/articleView?id=sf.flow_interviews.htm&type=5).

In regards to [InvocableMethod][2] calls, this is why there is a restriction on the types of data you can use. The flow runtime will wait until all interviews are at the same element, then call the Apex code with the parameters being all the inputs from all the interviews, and the output will be returned, in order, to the interview the input came from.

In order to facilitate this behavior in Apex, an extra `List` is wrapped around the input parameter and the return value. That means if you want to return a single value, like `Id`, you must put it in a `List`, and if you want to return a `List<Id>`, you need to return a `List<List<Id>>`.

In order to make sure that your code will behave correctly, you must make certain that, if you return a `List`, it has exactly the same number of elements as the input parameter, and in the same order as they were received. If you don't, you may experience errors or even erroneous logic, such as the wrong data reaching the wrong interview.

# Fixed Examples

    @InvocableMethod
    public static List<Account> getLeadConvertedAccount(List<Lead> leads) {
      List<Id> accountIds = new List<Id>();
      for(Lead record: leads) {
        accountIds.add(record.ConvertedAccountId);
      }
      Map<Id, Account> accountsById = new Map<Id, Account>([
        SELECT Name FROM Account WHERE Id IN :accountIds
      ]);
      List<Account> accounts = new List<Account>();
      // Make sure return values are in expected order //
      for(Lead record: leads) {
        accounts.add(accountsById.get(record.ConvertedAccountId);
      }
      return accounts;
    }

----

    @InvocableMethod 
    public static List<List<String>> splitTextByLine(List<String> source) {
      // Always bulkify //
      List<List<String>> results = new List<List<String>>();
      for(String value: source) {
        results.add(value?.split('\n'));
      }
    }

----

    @InvocableMethod
    public static List<Account> processAccountData(List<String> data) {
      List<Account> results = new List<Account>();
      for(String accountJson: data) {
        // Never fail to add a value for each input
        if(String.isNotBlank(accountJson)) {
          results.add((Account)JSON.deserialize(accountJson, Account.class));
        } else {
          results.add(null);
        }
      }
      return results;
    }

  [1]: https://help.salesforce.com/s/articleView?id=sf.flow_concepts_bulkification.htm&type=5
  [2]: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm