Proper programming requires the domain of a program to be recognized, and every possibility handled. It is very common for such domains to be acknowledged in only vague ways, leading to flaws; a common way such mistakes are made is by using a domain shaped by another, and which isn't fully understood.
Every program has a domain, typically finite, acknowledged or not.
One way to achieve this is by accepting the whole domain and gradually rejecting or transforming it, until that true domain remains. This is common with the true domain, in different program segments.
One reason most software can behave so reliably is by primarily experiencing its primary domain, and one reason most software can misbehave so reliably is when those vague edges of this domain are hit.
Coping with character sets is a fine example; common character sets contain many characters behaving differently from all others, commonly called control characters. Newer character sets feature those combining characters, which are a common cause of misalignment. More characters are added, as well. It's clear the only reasonable way to cope with this nonsense is by whitelisting allowed characters. Ofttimes, this approach will be easier than handling all possibilities of the entirety of such sets.
Modelling the domain is one way to acknowledge it, but domain acknowledgment requires neither a type system nor the ability to state invariants; it requires thought. Domain acknowledgment is necessary for machine code programming as it is with any other. Most formal systems fail to model surrounding environments adequately enough that they can be entirely ignored, which is one failure of computing.
It can be easy to accidentally overly-restrict a domain, but this is usually a minor flaw, as it can usually be corrected with simple adjustments, and is typically benign, unlike an overly-open domain.