Tygodniowe wyzwanie programistyczne — #5

By 18 October 2016 October 19th, 2016 ITlogy, Wydarzenia

Pią­ty dzień wyzwa­nia, piąte zadanie — nie pod­da­je­my się! Dla osób początku­ją­cych mamy dzisi­aj user sto­ry do zaim­ple­men­towa­nia, a prak­ty­cy zmierzą się ze zmi­ana­mi wyma­gań w pro­jek­cie. Gotowa?

Wyzwanie dla początkujących

Przed przys­tąpi­e­niem do zada­nia, zas­tanów się nad rezul­tatem Two­jej wczo­ra­jszej pra­cy: Czego dowiedzi­ałeś się o swoich umiejęt­noś­ci­ach? Czego się nauczyłeś? Co zro­bisz z tą wiedzą?

Pod­sta­wową for­mą opisy­wa­nia zadań w metodologii SCRUM jest user sto­ry. Dzisiejsze zadanie będzie miało dwa wymi­ary. Po pier­wsze, pokaże­my Ci jak for­mułować poprawne User Sto­ry, a po drugie, poz­wolimy Ci na włas­nej skórze odczuć jak to z tym User Sto­ry jest i czy fak­ty­cznie aż tak ważne jest, by napisać je dobrze. Gotowa?

Pod­sta­wowa for­ma user sto­ry to : Jako <użytkown­ik>, chcę <potrze­ba>, żeby <problem/cel>.
No dobrze, to prze­jdźmy do pier­wszej częś­ci naszego zada­nia prak­ty­cznego. Poświęć nie więcej niż 20 min na zaim­ple­men­towanie takiej user story:

Jako użytkown­ik, chcę zobaczyć szczegóły prog­nozy pogody dla bieżącego dnia, aby móc ocenić sytuację.

Jeśli nie potrafisz wszys­tkiego zakodować, ułóż log­iczną całość aplikacji za pomocą dia­gramów Akty­wnoś­ci, albo opisz krok po kroku wyma­gania, potrzeb­ne zaso­by, co konkret­nie Two­ja aplikac­ja będzie robiła.

Uwa­ga! Jeśli nie chcesz korzys­tać z API lub spraw­ia Ci to prob­le­my, możesz uproś­cić to zadanie i po pros­tu losować opis pogody danego dnia. Przyjmij wtedy, że pogodę opisu­je tem­per­atu­ra maksy­mal­na w ciągu dnia, szansa opadów, szy­bkość wia­tru i ciśnie­nie atmos­fer­yczne. Dla ambit­nych opis API do pogody i przykłady uży­cia w kilku językach zna­jdziesz np. na stronach yahoo: https://developer.yahoo.com/weather/#get-started=

Dopiero po częś­ci prak­ty­cznej prze­jdź do dal­szej częś­ci zadania!

Mamy nadzieję, że nie jesteś na nas zła, bo to co przed chwilą Ci pokaza­l­iśmy jest właśnie przykła­dem nien­ajlep­szej user sto­ry. Nie wiemy kim jest nasz użytkown­ik, jego potrze­ba to po pros­tu jakieś infor­ma­c­je, a cel jest również nieś­cisły. Czego dokład­nie on potrze­bu­je? Wilgo­t­noś­ci powi­etrza? Siły wia­tru? Czy naszym użytkown­ikiem jest nas­to­latek, który chce wiedzieć jak ubrać się do szkoły, czy może stu­dent geografii, który w opar­ciu o dostar­c­zone dane zro­bi pro­jekt zal­iczeniowy? Tego braku­je w pier­wszym przykładzie. Dlat­ego, zachę­camy Cię do zas­tosowa­nia rozsz­er­zonej wer­sji szablonu do user sto­ry, tak byś nie prze­gapiła żad­nych ważnych informacji.

5xW

Moż­na zatem sko­rzys­tać z tzw. 5xW(ang. Who, When,Where, What, Why), i stworzyć takie user story.

Jako<kto?><kiedy?><gdzie?>, chcę <co?> ponieważ<dlaczego?>

BDD

Ale zan­im prze­jdziemy do uszczegółowienia naszego zada­nia prak­ty­cznego, chce­my pokazać Ci jeszcze jeden fajny sposób na doszczegółowie­nie user sto­ry. Możesz sko­rzys­tać ze stworzenia sce­nar­iuszy na warun­ki satys­fakcji, a same sce­nar­iusze zapisać za pomocą BDD(Behavior-driven Devel­op­ment). Tak, wiemy, to kole­j­na definic­ja, ale sam zobaczysz, że jest to proste i przy­jemne i może pomóc Two­jej pra­cy! BDD to sposób na opis konkret­nych zachowań użytkownika/zachowania aplikacji.
Korzys­tamy w nim z trzech etapów Giv­en - opisu­jącego warun­ki początkowe, When opisu­jącego konkret­ną akcję i Then — opisu­jącego rezul­tat. Zda­nia zna­j­du­jące się za tymi wyraza­mi powin­ny być proste i opisu­jące jed­ną rzecz, a gdy chce się dodać kole­jną np. warunek początkowy moż­na sko­rzys­tać ze słówka And.

