top of page

Bomba czasowa - przydatna rzecz

Zdjęcie autora: Krzysztof Borkowski
Krzysztof Borkowski
16 lip 2020
7 minut(y) czytania

Zaktualizowano: 20 wrz 2020

Czasami zdarza się scenariusz podobny do następującego.


Oto jesteś jednym z uczestników sporego projektu. Pracuje w nim blisko 40 programistów oraz 8 analityków. Ty piastujesz w nim niewdzięczną rolę lidera jednego z zespołów developerskich. Razem z Tobą poci się w nim 9 developerów. Nie jest to rzecz jasna projekt typu waterfall, gdzie do developmentu trafiają wymuskane, ostateczne wersje dokumentów analitycznych, których zmiana wymaga uruchomienia straszliwej maszynerii pod nazwą "procedura zarządzania zmianą". W tym projekcie panuje agile.


A ściślej coś, co pod wieloma względami przypomina Scruma. W projekcie panuje więc pewnego rodzaju chaos, który całkiem nieźle jest kontrolowany. Jednakże liczba wątków jest naprawdę duża. Oto przykład. Właśnie jesteś w środku iteracji. Twój zespół skupia się na zaimplementowaniu funkcjonalności wystawiania ofert handlowych. Kilka dni wcześniej otrzymaliście od analityka dokument pt. "Oferty". Brzmi nieźle - fajnie, że w ogóle powstał (wiele projektów bowiem, wciąż nie ma w swoim składzie analityków). Jest jednak pewne ale... otóż dokument ten jest w wersji... 0.1. Oznacza to, że nie nadaje się nawet do tego, aby przejrzał go inny analityk z zespołu projektowego - o pokazaniu go klientowi, w ogóle nie ma mowy. Musicie jednak zacząć pracę nad tym papierem, ponieważ termin projektu jest nieprzekraczalny i w zasadzie nieosiągalny - więc, jak mawiał Jack Aubrey, "nie ma chwili do stracenia".


W minionych trzech dniach w Twojej skrzynce mailowej pojawiły się nowsze wersje dokumentów analitycznych, które Twój zespół zaimplementował już jakiś czas temu - będzie więc trzeba pochylić się nad tymi dokumentami jeszcze w tej iteracji, żeby rozplanować prace nad nimi w czasie najbliższego Sprint Planning Meetingu. Dodatkowo nęka Cię lider zespołu testerów, który właśnie próbuje z klientem zamknąć odbiory modułu konfigurowalnego cennika. Ty i Twój zespół męczyliście się nad tą funkcjonalnością przez trzy miesiące, ale... było to pół roku temu. Niestety dział QA nie miał wówczas mocy przerobowych, żeby zająć się testami tego modułu. Dopiero teraz testerzy zaczęli się nad nim pastwić.


Dodatkowo Twoje myśli i czas zaprzątają różnego rodzaju zagadnienia takie jak: problemy architektoniczne, różnego rodzaju bezsensowne spotkania, ciągłe rozmowy rekrutacyjne, które prowadzisz w celu pozyskania kolejnych mózgów do zespołu, błędy i nieścisłości w dokumentach analitycznych, które musisz wyjaśnić z analitykami (masz tutaj naprawdę długą listę nieścisłości i pytań) itd. itd. Masz już za sobą kilka lat doświadczenia zawodowego więc wiesz, że niektóre z tych zagadnień będziesz musiał sobie odpuścić, z braku czasu, a o niektórych zapomnisz i wrócą do ciebie w postaci ticksa w systemie rejestracji i śledzenia zgłoszeń (takim jak np. Redmine). Nie chcesz, żeby tak było - wiesz jednak, że tak będzie.


