Code Quality Keepers
opowieści z pola bitwy o jakość kodu
Rafał Głowiński, Bartosz Walacik
Confitura 2015
O nas
Rafał Głowiński
@GlowinskiRafal
Bartosz Walacik
@BartoszWalacik
http://javers.org
O czym będziemy mówić
Pytania:
→ Jak dbać o jakość kodu w zespole? Code review?
→ A co jeśli mamy 40+ zespołów?
→ Jak zadbać o jakość na poziomie całej organizacji?
O czym będziemy mówić
Opowiemy:
→ Po co CQK?
→ Jak zdefiniować kryteria jakości i ocenić kod różnych zespołów
→ Jak robimy prawdopodobnie największe Code Review w Polsce
→ Co udało nam się znaleźć: “perełki”, typowe błędy
→ A także… co udało nam się osiągnąć
→ 40+ zespołów (dev + infra)
Zespoły w Allegro
→ 4 miasta (Poznań, Toruń, Warszawa, Kraków)
→ Często doświadczenie głównie w PHP
→ Większość pisze i utrzymuje mikrousługi na JVM
Jak powstało CQK i po co?
Pierwszy skład zespołu CQK:
→ doświadczeni inżynierowie
→ zaangażowani w community
→ autorytet nie hierarchia
→ kilka osób myślących podobnie, startup
Jak powstało CQK i po co?
Pierwszy Cel:
Przejrzeć i ocenić (w miarę) obiektywnie cały kod
Pierwsze review:
→ 1 dzień w 1 salce
→ kilkadziesiąt projektów do przejrzenia
→ 15 minut na projekt (!)
→ totalna partyzantka
Jak powstało CQK i po co?
Wnioski z pierwszego review:
→ 15 min na projekt to za mało
→ potrzebna jest metodyka i organizacja
→ koncentracja na wybranych aspektach
Nowe Cele:
→ Cel 1: podnoszenie jakości kodu w całej organizacji
poprzez review, feedback i mentoring
→ Cel 2: wkład do ocen technicznych zespołów
Jak pracujemy
→ Zaczęliśmy od garstki osób, teraz jest nas więcej
→ Co kwartał zmieniamy metodykę review
→ Merytoryczny feedback
→ Przejście na tryb “push”
Metodyka review: pierwszy kwartał
“Wrażenia artystyczne”
(15 min na projekt…)
Metodyka review: drugi kwartał
Duże i częste naruszenia podstawowych zasad
Są błędy, ale generalnie jest ok
Jest dobrze!
Magic Servlet Antipattern is Back:
→ logika biznesowa/dostęp do repozytorium w kontrolerze
→ nieprawidłowe wykorzystanie frameworków (np. ręczne
mapowanie wyjątków na kodu HTTP)
Metodyka review: drugi kwartał
Zwięzłość i czytelność testów:
→ struktura testów, prawidłowe wykorzystanie bibliotek
→ co testują testy
→ mocks (overmocking, mockowanie nie swoich klas)
Szkolenia: trzeci kwartał
→ Naming i omówienie bytów w testach (Stub, Mock, Spy)
→ Spock: Data Driven, wyjątki, asercje, Groovy
→ (*) JUnit: Mockito, AssertJ, JUnitParams, catch-exception
→ Overmocking + verify = betonowy kod
→ Testy integracyjne: bazy danych, REST
Zwięzłość i czytelność testów cd.
Metodyka review: aktualnie
Model domenowy:
→ klasy modelu
→ organizacja pakietów
→ powiązanie z bibliotekami / infrastrukturą
Metodyka review: aktualnie
Model domenowy:
brak modelu domenowego: mutowalne & anemiczne POJO
operujące na nich klasy Service
Metodyka review: aktualnie
Model domenowy:
kod jest podzielony horyzontalnie
domena powiązana z infrastrukturą (zapytania do baz
danych, skomplikowane logowanie/monitoring, itp.)
widać starania odejścia od anemiczności modelu
(rozsądne podejście do mutowalności, obiekty zyskują
funkcjonalności)
Metodyka review: aktualnie
Model domenowy:
ładny model domenowy zgodny z OOP
struktura pakietów zorientowana domenowo
domena jest wyraźnie odseparowana od infrastruktury
podział pakietowy zgodnie z wytycznymi np. Hexagonal
Architecture
Eksperyment: review kodu JS
Opowiemy o tym za rok ;)
Główna działalność CQK
Tworzenie feedbacków dla zespołów
→ Zespół zgłasza do feedbacku projekt lub PR
→ Przeciętny projekt ma ~10KLOC
Co to jest feedback
Kilkustronicowy dokument na WIKI, lista punktów, np:
dobre testy integracyjne (np BlacklistEndpointIntegrationSpec.groovy)
SalesRegulationsFilterTest.java - do testu tego rodzaju prostych funkcji używajcie
podejścia data-driven test
Klasa Pdf.java mogłyby być immutable, czyli zamiast:
Pdf pdf = new Pdf();
pdf .setContent(documentContent);
lepiej
Pdf pdf = new Pdf (documentContent);
długa implementacja equals() w BlacklistRequest.java. Łatwo się pomylić w takim
kodzie. Czy ten equals() jest do czegoś potrzebny?
Fazy życia feedbacku: koszt os./h
I. Review kodu + spisanie 2-3
II. Weryfikacja 1-2
III. Publikacja i uwagi od zespołu 1
IV. Przepracowanie w zespole 0
Jak powstaje feedback
Faza I. Review kodu + spisanie uwag
→ Keeper klonuje repo i ogląda kod
→ Nie uruchamiamy tooli do statycznej analizy
→ Keeper tworzy dokument feedbacku i proponuje ocenę
Jak powstaje feedback
Faza II. Weryfikacja
→ Drugi Keeper przegląda feedback (kontrola merytoryczna)
→ Przy rozbieżnościach - uzgadniamy w ramach CQK
→ Kontrola czy feedback jest friendly
→ Drugi Keeper często dopisuje swoje uwagi
→ Approve
Jak powstaje feedback
Faza III. Publikacja i uwagi od zespołu
→ Feedback jest przekazywany do zespołu
→ Zachęcamy zespół do ustosunkowania się do feedback’u
Czy modyfikujemy feedback?
→ Świadome i uzasadnione naruszenia reguł
→ My czegoś nie zrozumieliśmy (brak kontekstu)
Jak powstaje feedback
Faza IV. Przepracowanie feedbacku w zespole
Mamy nadzieję, że zespół przekona się do naszych uwag i
propozycji
Jak powstaje feedback
Co znaleźliśmy - big picture
Co znaleźliśmy - big picture
Główne grzechy:
→ TDD - Test Driven Development
→ Anemic Domain Model
Co znaleźliśmy - big picture
TDD - problemy:
→ Overmocking
→ Mocking What-You-Don’t-Own
→ Testy są kopią implementacji
→ Za mało testów integracyjnych
→ Testy pisane pod Coverage
→ Testy bez wartości dokumentacyjnej
→ Testowanie implementacji a nie funkcjonalności
Co znaleźliśmy - big picture
Anemic Domain Model
→ Anemiczne, mutowalne Encje
→ Settery i gettery rządzą
→ Programowanie proceduralne nie obiektowe
→ Często w ogóle brak wydzielonego Modelu Domenowego
Co znaleźliśmy - big picture
Test unitowy prostego konstruktora
Co znaleźliśmy - ‘perełki’
public class ClientException extends RuntimeException {
static final long serialVersionUID = -712897190232112939L;
public ClientException(String message) {
super(message);
}
public ClientException(String message, Throwable cause) {
super(message, cause);
}
}
src
@Test
public void shouldSetMessageAndCauseInConstructor() {
//given
String exceptedMessage = "Very bad exception";
Exception expectedCauseException = new Exception("This is cause exception");
//when
ClientException exception = new ClientException(exceptedMessage, expectedCauseException);
String message = exception.getMessage();
Throwable causeException = exception.getCause();
//then
assertEquals(exceptedMessage, message);
assertEquals(expectedCauseException, causeException);
}
test
Nie testuj unitowo prostych konstruktorów oraz
metod
CQK radzi
Test unitowy Serwisu-przelotki
Co znaleźliśmy - ‘perełki’
public class SaleRegulationsService {
public List<SaleRegulations> findRegulations(String userId, Long timestamp) {
List<SaleRegulations> results;
if (timestamp == null) {
results = salesRegulationsRepository.findOneByUserId(userId);
} else {
results = salesRegulationsRepository.findByUserId(userId);
}
return results;
}
...
}
src
@Test
public void shouldReturnMoreResultsWhenTimestampIsGiven() {
// given
SaleRegulationsService saleRegulationsService =new SaleRegulationsService(salesRegulationsRepository,
eventProducer);
SaleRegulations saleRegulations1 =new SaleRegulations(USER_ID, SALE_REGULATIONS_TEXT, TIMESTAMP);
SaleRegulations saleRegulations2 =new SaleRegulations(USER_ID, SALE_REGULATIONS_TEXT_VERSION_2, TIMESTAMP2);
List<SaleRegulations> results =new ArrayList<>();
results.add(saleRegulations1);
results.add(saleRegulations2);
when(salesRegulationsRepository.findByUserId(USER_ID)).thenReturn(results);
// when
List<SaleRegulations> regulations = saleRegulationsService.findRegulations(USER_ID, TIMESTAMP);
// then
assertThat(regulations.size()).isEqualTo(2);
assertThat(regulations.get(0)).isEqualTo(saleRegulations1);
...
test
Nie testuj unitowo metod typu przelotka.
Testuj integracyjnie, używając Embedded (lub Fake) DB
CQK radzi
Overmocking
Co znaleźliśmy - ‘perełki’
@RunWith(MockitoJUnitRunner. class)
public class CreateEndpointTest {
@Mock
private RefundsSoapBindingStub npRefundService;
@Mock
private RefundsClient refundsClient;
@Mock
private PayuOrders payuOrders;
@Mock
private SignatureFactory signatureFactory;
@Mock
private RefundInfo refundInfoMock;
...
test
unikaj Mocków z verify(), to betonowanie kodu
CQK radzi
Konkurs Allegro
Kod: 5mSG5t
Najczęstsze błędy
Wstrzykiwanie zależności: pola vs konstruktor:
→ Jasna deklaracja kolaboratorów
→ Rozróżnienie opcjonalnych zależności
→ Łatwiejsza konstrukcja obiektów w testach
Najczęstsze błędy
Niemutowalność:
“Classes should be immutable unless there's a very good
reason to make them mutable....If a class cannot be made
immutable, limit its mutability as much as possible.”
Joshua Bloch, Effective Java
Najczęstsze błędy
Mockowanie nie swoich rzeczy:
(a.k.a: Don’t mock what you don’t own)
→ zmiany poza naszą kontrolą
→ podwójnie kosztowne (test / prod)
→ przekonaliśmy zespoły do zewnętrznego review
→ konwencja: should + given/when/then
→ przekonaliśmy większość zespołów do Spocka
→ zespoły piszą coraz lepsze testy integracyjne
→ Overmockingu mniej
→ zdecydowanie widać poprawę ogólnej jakości kodu
Sukcesy
→ review w ramach jednego zespołu często nie wystarcza
→ potrzeba systematyczności - jednorazowy zryw to za mało
→ otwartość na feedback
→ cierpliwość - efekty nie przyjdą od razu
Wnioski
Q/A?
Znajdziesz nas:
Blog: allegrotech.io
Twitter: @allegrotechblog
pracuj z nami
kariera.allegro.pl

Confitura 2015 - Code Quality Keepers @ Allegro

  • 1.
    Code Quality Keepers opowieściz pola bitwy o jakość kodu Rafał Głowiński, Bartosz Walacik Confitura 2015
  • 2.
    O nas Rafał Głowiński @GlowinskiRafal BartoszWalacik @BartoszWalacik http://javers.org
  • 3.
    O czym będziemymówić Pytania: → Jak dbać o jakość kodu w zespole? Code review? → A co jeśli mamy 40+ zespołów? → Jak zadbać o jakość na poziomie całej organizacji?
  • 4.
    O czym będziemymówić Opowiemy: → Po co CQK? → Jak zdefiniować kryteria jakości i ocenić kod różnych zespołów → Jak robimy prawdopodobnie największe Code Review w Polsce → Co udało nam się znaleźć: “perełki”, typowe błędy → A także… co udało nam się osiągnąć
  • 5.
    → 40+ zespołów(dev + infra) Zespoły w Allegro → 4 miasta (Poznań, Toruń, Warszawa, Kraków) → Często doświadczenie głównie w PHP → Większość pisze i utrzymuje mikrousługi na JVM
  • 6.
    Jak powstało CQKi po co? Pierwszy skład zespołu CQK: → doświadczeni inżynierowie → zaangażowani w community → autorytet nie hierarchia → kilka osób myślących podobnie, startup
  • 7.
    Jak powstało CQKi po co? Pierwszy Cel: Przejrzeć i ocenić (w miarę) obiektywnie cały kod Pierwsze review: → 1 dzień w 1 salce → kilkadziesiąt projektów do przejrzenia → 15 minut na projekt (!) → totalna partyzantka
  • 8.
    Jak powstało CQKi po co? Wnioski z pierwszego review: → 15 min na projekt to za mało → potrzebna jest metodyka i organizacja → koncentracja na wybranych aspektach Nowe Cele: → Cel 1: podnoszenie jakości kodu w całej organizacji poprzez review, feedback i mentoring → Cel 2: wkład do ocen technicznych zespołów
  • 9.
    Jak pracujemy → Zaczęliśmyod garstki osób, teraz jest nas więcej → Co kwartał zmieniamy metodykę review → Merytoryczny feedback → Przejście na tryb “push”
  • 10.
    Metodyka review: pierwszykwartał “Wrażenia artystyczne” (15 min na projekt…)
  • 11.
    Metodyka review: drugikwartał Duże i częste naruszenia podstawowych zasad Są błędy, ale generalnie jest ok Jest dobrze!
  • 12.
    Magic Servlet Antipatternis Back: → logika biznesowa/dostęp do repozytorium w kontrolerze → nieprawidłowe wykorzystanie frameworków (np. ręczne mapowanie wyjątków na kodu HTTP) Metodyka review: drugi kwartał Zwięzłość i czytelność testów: → struktura testów, prawidłowe wykorzystanie bibliotek → co testują testy → mocks (overmocking, mockowanie nie swoich klas)
  • 13.
    Szkolenia: trzeci kwartał →Naming i omówienie bytów w testach (Stub, Mock, Spy) → Spock: Data Driven, wyjątki, asercje, Groovy → (*) JUnit: Mockito, AssertJ, JUnitParams, catch-exception → Overmocking + verify = betonowy kod → Testy integracyjne: bazy danych, REST
  • 14.
    Zwięzłość i czytelnośćtestów cd. Metodyka review: aktualnie Model domenowy: → klasy modelu → organizacja pakietów → powiązanie z bibliotekami / infrastrukturą
  • 15.
    Metodyka review: aktualnie Modeldomenowy: brak modelu domenowego: mutowalne & anemiczne POJO operujące na nich klasy Service
  • 16.
    Metodyka review: aktualnie Modeldomenowy: kod jest podzielony horyzontalnie domena powiązana z infrastrukturą (zapytania do baz danych, skomplikowane logowanie/monitoring, itp.) widać starania odejścia od anemiczności modelu (rozsądne podejście do mutowalności, obiekty zyskują funkcjonalności)
  • 17.
    Metodyka review: aktualnie Modeldomenowy: ładny model domenowy zgodny z OOP struktura pakietów zorientowana domenowo domena jest wyraźnie odseparowana od infrastruktury podział pakietowy zgodnie z wytycznymi np. Hexagonal Architecture
  • 18.
    Eksperyment: review koduJS Opowiemy o tym za rok ;)
  • 19.
    Główna działalność CQK Tworzeniefeedbacków dla zespołów → Zespół zgłasza do feedbacku projekt lub PR → Przeciętny projekt ma ~10KLOC
  • 20.
    Co to jestfeedback Kilkustronicowy dokument na WIKI, lista punktów, np: dobre testy integracyjne (np BlacklistEndpointIntegrationSpec.groovy) SalesRegulationsFilterTest.java - do testu tego rodzaju prostych funkcji używajcie podejścia data-driven test Klasa Pdf.java mogłyby być immutable, czyli zamiast: Pdf pdf = new Pdf(); pdf .setContent(documentContent); lepiej Pdf pdf = new Pdf (documentContent); długa implementacja equals() w BlacklistRequest.java. Łatwo się pomylić w takim kodzie. Czy ten equals() jest do czegoś potrzebny?
  • 21.
    Fazy życia feedbacku:koszt os./h I. Review kodu + spisanie 2-3 II. Weryfikacja 1-2 III. Publikacja i uwagi od zespołu 1 IV. Przepracowanie w zespole 0 Jak powstaje feedback
  • 22.
    Faza I. Reviewkodu + spisanie uwag → Keeper klonuje repo i ogląda kod → Nie uruchamiamy tooli do statycznej analizy → Keeper tworzy dokument feedbacku i proponuje ocenę Jak powstaje feedback
  • 23.
    Faza II. Weryfikacja →Drugi Keeper przegląda feedback (kontrola merytoryczna) → Przy rozbieżnościach - uzgadniamy w ramach CQK → Kontrola czy feedback jest friendly → Drugi Keeper często dopisuje swoje uwagi → Approve Jak powstaje feedback
  • 24.
    Faza III. Publikacjai uwagi od zespołu → Feedback jest przekazywany do zespołu → Zachęcamy zespół do ustosunkowania się do feedback’u Czy modyfikujemy feedback? → Świadome i uzasadnione naruszenia reguł → My czegoś nie zrozumieliśmy (brak kontekstu) Jak powstaje feedback
  • 25.
    Faza IV. Przepracowaniefeedbacku w zespole Mamy nadzieję, że zespół przekona się do naszych uwag i propozycji Jak powstaje feedback
  • 26.
    Co znaleźliśmy -big picture
  • 27.
    Co znaleźliśmy -big picture
  • 28.
    Główne grzechy: → TDD- Test Driven Development → Anemic Domain Model Co znaleźliśmy - big picture
  • 29.
    TDD - problemy: →Overmocking → Mocking What-You-Don’t-Own → Testy są kopią implementacji → Za mało testów integracyjnych → Testy pisane pod Coverage → Testy bez wartości dokumentacyjnej → Testowanie implementacji a nie funkcjonalności Co znaleźliśmy - big picture
  • 30.
    Anemic Domain Model →Anemiczne, mutowalne Encje → Settery i gettery rządzą → Programowanie proceduralne nie obiektowe → Często w ogóle brak wydzielonego Modelu Domenowego Co znaleźliśmy - big picture
  • 31.
    Test unitowy prostegokonstruktora Co znaleźliśmy - ‘perełki’
  • 32.
    public class ClientExceptionextends RuntimeException { static final long serialVersionUID = -712897190232112939L; public ClientException(String message) { super(message); } public ClientException(String message, Throwable cause) { super(message, cause); } } src
  • 33.
    @Test public void shouldSetMessageAndCauseInConstructor(){ //given String exceptedMessage = "Very bad exception"; Exception expectedCauseException = new Exception("This is cause exception"); //when ClientException exception = new ClientException(exceptedMessage, expectedCauseException); String message = exception.getMessage(); Throwable causeException = exception.getCause(); //then assertEquals(exceptedMessage, message); assertEquals(expectedCauseException, causeException); } test
  • 34.
    Nie testuj unitowoprostych konstruktorów oraz metod CQK radzi
  • 35.
    Test unitowy Serwisu-przelotki Coznaleźliśmy - ‘perełki’
  • 36.
    public class SaleRegulationsService{ public List<SaleRegulations> findRegulations(String userId, Long timestamp) { List<SaleRegulations> results; if (timestamp == null) { results = salesRegulationsRepository.findOneByUserId(userId); } else { results = salesRegulationsRepository.findByUserId(userId); } return results; } ... } src
  • 37.
    @Test public void shouldReturnMoreResultsWhenTimestampIsGiven(){ // given SaleRegulationsService saleRegulationsService =new SaleRegulationsService(salesRegulationsRepository, eventProducer); SaleRegulations saleRegulations1 =new SaleRegulations(USER_ID, SALE_REGULATIONS_TEXT, TIMESTAMP); SaleRegulations saleRegulations2 =new SaleRegulations(USER_ID, SALE_REGULATIONS_TEXT_VERSION_2, TIMESTAMP2); List<SaleRegulations> results =new ArrayList<>(); results.add(saleRegulations1); results.add(saleRegulations2); when(salesRegulationsRepository.findByUserId(USER_ID)).thenReturn(results); // when List<SaleRegulations> regulations = saleRegulationsService.findRegulations(USER_ID, TIMESTAMP); // then assertThat(regulations.size()).isEqualTo(2); assertThat(regulations.get(0)).isEqualTo(saleRegulations1); ... test
  • 38.
    Nie testuj unitowometod typu przelotka. Testuj integracyjnie, używając Embedded (lub Fake) DB CQK radzi
  • 39.
  • 40.
    @RunWith(MockitoJUnitRunner. class) public classCreateEndpointTest { @Mock private RefundsSoapBindingStub npRefundService; @Mock private RefundsClient refundsClient; @Mock private PayuOrders payuOrders; @Mock private SignatureFactory signatureFactory; @Mock private RefundInfo refundInfoMock; ... test
  • 41.
    unikaj Mocków zverify(), to betonowanie kodu CQK radzi
  • 42.
  • 43.
    Najczęstsze błędy Wstrzykiwanie zależności:pola vs konstruktor: → Jasna deklaracja kolaboratorów → Rozróżnienie opcjonalnych zależności → Łatwiejsza konstrukcja obiektów w testach
  • 44.
    Najczęstsze błędy Niemutowalność: “Classes shouldbe immutable unless there's a very good reason to make them mutable....If a class cannot be made immutable, limit its mutability as much as possible.” Joshua Bloch, Effective Java
  • 45.
    Najczęstsze błędy Mockowanie nieswoich rzeczy: (a.k.a: Don’t mock what you don’t own) → zmiany poza naszą kontrolą → podwójnie kosztowne (test / prod)
  • 46.
    → przekonaliśmy zespołydo zewnętrznego review → konwencja: should + given/when/then → przekonaliśmy większość zespołów do Spocka → zespoły piszą coraz lepsze testy integracyjne → Overmockingu mniej → zdecydowanie widać poprawę ogólnej jakości kodu Sukcesy
  • 47.
    → review wramach jednego zespołu często nie wystarcza → potrzeba systematyczności - jednorazowy zryw to za mało → otwartość na feedback → cierpliwość - efekty nie przyjdą od razu Wnioski
  • 48.
  • 49.
    Znajdziesz nas: Blog: allegrotech.io Twitter:@allegrotechblog pracuj z nami kariera.allegro.pl