Przykład­owo, wyszuki­wanie w google zapisane w ten sposób będzie brzmiało:

Giv­en Jestem na stron­ie www.google.pl
When wpisu­ję kobi­ety­doko­du w polu tekstowym
And klikam Wyszukaj
Then pokazu­je mi stronę z wynika­mi wyszukiwania
And blog kobi­ety­doko­du zna­j­du­je się w tych wynikach

Fajne? Pomyśl też o struk­turze testów z pier­wszego zada­nia tam też dzi­ałam w ten sposób, najpierw tworzyłam zmi­en­ną ze słowem, które było przekazy­wane do metody, potem wywoły­wam tę metodę, a na końcu sprawdza­łam rezul­taty. Było Given-When-Then.

No dobrze, to wróćmy do zada­nia praktycznego:

Jak brz­mi ono w postaci poprawnej User Story?

Jako oso­ba dojeżdża­ją­ca row­erem do pra­cy, chcę widzieć pod­sumowanie pogody danego dnia, ponieważ na tej pod­staw­ie pode­j­mu­ję infor­ma­cję czy zabrać para­solkę czy nie.

No i od razu mamy więcej infor­ma­cji! Na czym pole­ga Two­je zadanie? Spróbuj stworzyć kil­ka sce­nar­iuszy wery­fiku­ją­cych założe­nia, sto­su­jąc BDD. Jak powin­na zachować się Two­ja aplikacja?

Następ­nie wróć do kodu z początku zada­nia — co musisz w nim zmienić by dzi­ałał on zgod­nie z fak­ty­czny­mi potrze­ba­mi użytkown­i­ka? Spróbuj poświę­cić pozostały czas na zmi­any, a jeśli go zabraknie stwórz listę TODO, co jeszcze należy zrobić.

Zas­tanów się też:

  • dlaczego User Sto­ries są tak pop­u­larne? Co jest ich najwięk­szą siłą wg Ciebie?
  • kiedy ostat­ni raz pra­cow­ałaś z User Sto­ries? Czy były dobrze napisane?
  • kto w Two­jej fir­mie powinien tworzyć User Stories?

Mam nadzieję, że to zadanie przekon­ało Cię do dobrego opisy­wa­nia User Sto­ries i że dzię­ki niemu będziesz wyma­gał go od siebie i od innych.

Nasza odpowiedź

Moje rozwiązanie jest bard­zo proste, ale chci­ałam pokazać idee, nie sku­pi­a­jąc się na technikaliach.
Losu­ję więc pogodę dla każdego dnia.
Rozwiązanie dla „złej” User Sto­ry wyglą­da tak: https://gist.github.com/apietras/c6bbf65840dc018f1ade180defa27f87

Po przeczy­ta­niu kom­plet­nej User Sto­ry stworzyłam następu­jące warun­ki satysfakcji:

Giv­en Szanse na deszcz są mniejsze niż 30%
When Użytkown­ik sprawdza pogodę
Then wyświ­et­la się komu­nikat „Enjoy sun­ny day!”

Giv­en Szanse na deszcz są więk­sze niż 30% i mniejsze niż 50%
When Użytkown­ik sprawdza pogodę
Then wyświ­et­la się komu­nikat „You should con­sid­er tak­ing umbrella!”

Giv­en Szanse na deszcz są więk­sze niż 50%
When Użytkown­ik sprawdza pogodę
Then wyświ­et­la się komu­nikat „You have to take umbrella!”

A następ­nie zmieniłam klasę Weath­erTeller: https://gist.github.com/apietras/3e5c39b00f5d47bfa35026874ca5df5e

Zwróć uwagę, że warun­ki satys­fakcji to gotowe sce­nar­iusze testowe, które należało­by zaim­ple­men­tować dla naszej aplikacji.

Wniosek: Dobrze napisane user sto­ry to dobrze opisane potrze­by użytkown­i­ka, a tym samym imple­men­tac­ja taka, jakiej oczeku­je klient. Oczy­wiś­cie, mapowanie potrzeb na konkretne US to zupełnie inna his­to­ria i dość pra­cochłon­ny pro­ces — jed­nak jako devel­op­er pamię­taj, by zawsze wyma­gać czy od klien­ta czy od współpra­cown­ików uszczegóław­ia­nia user sto­ry, tak jak tylko to możliwe.