I oto więc, kiedy tak sobie rozmyślasz nad marnym losem syna Twojego ojca, jeden z developerów w Twoim zespole podchodzi do Ciebie i pokazuje Ci fragment dokumentacji analitycznej (wspomniany już wcześniej dokument "Oferty", wersja 0.1), w której znajduje się taki zapis:

  1. W chwili tworzenia oferty handlowej, użytkownik systemu ustawia na niej warunki płatności. Do dyspozycji ma dwa parametry:

    1. jaki procent całkowitej kwoty oferty zamawiający musi zapłacić dostawcy zanim ten wyśle towar zamawiającemu (tzw. przedpłata)

    2. jaki procent całkowitej kwoty oferty zamawiający musi zapłacić dostawcy po odbiorze towaru (tzw. płatność po odbiorze towaru)

  2. TODO: Uwaga! Dla zamawiających, którzy w historii współpracy mają opóźnienia w płatnościach, system nie pozwala na podanie wartości innej niż 0% dla parametru drugiego; w obecnej chwili nie wiadomo jednak, jak dokładnie taki algorytm powinien wyglądać

Nie do końca wiesz, co z zrobić z tym TODO. Oczyma wyobraźni architektonicznej widzisz taki oto diagram klas i taki oto kod.

Na diagramie mamy dwie klasy: Offer - opisuje obiekty reprezentujące oferty oraz Company - opisuje obiekty reprezentujące firmy.


Klasa Offer ma pole recipient typu Company, które zawiera informację do kogo oferta jest skierowana. Posiada również metodę updatePaymentTerms, która służy do ustawiania warunków płatności. Metoda przyjmuje dwa argumenty: wysokość przedpłaty oraz wysokość płatności po odbiorze towaru.


Klasa Company ma metodę hasBeenLateWithPayment, która zwraca informację, czy w historii kontaktów z daną firmą zdarzały się sytuacje, w których firma ta zalegała z płatnością.


Tak więc kod tych klas będzie wyglądał następująco:

public final class Offer {
  private final Company recipient;

  private int prepayment;
  private int paymentAfterAcceptanceOfGoods;

  // Pozostałe pola, konstruktory i metody pominięte
  // ze względu na zwięzłość

  public void updatePaymentTerms(
                    final int prepayment,
                    final int paymentAfterAcceptanceOfGoods) {
    // walidacja argumentów i stanu klasy pominięte przez zwięzłość
    // o co chodzi z tą walidacją? zajrzyj tutaj 
    if ( recipient.hasBeenLateWithPayment() ) {
      if ( paymentAfterAcceptanceOfGoods != 0 ) {
        throw new IllegalArgumentException();
      }
    }

    this.prepayment = prepayment;
    this.paymentAfterAcceptanceOfGoods = paymentAfterAcceptanceOfGoods;
  }
}
public class Company {
  // Pozostałe pola, konstruktory i metody pominięte
  // ze względu na zwięzłość

  public boolean hasBeenLateWithPayment() {
    final boolean result = false;

    // Hmm... brakuj informacji jak to zaimplementować

    return result;
  }
}

Myślisz o tym, co zrobić z metodą hasBeenLateWithPayment w klasie Company. Rozważasz dwie opcje.


Pierwsza. Można zostawić ten kod tak, jak wygląda teraz (czyli metoda zawsze zwraca false) i po prostu czekać na to, aż ktoś, kiedyś (może) wykryje ten błąd i założy zgłoszenie w issue trackerze. Oczywiście nawet jeżeli takie zgłoszenie powstanie, to nie ma pewności, że zostanie ono zauważone. Ty sam, jesteś wręcz pewien, że zostanie przeoczone. Już w tej chwili zgłoszeń jest kilkaset, a do tego klient wszystkie swoje zgłoszenia oznacza jako krytyczne (nawet te dotyczące błędów ortograficznych w komunikatach dla użytkownika systemu). Nie łudzisz się więc, że zgłoszenie to będzie się w jakikolwiek sposób wyróżniało. Tak więc spodziewasz się, że Wasz system trafi na produkcję z tym falsem i że ładnych parę ofert, wygenerowanych w kilkudziesięciu filiach Twojego klienta na całym świecie, pójdzie do kontrahentów Twojego klienta z niedopuszczalnymi warunkami płatności. Wiesz, że jeżeli do tego dojdzie, będzie niezła chryja i wiele zarwanych nocy (a jeżeli nie wiesz, że tak właśnie będzie, to przeczytaj: Fail Fast... or die! (część I)).


