Błąd, którego można było uniknąć

Jakiś czas temu w pewnym projekcie miałem dosyć ciekawy błąd po wdrożeniu nowej funkcjonalności na produkcję. Błąd, a w zasadzie bardziej jego przyczyna skłoniły mnie do opisania tego na blogu.

Aplikacja, w której opisywany błąd wystąpił ma za zadanie synchronizować dane między dwoma systemami. Niestety, ale nie pisałem jej od początku, więc jestem zmuszony pracować z cudzym kodem. Zwykle programiści mają zwyczaj narzekać na każdy kod, którego sami nie pisali 😀 , ale w tej sytuacji to nie będzie narzekanie, a jedynie refleksja jak można było tego uniknąć. Trzeba sobie uświadomić, że na kod, który sami pisaliśmy, też często ktoś inny narzeka. Idealnych rozwiązań po prostu nie ma i zawsze znajdzie się jakaś sytuacja, której niestety nie przewidzieliśmy, a nasze rozwiązanie do niej nie jest dopasowane. Po prostu ważne, aby wyciągać wnioski i starać się im zapobiegać w przyszłości.

Wdrożenie i problem

Nowa funkcjonalność miała za zadanie przerzucać dokumenty z jednego systemu do drugiego. Niestety po dwóch dniach od wdrożenia, okazało się, że na każdym dokumencie brakuje adresu. Szybka myśl, jakim cudem to w ogóle zostało przegapione. Testy co prawda w projekcie bardzo skromne, więc takich rzeczy nie wychwycą, ale element był sprawdzany przeze mnie i później przez testera, więc powinno grać. Jednak nikt nie zauważył żadnego problemu.

Przyczyna

Znalezienie przyczyny nie trwało jakoś długo, po prostu sprawdziłem bazy danych systemów, a później bazę danych aplikacji, nad którą pracowałem i okazało się, że dane są prawidłowo wczytywane do aplikacji, ale już nieprawidłowe dane wychodzą. Szybki rzut oka na kod i wyłapuję literówkę. Jednak zdziwiłem się bardzo, że taka literówka nie spowodowała żadnego wyjątku. Naprawienie było dosyć proste, jednak taki błąd nie powinien się zdarzyć.

W projekcie wykorzystywany jest Doctrine, jednak przy wysyłaniu elementów nie jest używana standardowa encja tylko ArrayObject tworzony w ten sposób:

$data = new ArrayObject($hydrator->extract($entity));

Załóżmy, że mamy taki kod:

<?php

$arrayObject = new ArrayObject([
    'data1' => 'test1',
    'data2' => 'test2'
]);

var_dump($arrayObject->data1);
var_dump($arrayObject->dtaa2);

Wynik dostaniemy następujący:

string(5) "test1"
PHP Notice:  Undefined index: dtaa2 in test.php on line 9
NULL

Co prawda literówka w nazwie wyrzuca PHP Notice, jednak łatwo go przeoczyć. Warto jednak zauważyć, że i tak jest zwracany NULL.

Jak można było tego uniknąć?

Nie wiem jaki był cel wypakowywania encji do ArrayObject, ale najłatwiejszym sposobem na uniknięcie tego typu problemów jest używanie po prostu encji. Po pierwsze daje nam to pewność co do używanych nazw, bo korzystając z gettera w przypadku błędnej nazwy php rzuci Fatal error. Po drugie daje nam to dodatkową zaletę w postaci podpowiedzi w IDE.

Swoją drogą to tylko Notice w przypadku Undefined index lub Undefined property to według mnie trochę kiepska sprawa. Co o tym sądzisz? Z tego co się orientuję to na przykład w pythonie w takiej sytuacji dochodzi do przerwania wykonywania kodu i wyrzucany jest odpowiedni wyjątek.

Udostępnij: