Krótko mówiąc, sprawia, że stan programu jest nieprzewidywalny.
Aby to rozwinąć, wyobraź sobie, że masz kilka obiektów, które wykorzystują tę samą zmienną globalną. Zakładając, że nie używasz źródła losowości w żadnym z modułów, to wynik konkretnej metody można przewidzieć (a zatem przetestować), jeśli stan systemu jest znany przed wykonaniem metody.
Jeśli jednak metoda w jednym z obiektów wyzwala efekt uboczny, który zmienia wartość wspólnego stanu globalnego, wówczas nie wiesz już, jaki jest stan początkowy, gdy wykonasz metodę w drugim obiekcie. Nie możesz już przewidywać, jakie wyniki uzyskasz po wykonaniu metody, a zatem nie możesz jej przetestować.
Na poziomie akademickim może to nie wydawać się aż tak poważne, ale możliwość testowania kodu jednostkowego jest ważnym krokiem w procesie dowodzenia jego poprawności (lub przynajmniej przydatności do określonego celu).
W prawdziwym świecie może to mieć bardzo poważne konsekwencje. Załóżmy, że masz jedną klasę wypełniającą globalną strukturę danych, a inną klasę, która zużywa dane w tej strukturze danych, zmieniając jej stan lub niszcząc je w trakcie procesu. Jeśli klasa procesora wykona metodę przed wykonaniem klasy populator, wynik będzie taki, że klasa procesorów prawdopodobnie będzie miała niekompletne dane do przetworzenia, a struktura danych, nad którą pracowała klasa populator, może zostać uszkodzona lub zniszczona. Zachowanie programu w takich okolicznościach staje się całkowicie nieprzewidywalne i prawdopodobnie doprowadzi do epickiej straty.
Ponadto stan globalny szkodzi czytelności twojego kodu. Jeśli Twój kod ma zewnętrzną zależność, która nie jest wyraźnie wprowadzona do kodu, to każdy, kto dostanie zadanie utrzymania kodu, będzie musiał go poszukać, aby dowiedzieć się, skąd on pochodzi.
Jeśli chodzi o istniejące alternatywy, to nie ma żadnego stanu globalnego, ale w praktyce zwykle można ograniczyć stan globalny do jednego obiektu, który otacza wszystkie pozostałe i do którego nigdy nie można się odwoływać, opierając się na regułach określania zakresu języka, którego używasz. Jeśli dany obiekt potrzebuje określonego stanu, powinien jawnie o to poprosić, przekazując go jako argument do swojego konstruktora lub metody ustawiającej. Jest to znane jako wstrzykiwanie zależności.
Może wydawać się głupie przejście w stan, do którego masz już dostęp ze względu na zasady określania zakresu w jakimkolwiek języku, którego używasz, ale zalety są ogromne. Teraz, gdy ktoś patrzy na kod w izolacji, jasne jest, jakiego stanu potrzebuje i skąd pochodzi. Ma również ogromne zalety w zakresie elastyczności modułu kodu, a tym samym możliwości ponownego użycia go w różnych kontekstach. Jeśli stan jest przekazywany, a zmiany stanu są lokalne dla bloku kodu, możesz przekazać dowolny stan (jeśli jest to poprawny typ danych) i zlecić jego przetworzenie. Kod napisany w tym stylu ma wygląd kolekcji luźno powiązanych komponentów, które można łatwo zamieniać. Kod modułu nie powinien dbać o to, skąd pochodzi stan, tylko jak go przetworzyć.
Istnieje wiele innych powodów, dla których przekazywanie stanu znacznie przewyższa poleganie na stanie globalnym. Ta odpowiedź w żadnym wypadku nie jest wyczerpująca. Prawdopodobnie mógłbyś napisać całą książkę o tym, dlaczego stan globalny jest zły.