Drugie rozwiązanie to rzucać tutaj wyjątek... ale wówczas w ogóle nie będzie możliwe tworzenie ofert, w których wartość płatności po odbiorze byłaby większa niż 0%. Fatalnie! Zablokuje to bowiem możliwość testowania szeregu bardzo skomplikowanych przypadków użycia takich jak np.:

  1. tworzenie faktur zaliczkowych i faktur końcowych

  2. automatyczne wystawianie upomnień do kontrahentów, którzy otrzymali towar ale za niego nie zapłacili

  3. księgowanie przedpłat i płatności po odbiorze w module księgowym systemu.

Nie możesz więc zablokować tej funkcjonalności rzucając tam wyjątek.


Masz jednak do dyspozycji inne rozwiązanie. Myślisz sobie tak:


Uruchomienie produkcyjne mamy 1 stycznia, czyli... policzmy... zostało nam jakieś dziewięć miesięcy. Dam nam więc wszystkim trzy miesiące na zaimplementowanie tej funkcjonalności, a gdyby jednak okazało się, że czas minął, to wysadzę w powietrze możliwość wystawiania ofert w tym za...nym systemie. Na pewno ktoś wówczas zauważy, że trzeba jak najszybciej pochylić się nad tematem zamawiających, którzy w swojej historii mają opóźnienia w płatnościach.


Oto więc, co robisz:

  1. Mówisz developerowi, który przyszedł do Ciebie po radę, żeby założył w issue trackerze zgłoszenie. Zgłoszenie ma mieć taki oto tytuł: "Błąd: istnieje możliwość tworzenia ofert z niezerową wartością płatności po odbiorze towaru dla zamawiających z historią opóźnień w płatnościach". W szczegółach zgłoszenia opisujesz, że to dlatego, że nie ma na ten temat dokumentacji analitycznej.

  2. Informujesz o istnieniu tego zgłoszenia analityka z Twojego zespołu i przypisujesz do niego to zgłoszenie. Ten, kiedy o tym słyszy, wścieka się - twierdzi, że nie jest to jego wina tylko klienta, który nie ma czasu o tym porozmawiać. Myślisz sobie: "to nawet lepiej!". Sugerujesz swojemu koledze analitykowi, żeby przepisał to zgłoszenie do kierownika projektu po stronie klienta z prośbą, o jak najszybsze zorganizowanie spotkania w tym temacie. Tak więc zgłoszenie leży teraz u klienta.

  3. Teraz prosisz swojego kolegę developera, żeby wyrzeźbił taki oto kod:

public class TimeBomb {
  private TimeBomb() {
    throw new UnsupportedOperationException("Nie waż się tego "
      + "konstruktora wywoływać... "
      + "nawet za pomocą refleksji łobuzie jeden!");
  }

  public static void blowUpOn(final LocalDate date,
                              final String message) {
    // walidacja argumentów i stanu klasy pominięte przez zwięzłość
    if (LocalDate.now().isAfter(date))
      throw new RuntimeException(message);
  }
}

... i poprawił implementację metody hasBeenLateWithPayment w następujący sposób:

public boolean hasBeenLateWithPayment() {
  final boolean result = false;

  TimeBomb.blowUpOn(LocalDate.of(2020, 5, 31),
    "Co się stało? Zajrzyj do zgłoszenia ACME-871");

  return result;
}

Co takiego robi ten kod? Sprawa jest prosta. Metoda hasBeenLateWithPayment będzie zwracała false, ale tylko przez trzy miesiące, a dokładnie do 31 maja 2020 roku włącznie. Od 1 czerwca 2020 roku będzie generowała wyjątek.


Wyobraźmy więc sobie, że oto nadszedł 1 czerwca 2020 roku, a świat zapomniał o tym, że należy określić, jak ma zachowywać się metoda hasBeenLateWithPayment. Co się wówczas stanie? Może po prostu opowiem Ci, co się stało ostatnio, kiedy taka bomba, założona na moje polecenie, wyleciała w powietrze w pewnym projekcie. Nie był to mały projekt, skoro w jego kick-offie brało udział trzech ministrów, doradca Premiera i... jeszcze jakieś sto osób - a jednak nie baliśmy się umieścić w tym projekcie bomby czasowej. Znacznie bardziej baliśmy się wejść z bugiem na produkcję.