Wyzwanie dla praktyków

Przed przys­tąpi­e­niem do zada­nia, zas­tanów się nad rezul­tatem wczo­ra­jszej pra­cy: Czego dowiedzi­ałeś się o swoich umiejęt­noś­ci­ach? Czego się nauczyłeś? Co zro­bisz z tą wiedzą?

W pro­gramowa­niu są pewne czyn­noś­ci pow­tarzalne — wszys­tko to, co nazy­wamy ‚boil­er­plate code’ czy po pros­tu pod­sta­wowa kon­fig­u­rac­ja aplikacji. Są też zada­nia, które wyma­ga­ją od nas powtórzenia całego wysiłku pomi­mo pozornie niewielkiej zmiany.
Dzisiejsze zadanie pole­ga na napisa­niu pro­gra­mu, który będzie wczy­ty­wał z pliku CSV infor­ma­c­je o cenach akcji danego dnia w pewnym okre­sie cza­su i na tej pod­staw­ie gen­erowała pod­sumowanie cen za każdy miesiąc (śred­nia cena, najniższa i najwyższa). For­mat linii pliku wejściowego:

{data w formacie YYYY-MM-DD};{cena}

Po jego zaim­ple­men­towa­niu, prze­jrzyj poniższe zmi­any imple­men­tu­jąc je po kolei. UWAGA: Nie otwier­aj kole­jnej zmi­any, dopó­ki nie zaim­ple­men­tu­jesz poprzed­niej! Są one odpowied­nika­mi zmi­an w pro­jek­cie z jaki­mi na codzień przy­chodzi Ci się mierzyć. Pamię­taj, aby przyjąć sobie lim­it cza­sowy — 40–50 min­ut na imple­men­tację wszys­t­kich zmian.

(aby przeczy­tać zmi­any, zaz­nacz całą lin­ijkę zaczy­na­jącą się od ‘Zmi­ana’ — wtedy będziesz mogła przeczy­tać opis danej zmiany)

Zmi­ana pier­wsza: dane mogą być nie po kolei lub zdublowane (jeśli ceny są różne, liczy się ta, która pojaw­iła się jako pier­wsza).

Zmi­ana dru­ga: cena może być w różnych for­mat­ach (krop­ki lub przecin­ki jako odd­zie­le­nie częś­ci ułamkowej, mogą wys­tępować spac­je odd­ziela­jące tysiące).

Zmi­ana trze­cia: mogą wys­tępować luki w danych (np. braku­jące dni) — wtedy należy je uzu­pełnić oblicza­jąc śred­nią cenę z dnia poprzed­niego i z dnia następ­nego (założe­nie jest takie, że luka trwa maksy­mal­nie jeden dzień).

Zmi­ana czwarta: poza pod­sumowaniem miesięcznym, ma być wyp­isy­wane także roczne.

Zmi­ana pią­ta: luki mogą trwać dłużej (wtedy wszys­tkie dni, dla których nie posi­adamy wartoś­ci uzu­peł­ni­amy tą samą ceną — uśred­nioną wartoś­cią z ostat­niego dnia, dla którego znamy wartość, oraz z kole­jnego dnia, dla którego znamy wartość).

Jak pewnie zauważyłaś każ­da z tych zmi­an jest try­wial­na, o ile pojaw­iła­by się na początku. Fakt, że pojaw­ia się dopiero po zaim­ple­men­towa­niu częś­ci aplikacji częs­to generu­ję potrze­bę przepisa­nia całej aplikacji lub jej znacznej częś­ci. Po zakończe­niu ćwiczenia, zas­tanów się nad odpowiedziami:

  • która zmi­ana wprowadz­iła najwięk­szy ‚zamęt’ ? Dlaczego, co było w niej tak specy­ficznego, że wyma­gało głębo­kich zmi­an w aplikacji?
  • co mogłaś zro­bić na początku pro­jek­tu, aby ułatwić sobie zmi­any w przyszłoś­ci (uwa­ga: chodzi tutaj o aspek­ty tech­nicznie, a nie o dokład­niejsze dopy­tanie biznesu/przeanalizowanie problemu)
  • czy w pro­ce­sie tworzenia tej aplikacji stworzyłaś dług tech­niczny? czy moż­na było go uniknąć? czy trud­no jest go usunąć?
  • czy po piątej zmi­an­ie, Two­ja imple­men­tac­ja pozwala na łatwe mody­fikac­je pro­ce­su w przyszłości?
  • dlaczego nie zaim­ple­men­towałaś aplikacji w wer­sji ostate­cznej (jeśli chodzi o struk­turę kodu, a nie wszys­tkie para­me­try) od początku? z jakich założeń to wynikało? jakie założe­nia poczyniłaś, nie doku­men­tu­jąc ich?

