While a filename extension could probably be superseded by file metadata, it's also nice when you can read the intention right in the name without additional tooling.
Especially when you consider a hypothetical world without filename extensions thus every UI shows the filetype next to the filename thus "readme.md" in our world is just "readme:md" in their world, and it's not obvious what was gained nor lost.
In fact, I always want to see file types alongside their names, so I really don't mind that the two are together.
Separately, what `ls` displays with or without switches would be independent of how the extension is stored. Say the extension were a distinct field, `ls` could still display that as `{filename}.{extension}` by default.
It's an accident of history that the extension is part of the filename.
But that would break piping the output of `ls` to other tools that expect a filename.
https://mywiki.wooledge.org/ParsingLs
https://www.shellcheck.net/wiki/SC2010
It's brittle and the output is designed for humans, not machine parsing.
Use a glob or find.
In a world where extensions were their own distinct metadata, whether or not to show them would be a switch to `ls` (whose output you shouldn't be parsing) and how to retrieve that metadata from a filename w/o parsing `ls` output would be something like:
ext=$(stat --format="%E" -- "$filename")
You'd also have a switch to `find` to select files by extension instead of having to pattern match their names.My rule is that it's ok to use `ls` only in a directory where you know the filenames aren't weird. That rule is the same regardless of whether a human or a computer will be parsing its output. In general, reusable shell scripts shouldn't make assumptions about their environment, so they shouldn't use `ls`; but piping `ls` to another command in a one-off manner at an interactive shell is fine if you know the filenames in the directory aren't weird, just like it would be fine to use `ls` at the same interactive shell and simply read its output yourself.