It's hard to avoid the term referential transparency when reading a functional programming book.
This is a concept that is easy to demonstrate in a functional language, since immutability and pure functions are first class citizens. However it's possible to use this concept in an imperative language too.
The addition function of integers are a common example of a pure function, it will always return the same value for the two given values.
let sum = 42 + 1337
// val sum: int = 1379
Which means that if a program had the above expression sprinkled everywhere in a code base we could replace it with 1379 instead of 42 + 1337. In other words; if the expression can be replaced with its corresponding value then we have referential transparency.
Of course this is a silly example, but we can imagine a more complex expression that uses many more pure functions and immutable variables.
let three = 3
let add x y = x + y
let inc x = x + 1
let dec x = x - 1
let result = dec three |> add 1 |> inc |> inc
// val result: int = 5
// could be substituted with
let result = add three 2
// or
let result = 3 + 2
// or
let result = 5
// or ...
This concept makes it much easier to reason about the code because it requires only local reasoning! And this will give the side effect (in this case it's good 😋) that the code will be easier to test, maintain and refactor.
Writing your code with referential transparency in mind will also enable smart compilers to optimize the code.