Oto więc pewnego dnia, na 3 miesiące przed uruchomieniem produkcyjnym, dostałem telefon od kierownika projektu z informacją, że "nic nie działa". Była 07:30 rano. Klient właśnie rozpoczął kolejny dzień testów systemu, ale niestety tego dnia, jedna z kluczowych funkcjonalności kończyła się błędem w stu procentach przypadków. Zapytałem co się dzieje. Kiedy przeczytał mi opis, który wyskakiwał użytkownikom na ekranie, od razu wiedziałem, że w powietrze wyleciała bomba. Poprosiłem, żeby zerknął do issue trackera pod wskazany w komunikacie numer.


Okazało się, że chodzi o zgłoszenie, która założyliśmy klientowi dwa miesiące wcześniej. Pomimo kilku naszych monitów, w strukturach klienta nie znalazł się nikt, kto chciałbym wziąć odpowiedzialność za poruszony w zgłoszeniu problem. Pracownicy klienta woleli raczej przerzucać się tym zgłoszeniem niczym gorącym kartoflem - no i się doigrali.


W rozmowie z kierownikiem, ustaliliśmy, że bombę przesuniemy o dwa tygodnie do przodu. Prosta sprawa - wystarczyło zmienić jedną linijkę kodu i zainstalować nowszą wersję systemu na środowisku testowym klienta - 30 minut po odłożeniu słuchawki apka znów działała bez problemów. Efekt jednak osiągnęliśmy. Swąd po wybuchu obiegł niemal każdy zakamarek budynków ministerialnych i jeszcze tego samego dnia klient wyznaczył osobę odpowiedzialną za rozwiązanie poruszanego w zgłoszeniu zagadnienia - w zaledwie pół dnia osiągnęliśmy efekt, którego nie udało nam się osiągnąć przez minione dwa miesiące... a dwa tygodnie później funkcjonalność była już w masterze - nieźle, jak na dwie linijki kodu.


Faktem, wartym wspomnienia, jest to, że kierownictwo projektu ze strony klienta nie miało do nas w ogóle pretensji o zaistniałą sytuację. Wręcz przeciwnie. Dzięki bombie osiągnęło efekt, którego prośbami i naciskami samo nie mogło osiągnąć. Dopiero bowiem kiedy system przestał realizować jedną z kluczowych funkcjonalności i sparaliżował proces testowania aplikacji, właściciel biznesowy systemu potraktował zgłoszenie na poważnie, zabrał się za nie do pracy i określił kształt algorytmu, który mieliśmy zaimplementować.


A gdyby bomby nie było? Pewnie weszlibyśmy na produkcję z poważnym bugiem. W najlepszym wypadku, dzięki testerom, dowiedzielibyśmy się o jego istnieniu na kilka dni przed wdrożeniem produkcyjnym. Scenariusz wyglądałby wówczas zupełnie inaczej, znacznie mniej dla nas przyjemnie - w oczach klienta, bowiem, to my bylibyśmy winni zaistniałej sytuacji.


Słowo ostrzeżenia


Bomba czasowa to przydatne narzędzie, ale.... nie należy go nadużywać, to nie reminder. Jeżeli co dziesięć linii kodu będziesz umieszczał bombę i każdego dnia w Twoim systemie dojdzie do 20 wybuchów, technika ta stanie się całkowicie bezużyteczna. Jedynym jej efektem będzie to, że codziennie będziesz tych 20 bomb przesuwać o kolejne tygodnie do przodu... machinalnie - aż pewnego dnia (a będzie to na dwa dni przed uruchomieniem produkcyjnym) wybuchnie Ci trzy tysiące bomb i przed oczyma stanie Ci taki oto, dobrze znany napis: GAME OVER.


"One more thing..."


Ten i inne artykuły piszę po to, żeby podzielić się swoją wiedzą i doświadczeniem z innymi. Jednakże napisanie artykułu to dopiero połowa sukcesu - drugą połowę stanowi dotarcie z treścią do innych. Możesz mi bardzo pomóc w tym zadaniu - wystarczy, że klikniesz w jedną z czterech ikon poniżej i udostępnisz innym link do tego posta. Za tę pomoc będę Ci bardzo wdzięczny.

 
 
 

