And then you introduce extra two levels of nested loops and suddenly "i", "j", and "k" don't make any sense on their own, but "ProductIndex", "BatchIndex" and "SeriesIndex" do.

ijk for indices in loops are actually clearer than random names in nested loops precisely because it is a *very common convention* and because they occur in a defined order. So you always know that "j" is the second nesting level, for instance. Which relates to the visual layout of the code.

You may not have known of this convention or you are unable to apply "the principle of least astonishment". A set of random names for indices is less useful because it communicates less and takes longer to comprehend.

Just like most humans do not read text one letter at a time, many programmers also do not read code as prose. They scan it rapidly looking at shapes and familiar structures. "ProductIndex", "BatchIndex" and "SeriesIndex" do not lend themselves to scanning, so you force people who need to understand the code to slow down to the speed of someone who reads code like they'd read prose. That is a bit amateurish.

> ijk for indices in loops are actually clearer than random names in nested loops precisely because it is a very common convention and because they occur in a defined order. So you always know that "j" is the second nesting level, for instance. Which relates to the visual layout of the code.

In problem domains that emphasize multidimensional arrays, yes.

More often nowadays I would see `i` and think "an element of some sequence whose name starts with i". (I tend to use `k` and `v` to iterate keys and values of dictionaries, but spell `item` in full. I couldn't tell you why.)

I partly agree, and partly don't. When ijk really is unambiguous and the order is common (say you're implementing a well-known algorithm) I totally agree, the convention aids understanding.

But nesting order often doesn't control critical semantics. Personally, it has much more often implied a heuristic about the lengths or types (map, array, linked list) of the collections (i.e. mild tuning for performance but not critical), and it could be done in any order with different surrounding code. There the letters are meaningless, or possibly worse because you can't expect that similar code elsewhere does things in the same nesting order.

This likely depends heavily on your field though.

I think I know what you mean. Let's assume a nesting structure like this:

Company -> Employee -> Device

That is, a company has a number of employees that have a number of devices, and you may want to traverse all cars. If you are not interested in where in the list/array/slice a given employee is, or a given device is, the index is essentually a throwaway variable. You just need it to address an entity. You're really interested in the Person structure -- not its position in a slice. So you'd assign it to a locally scoped variable (pointer or otherwise).

In Go you'd probably say something like:

for _, company := range companies { for _, employee := range company.Employees { for _, device := range employee.Devices // ..do stuff } }

ignoring the indices completely and going for the thing you want (the entity, not its index).

Of course, there are places where you do care about the indices (since you might want to do arithmetic on them). For instance if you are doing image processing or work on dense tensors. Then using the convention borrowed from math tends to be not only convenient, but perhaps even expected.