Piąty dzień wyzwania, piąte zadanie — nie poddajemy się! Dla osób początkujących mamy dzisiaj user story do zaimplementowania, a praktycy zmierzą się ze zmianami wymagań w projekcie. Gotowa?
Wyzwanie dla początkujących
Przed przystąpieniem do zadania, zastanów się nad rezultatem Twojej wczorajszej pracy: Czego dowiedziałeś się o swoich umiejętnościach? Czego się nauczyłeś? Co zrobisz z tą wiedzą?
Podstawową formą opisywania zadań w metodologii SCRUM jest user story. Dzisiejsze zadanie będzie miało dwa wymiary. Po pierwsze, pokażemy Ci jak formułować poprawne User Story, a po drugie, pozwolimy Ci na własnej skórze odczuć jak to z tym User Story jest i czy faktycznie aż tak ważne jest, by napisać je dobrze. Gotowa?
Podstawowa forma user story to : Jako <użytkownik>, chcę <potrzeba>, żeby <problem/cel>.
No dobrze, to przejdźmy do pierwszej części naszego zadania praktycznego. Poświęć nie więcej niż 20 min na zaimplementowanie takiej user story:
Jako użytkownik, chcę zobaczyć szczegóły prognozy pogody dla bieżącego dnia, aby móc ocenić sytuację.
Jeśli nie potrafisz wszystkiego zakodować, ułóż logiczną całość aplikacji za pomocą diagramów Aktywności, albo opisz krok po kroku wymagania, potrzebne zasoby, co konkretnie Twoja aplikacja będzie robiła.
Uwaga! Jeśli nie chcesz korzystać z API lub sprawia Ci to problemy, możesz uprościć to zadanie i po prostu losować opis pogody danego dnia. Przyjmij wtedy, że pogodę opisuje temperatura maksymalna w ciągu dnia, szansa opadów, szybkość wiatru i ciśnienie atmosferyczne. Dla ambitnych opis API do pogody i przykłady użycia w kilku językach znajdziesz np. na stronach yahoo: https://developer.yahoo.com/weather/#get-started=
Dopiero po części praktycznej przejdź do dalszej części zadania!
Mamy nadzieję, że nie jesteś na nas zła, bo to co przed chwilą Ci pokazaliśmy jest właśnie przykładem nienajlepszej user story. Nie wiemy kim jest nasz użytkownik, jego potrzeba to po prostu jakieś informacje, a cel jest również nieścisły. Czego dokładnie on potrzebuje? Wilgotności powietrza? Siły wiatru? Czy naszym użytkownikiem jest nastolatek, który chce wiedzieć jak ubrać się do szkoły, czy może student geografii, który w oparciu o dostarczone dane zrobi projekt zaliczeniowy? Tego brakuje w pierwszym przykładzie. Dlatego, zachęcamy Cię do zastosowania rozszerzonej wersji szablonu do user story, tak byś nie przegapiła żadnych ważnych informacji.
5xW
Można zatem skorzystać z tzw. 5xW(ang. Who, When,Where, What, Why), i stworzyć takie user story.
Jako<kto?><kiedy?><gdzie?>, chcę <co?> ponieważ<dlaczego?>
BDD
Ale zanim przejdziemy do uszczegółowienia naszego zadania praktycznego, chcemy pokazać Ci jeszcze jeden fajny sposób na doszczegółowienie user story. Możesz skorzystać ze stworzenia scenariuszy na warunki satysfakcji, a same scenariusze zapisać za pomocą BDD(Behavior-driven Development). Tak, wiemy, to kolejna definicja, ale sam zobaczysz, że jest to proste i przyjemne i może pomóc Twojej pracy! BDD to sposób na opis konkretnych zachowań użytkownika/zachowania aplikacji.
Korzystamy w nim z trzech etapów Given - opisującego warunki początkowe, When opisującego konkretną akcję i Then — opisującego rezultat. Zdania znajdujące się za tymi wyrazami powinny być proste i opisujące jedną rzecz, a gdy chce się dodać kolejną np. warunek początkowy można skorzystać ze słówka And.
Przykładowo, wyszukiwanie w google zapisane w ten sposób będzie brzmiało:
Given Jestem na stronie www.google.pl
When wpisuję kobietydokodu w polu tekstowym
And klikam Wyszukaj
Then pokazuje mi stronę z wynikami wyszukiwania
And blog kobietydokodu znajduje się w tych wynikach
Fajne? Pomyśl też o strukturze testów z pierwszego zadania tam też działam w ten sposób, najpierw tworzyłam zmienną ze słowem, które było przekazywane do metody, potem wywoływam tę metodę, a na końcu sprawdzałam rezultaty. Było Given-When-Then.
No dobrze, to wróćmy do zadania praktycznego:
Jak brzmi ono w postaci poprawnej User Story?
Jako osoba dojeżdżająca rowerem do pracy, chcę widzieć podsumowanie pogody danego dnia, ponieważ na tej podstawie podejmuję informację czy zabrać parasolkę czy nie.
No i od razu mamy więcej informacji! Na czym polega Twoje zadanie? Spróbuj stworzyć kilka scenariuszy weryfikujących założenia, stosując BDD. Jak powinna zachować się Twoja aplikacja?
Następnie wróć do kodu z początku zadania — co musisz w nim zmienić by działał on zgodnie z faktycznymi potrzebami użytkownika? Spróbuj poświęcić pozostały czas na zmiany, a jeśli go zabraknie stwórz listę TODO, co jeszcze należy zrobić.
Zastanów się też:
- dlaczego User Stories są tak popularne? Co jest ich największą siłą wg Ciebie?
- kiedy ostatni raz pracowałaś z User Stories? Czy były dobrze napisane?
- kto w Twojej firmie powinien tworzyć User Stories?
Mam nadzieję, że to zadanie przekonało Cię do dobrego opisywania User Stories i że dzięki niemu będziesz wymagał go od siebie i od innych.
Nasza odpowiedź
Moje rozwiązanie jest bardzo proste, ale chciałam pokazać idee, nie skupiając się na technikaliach.
Losuję więc pogodę dla każdego dnia.
Rozwiązanie dla „złej” User Story wygląda tak: https://gist.github.com/apietras/c6bbf65840dc018f1ade180defa27f87
Po przeczytaniu kompletnej User Story stworzyłam następujące warunki satysfakcji:
Given Szanse na deszcz są mniejsze niż 30%
When Użytkownik sprawdza pogodę
Then wyświetla się komunikat „Enjoy sunny day!”
Given Szanse na deszcz są większe niż 30% i mniejsze niż 50%
When Użytkownik sprawdza pogodę
Then wyświetla się komunikat „You should consider taking umbrella!”
Given Szanse na deszcz są większe niż 50%
When Użytkownik sprawdza pogodę
Then wyświetla się komunikat „You have to take umbrella!”
A następnie zmieniłam klasę WeatherTeller: https://gist.github.com/apietras/3e5c39b00f5d47bfa35026874ca5df5e
Zwróć uwagę, że warunki satysfakcji to gotowe scenariusze testowe, które należałoby zaimplementować dla naszej aplikacji.
Wniosek: Dobrze napisane user story to dobrze opisane potrzeby użytkownika, a tym samym implementacja taka, jakiej oczekuje klient. Oczywiście, mapowanie potrzeb na konkretne US to zupełnie inna historia i dość pracochłonny proces — jednak jako developer pamiętaj, by zawsze wymagać czy od klienta czy od współpracowników uszczegóławiania user story, tak jak tylko to możliwe.
Wyzwanie dla praktyków
Przed przystąpieniem do zadania, zastanów się nad rezultatem wczorajszej pracy: Czego dowiedziałeś się o swoich umiejętnościach? Czego się nauczyłeś? Co zrobisz z tą wiedzą?
W programowaniu są pewne czynności powtarzalne — wszystko to, co nazywamy ‚boilerplate code’ czy po prostu podstawowa konfiguracja aplikacji. Są też zadania, które wymagają od nas powtórzenia całego wysiłku pomimo pozornie niewielkiej zmiany.
Dzisiejsze zadanie polega na napisaniu programu, który będzie wczytywał z pliku CSV informacje o cenach akcji danego dnia w pewnym okresie czasu i na tej podstawie generowała podsumowanie cen za każdy miesiąc (średnia cena, najniższa i najwyższa). Format linii pliku wejściowego:
{data w formacie YYYY-MM-DD};{cena}
Po jego zaimplementowaniu, przejrzyj poniższe zmiany implementując je po kolei. UWAGA: Nie otwieraj kolejnej zmiany, dopóki nie zaimplementujesz poprzedniej! Są one odpowiednikami zmian w projekcie z jakimi na codzień przychodzi Ci się mierzyć. Pamiętaj, aby przyjąć sobie limit czasowy — 40–50 minut na implementację wszystkich zmian.
(aby przeczytać zmiany, zaznacz całą linijkę zaczynającą się od ‘Zmiana’ — wtedy będziesz mogła przeczytać opis danej zmiany)
Zmiana pierwsza: dane mogą być nie po kolei lub zdublowane (jeśli ceny są różne, liczy się ta, która pojawiła się jako pierwsza).
Zmiana druga: cena może być w różnych formatach (kropki lub przecinki jako oddzielenie części ułamkowej, mogą występować spacje oddzielające tysiące).
Zmiana trzecia: mogą występować luki w danych (np. brakujące dni) — wtedy należy je uzupełnić obliczając średnią cenę z dnia poprzedniego i z dnia następnego (założenie jest takie, że luka trwa maksymalnie jeden dzień).
Zmiana czwarta: poza podsumowaniem miesięcznym, ma być wypisywane także roczne.
Zmiana piąta: luki mogą trwać dłużej (wtedy wszystkie dni, dla których nie posiadamy wartości uzupełniamy tą samą ceną — uśrednioną wartością z ostatniego dnia, dla którego znamy wartość, oraz z kolejnego dnia, dla którego znamy wartość).
Jak pewnie zauważyłaś każda z tych zmian jest trywialna, o ile pojawiłaby się na początku. Fakt, że pojawia się dopiero po zaimplementowaniu części aplikacji często generuję potrzebę przepisania całej aplikacji lub jej znacznej części. Po zakończeniu ćwiczenia, zastanów się nad odpowiedziami:
- która zmiana wprowadziła największy ‚zamęt’ ? Dlaczego, co było w niej tak specyficznego, że wymagało głębokich zmian w aplikacji?
- co mogłaś zrobić na początku projektu, aby ułatwić sobie zmiany w przyszłości (uwaga: chodzi tutaj o aspekty technicznie, a nie o dokładniejsze dopytanie biznesu/przeanalizowanie problemu)
- czy w procesie tworzenia tej aplikacji stworzyłaś dług techniczny? czy można było go uniknąć? czy trudno jest go usunąć?
- czy po piątej zmianie, Twoja implementacja pozwala na łatwe modyfikacje procesu w przyszłości?
- dlaczego nie zaimplementowałaś aplikacji w wersji ostatecznej (jeśli chodzi o strukturę kodu, a nie wszystkie parametry) od początku? z jakich założeń to wynikało? jakie założenia poczyniłaś, nie dokumentując ich?
Z tych wszystkich pytań chyba najważniejsze jest ostatnie i wnioski, które z niego płyną — jakie założenia przyjmujesz bez zastanowienia się i jak wpływają one na podejmowane przez Ciebie decyzje? Czy w swojej pracy zawodowej podjęłaś ostatnio jakieś założenia, które mogą wpłynąć na projekt w przyszłości? Czy była to decyzja świadoma?
Nasza odpowiedź
Moje rozwiązanie znajdziesz pod adresem https://github.com/jderda/wyzwanie-powtarzanie (każda ‚zmiana’ to osobny commit, aby móc prześledzić, jak rozwijała się aplikacja).
Tym razem postanowiłem się nieco bardziej pobawić interfejsami funkcyjnymi w Javie ;) Pozwoliło mi to dość uprościć zmiany wprowadzane na poszczególnych etapach (jak zauważysz na diffach)! W praktyce, każda lambda jest jednak osobną funkcją, więc każda z tych zmian realnie dotykała ok. 30–50% funkcji w mojej aplikacji. Z drugiej strony, nie musiałem dokonywać dużych zmian strukturalnych, wystarczyło zmienić zachowania (jedynie w przypadku generowania podsumowania rocznego musiałem zmienić sposób, w jaki była wywoływana metoda).
Jeśli chodzi o to, co mogłem zrobić od początku projektu, aby uprościć dalsze zmiany, to zdecydowanie wyciągnięcie podstawowych ‚budulców’ do osobnych funkcji (czy też lambd) — np. konwersja z ciągu znaków na datę czy z ciągu znaków na liczbę. Dzięki teamu zmiany były ‚lokalne’ i nie wpływały negatywnie na czytelność kodu. Druga kwestia, którą także udało mi się od początku wprowadzić, to nie wprowadzanie dodatkowych założeń ani funkcji — implementacja była minimalistyczna na tyle, na ile wymagały tego warunki zadania, ale jednocześnie nie wprowadzało dodatkowych ograniczeń (np. na zakres liczb, czy format daty). Niektóre funkcje mogą się przez to wydawać nielogiczne — np. wypisywanie podsumowania miesięcznego nie jest posortowane (można to jednak łatwo zmienić — zobacz zmiany związane z ostatnią rewizją).
Implementacja raczej nie ‚wzbogaciła’ się o dług techniczny — oczywiście pewne rzeczy można było rozwiązać inaczej, skorzystać z bibliotek, być zgodnym ze standardem CSV itp, ale względem pierwszej wersji ostatnia nie ma więcej długu technicznego czy rzeczy wymagających poprawy.
Jeśli chodzi o strukturę aplikacji, w początkowej wersji zabrakło kroku ‚czyszczenia’ danych (czyli w naszym wypadku uzupełniania luk w danych) — początkowo byłby on pusty, nie było więc dużego sensu w jego implementacji od początku.
Moje rozwiązanie mogło powstawać nieco pod wpływem pełnej treści zadania, które sam wcześniej pisałem (znałem więc założenia) ;) Mimo wszystko było to ciekawe doświadczenie i przypomniało mi, jak ważne jest programowanie z założeniem, że zmiany prędzej czy później na pewno się pojawią.
Pamiętaj, że nowe zadania będą się pojawiać codziennie o godzinie 11. Rozwiązania będziemy umieszczać pod zadaniami kolejnego dnia o godzinie 18. Nie zapomnij podzielić się swoimi odpowiedziami i przemyśleniami na wydarzeniu na facebooku, a jak masz ochotę to też w komentarzu ;)!
Linki do wszystkich zadań znajdziesz w innym wpisie na naszym blogu. Powodzenia!