Компилятор Go (и процессор) могут переупорядочивать операции чтения и записи, если это не меняет наблюдаемое поведение внутри той же горутины. Это стандартная оптимизация.
Проблема возникает при наблюдении из другой горутины без синхронизации:
var a, b int
// Горутина 1
go func() {
a = 1 // компилятор может переставить
b = 2 // эти две записи местами
}()
// Горутина 2 может увидеть b=2, a=0
// потому что записи переупорядочены
if b == 2 {
fmt.Println(a) // может быть 0!
}
Внутри одной горутины программа ведет себя "как написана" (as-if-serial). Но другие горутины видят результаты только через happens-before. Решение: каналы, мьютексы, atomic.