The bigger lesson here is that boundary behavior deserves the same attention as the happy path. 1:length(x) looks completely reasonable when x has elements, but the empty case changes the semantics because : is generating a sequence rather than acting like a loop boundary.
I like seq_along(x) because it makes the intent explicit: “iterate over the indices that actually exist.” That also makes the code resilient when an upstream filter, query, or batch unexpectedly produces zero rows.
This is a good example of why production bugs often hide in valid language behavior rather than obviously broken code. Testing empty, singleton, and normal inputs is a surprisingly effective way to catch these boundary assumptions early.