This kind of mistake is quite easy to make, especially when somebody writes a function that takes several IDs as arguments next to each other. I've seen it happen a number of times over the years
function foo(customerId, itemId, orderId) {
if(!customer[customerId]) throw new Error("Customer with id=" + customerId + " does not exist");
if(!item[itemId]) throw new Error("Item with id=" + itemId + " does not exist");
if(!order[orderId]) throw new Error("Order with id=" + orderId+ " does not exist");
}
Just an example of how you can detect errors early with defensive programming.
As oppose to a zero cost build time check, that's 3 expensive runtime DB checks, plus an account and a member could have the same ID, so it might run anyways but on the wrong DB rows.
You made two points that are correct in theory, but in practice those lookups would/could take micro-seconds, and the bug would be detected within minutes in a production environment. And it would take five minutes or less to patch.
We could argue if having 1-3 code cowboys is better then a large team. If the code cowboys can produce higher quality, quicker. They will be difficult to replace after many years though.
Yeah this is exactly why using primitive types for ids is bad. If you encode it in the type system then you cannot make this error and don't need to check it at runtime.
Most bugs are found at runtime. The static analysis mostly finds silly errors that are easy to fix. A great type system do make it easier to write spaghetti code though, and you can have one letter variable names - naming is difficult.