serial splits a play's hosts into batches, running the whole play against each batch before moving to the next, which is the basis of a rolling deployment. It accepts a number, a percentage, or a list such as [1, 5, "30%"] for a canary followed by widening batches.
What serial does not do is stop on failure in the way people expect. By default a failed host is removed from the play and the remaining hosts in the batch continue, and subsequent batches still run. A deployment that breaks on the canary proceeds to break everywhere.
any_errors_fatal: true aborts the entire play as soon as any host in the current batch fails, which is the correct pairing for a rolling update. max_fail_percentage is the softer form, aborting when more than the given percentage of a batch fails; it is evaluated per batch, not across the play, and a value of 0 behaves like any_errors_fatal. Both are play-level keywords and have no effect if set on a task.