There are a few techniques to make code more reliable:
It is a set of pre and postconditions attached to functions or types, which can be functions themselves. They might be enforced both at compile and execution time, or by external tools against the code See: ADA 2012 implementation
The programming language forces the programmer to specify types. The compiler enforces the coherency of the types. Types are connected by inheritance and composition. Aspects can be specified via interfaces too.
Haskell excels at this. Type system
It is a set of functions that are executed independently of the execution. Dynamic languages such as python, ruby or JS are more focused on this approach. See TDD
Individual units, modules or functions of the program are tested. They are specially effective for side-effect free functions and well designed code.
Domain behaviors are tested. Behaviour driven development uses this as a premise as well. It uses a DSL specifically designed for the purpose of testing.
The idea is to test bigger sections of the code that follow certain behaviours associated with the Domain.
Proving correctness of the algorithm with some mathematical framework. It requires certain conditions specified in an abstract language. Formal proof is easier to do in functional languages due to their closeness to Algebra.
The quality of the code relies on the structure of it. A better structure implies less code or more robust on most scenarios
Making the code closer to the domain means that there is less chance for implicit concepts or unexpected behaviour. Domain Driven design is a set of techniques to achieve this purpose
DSL (Abstraction + Contract)
It involves the creation of new languages to deal with specific parts of the domain. It could be an internal or an external DSL. The parser of the language enforces the contract. The design of the language improves the quality through what it can describe