Z tych wszys­t­kich pytań chy­ba najważniejsze jest ostat­nie i wnios­ki, które z niego płyną — jakie założe­nia przyj­mu­jesz bez zas­tanowienia się i jak wpły­wa­ją one na pode­j­mowane przez Ciebie decyz­je? Czy w swo­jej pra­cy zawodowej pod­jęłaś ostat­nio jakieś założe­nia, które mogą wpłynąć na pro­jekt w przyszłoś­ci? Czy była to decyz­ja świadoma?

Nasza odpowiedź

Moje rozwiązanie zna­jdziesz pod adresem https://github.com/jderda/wyzwanie-powtarzanie (każ­da ‚zmi­ana’ to osob­ny com­mit, aby móc prześledz­ić, jak rozwi­jała się aplikacja).

Tym razem postanow­iłem się nieco bardziej pobaw­ić inter­fe­jsa­mi funkcyjny­mi w Javie ;) Poz­woliło mi to dość uproś­cić zmi­any wprowadzane na poszczegól­nych eta­pach (jak zauważysz na dif­fach)! W prak­tyce, każ­da lamb­da jest jed­nak osob­ną funkcją, więc każ­da z tych zmi­an real­nie dotykała ok. 30–50% funkcji w mojej aplikacji. Z drugiej strony, nie musi­ałem dokony­wać dużych zmi­an struk­tu­ral­nych, wystar­czyło zmienić zachowa­nia (jedynie w przy­pad­ku gen­erowa­nia pod­sumowa­nia rocznego musi­ałem zmienić sposób, w jaki była wywoły­wana metoda).

Jeśli chodzi o to, co mogłem zro­bić od początku pro­jek­tu, aby uproś­cić dal­sze zmi­any, to zde­cy­dowanie wyciąg­nię­cie pod­sta­wowych ‚budul­ców’ do osob­nych funkcji (czy też lambd) — np. kon­wer­s­ja z ciągu znaków na datę czy z ciągu znaków na liczbę. Dzię­ki tea­mu zmi­any były ‚lokalne’ i nie wpły­wały negaty­wnie na czytel­ność kodu. Dru­ga kwes­t­ia, którą także udało mi się od początku wprowadz­ić, to nie wprowadzanie dodatkowych założeń ani funkcji — imple­men­tac­ja była min­i­mal­isty­cz­na na tyle, na ile wyma­gały tego warun­ki zada­nia, ale jed­nocześnie nie wprowadza­ło dodatkowych ograniczeń (np. na zakres liczb, czy for­mat daty). Niek­tóre funkc­je mogą się przez to wydawać niel­og­iczne — np. wyp­isy­wanie pod­sumowa­nia miesięcznego nie jest posor­towane (moż­na to jed­nak łat­wo zmienić — zobacz zmi­any związane z ostat­nią rewizją).

Imple­men­tac­ja raczej nie ‚wzbo­gaciła’ się o dług tech­niczny — oczy­wiś­cie pewne rzeczy moż­na było rozwiązać inaczej, sko­rzys­tać z bib­liotek, być zgod­nym ze stan­dar­d­em CSV itp, ale wzglę­dem pier­wszej wer­sji ostat­nia nie ma więcej długu tech­nicznego czy rzeczy wyma­ga­ją­cych poprawy.

Jeśli chodzi o struk­turę aplikacji, w początkowej wer­sji zabrakło kroku ‚czyszczenia’ danych (czyli w naszym wypad­ku uzu­peł­ni­a­nia luk w danych) — początkowo był­by on pusty, nie było więc dużego sen­su w jego imple­men­tacji od początku.

Moje rozwiązanie mogło pow­stawać nieco pod wpły­wem pełnej treś­ci zada­nia, które sam wcześniej pisałem (znałem więc założe­nia) ;) Mimo wszys­tko było to ciekawe doświad­cze­nie i przy­pom­ni­ało mi, jak ważne jest pro­gramowanie z założe­niem, że zmi­any prędzej czy później na pewno się pojawią.

Pamię­taj, że nowe zada­nia będą się pojaw­iać codzi­en­nie o godzinie 11. Rozwiąza­nia będziemy umieszczać pod zada­ni­a­mi kole­jnego dnia o godzinie 18. Nie zapom­nij podzielić się swoi­mi odpowiedzi­a­mi i prze­myśle­ni­a­mi na wydarze­niu na face­booku, a jak masz ochotę to też w komentarzu ;)!

Lin­ki do wszys­t­kich zadań zna­jdziesz w innym wpisie na naszym blogu. Powodzenia!