5 komentarzy


myla.phan236.9
2 dni temu

https://soicaulodechuan.mobi/win2888-org có cái bảng thống kê lô gan nhìn khá gọn, mình để ý chỗ này hơn mấy đoạn dự đoán. Ngày ra với số ngày gan được đặt thành cột riêng nên nhìn sang là phân biệt được, đỡ phải dò trong một đoạn chữ dài. Mình thường hơi ngại những bài nhiều số liệu, nhưng để dạng bảng thế này thì dễ đọc hơn chút. Còn phần mục lục cũng hợp với kiểu xem lướt của mình: nội dung miền Bắc, miền Trung, miền Nam chia riêng, không trộn hết vào nhau. Chưa nói chuyện các con số dự đoán có đúng hay không nhé, ở đây mình chỉ thấy cách sắp nội dung khá dễ theo dõi.…

Polub

nolafo.wle156+abc123
27 sie

keo nha cai mình thấy mọi người nhắc hoài nên tò mò bấm vào xem thử giao diện thế nào thôi. Mình không có ngồi soi từng mục hay nội dung cụ thể, chủ yếu nhìn cách họ sắp xếp trang cho dễ dùng không. Ấn tượng đầu là bố cục khá thoáng, các khối thông tin chia rõ ràng nên lướt một vòng là nắm được chỗ nào với chỗ nào, không bị dồn chữ nhìn mệt. Mấy bảng số liệu hiển thị dạng cột cũng gọn, nhìn nhanh vẫn hiểu mà không rối. Với mình kiểu trình bày vậy là ổn, nhất là khi chuyển qua lại giữa các phần thì menu nằm chỗ dễ thấy, bấm cái…

Polub

katrinacha.vez.52.0.2
27 sie

Bài của bạn trình bày ngắn gọn mà dễ hiểu, mình đọc liền một lượt là nắm được ý, cảm ơn bạn đã chia sẻ. Mình thì ngày nào cũng có thói quen xem thống kê XSMB, nhưng trước giờ cứ phải mở nhiều chỗ để so lại nên hơi mất thời gian. Thế là mình tự gom các thông tin cần thiết vào một chỗ cho tiện theo dõi, kiểu khi cần chỉ việc mở ra là xem ngay, lưu lại cũng đỡ quên. Mình để chung trên một trang ở soicau247.com để lúc muốn tra nhanh thì quay lại, khỏi phải nhớ từng nguồn lặt vặt. Bạn nào cũng hay cập nhật số liệu theo ngày như mình…

Polub

melaniemarshall6592
21 sie

Bài viết dễ hiểu và hữu ích, cảm ơn bạn đã chia sẻ. Mình đọc đoạn nói về cách kiểm tra và hiểu cường độ bê tông thấy giải đáp đúng cái mình đang thắc mắc. Dạo này mình hay để ý giao diện các nền tảng giải trí online nên có gom lại vài trang để tiện xem bố cục, cách họ chia mục và trình bày bảng dữ liệu. Ai quan tâm thì ghé https://vcook.jp/users/119380

Polub

lydiaharve.y50.4.4.4
12 sie

Mình chơi xổ số kiểu cho vui là chính, xem kết quả để giải trí chứ không đặt nặng chuyện phải trúng to. Trước đây nghe bạn bè rỉ tai đủ kiểu “cầu” nọ “cầu” kia nên mình cũng thử theo dõi một thời gian cho biết. Mỗi lần đọc mấy bài nhận định, mình hay ghi lại vài điểm đáng chú ý rồi đợi ra kết quả để so lại, coi thử có phải mình đang tự tin quá mức hay không. Có hôm trúng được chút thì cũng phấn khởi, nhưng sai liên tục cũng nhiều nên mình luôn nhắc bản thân giữ đầu óc tỉnh và kiểm soát tiền. Thỉnh thoảng mình vào soicauxsmb.io để đọc thêm…

Polub

© 2020 by Borkowski IT Consulting. All rights reserved.

bottom of page