Slide 1

Slide 1 text

Strategie pracy z Legacy Code PHPers Summit 2016, Poznań, 20.08.2016 r. Tomasz Gramza mail: [email protected] twitter: @tomaszgramza linkedin: @tomaszgramza github: @to-masz zmiany

Slide 2

Slide 2 text

2

Slide 3

Slide 3 text

3 source: Aled Lewis CAN’T TOUCH THIS

Slide 4

Slide 4 text

4

Slide 5

Slide 5 text

Agenda 5 Legacy Code praca z Strategie zmiana 1. 2. 3. Co to? Skąd się bierze? Po co? Przykłady … Potrzeba zmiany Dobra zmiana :)

Slide 6

Slide 6 text

6

Slide 7

Slide 7 text

7

Slide 8

Slide 8 text

8

Slide 9

Slide 9 text

9 Legacy code - każdy kod, który jako programista odziedziczasz po kimś innym.

Slide 10

Slide 10 text

10

Slide 11

Slide 11 text

11 Legacy code - każdy kod, który jako programista odziedziczasz po kimś innym. … i po sobie.

Slide 12

Slide 12 text

12 image: www.freeimages.co.uk

Slide 13

Slide 13 text

13 image: http://cooking.stackexchange.com/questions/54802/

Slide 14

Slide 14 text

Legacy Code - skojarzenia Nieprzejrzysta, splątana struktura 14 Brak testów Niezrozumiały Muszę z nim pracować, a nie mam ochoty Tydzień walki z dodaniem funkcjonalności, której zrobienie powinno zająć tylko chwilę

Slide 15

Slide 15 text

15

Slide 16

Slide 16 text

16 Legacy code - kod, z którego nie jesteśmy już zadowoleni - kod, który nam ciąży - kod, o którym chcielibyśmy zapomnieć

Slide 17

Slide 17 text

17 Skąd sie bierze Legacy Code?

Slide 18

Slide 18 text

18

Slide 19

Slide 19 text

error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING 19

Slide 20

Slide 20 text

20 iteracja

Slide 21

Slide 21 text

21 Problem Rozwiazanie Szybko, ale bałaganiarsko Dokladnie, ale wolno

Slide 22

Slide 22 text

22 ID_LOKALIZACJA WOJEWÓDZTWO MIASTO DZIELNICA 1 WIELKOPOLSKIE POZNAŃ GRUNWALD 2 WIELKOPOLSKIE POZNAŃ NOWE MIASTO ID_LOKALIZACJA ID_WOJEWÓDZTWO ID_MIASTO ID_DZIELNICA 1 14 220 34 2 14 220 35 WOJEWÓDZTWA MIASTA DZIELNICE Co jesli dwa miasta o tej samej nazwie?

Slide 23

Slide 23 text

23 Dziedzictwo Szybka praca Efektywna praca

Slide 24

Slide 24 text

24 Dziedzictwo Szybka praca Efektywna praca A. B. Refaktoryzacja

Slide 25

Slide 25 text

Ile Legacy Code mamy w projekcie? ●  Zduplikowany kod ●  Naruszenia reguł ●  Nieudokumentowane publicze metody ●  Pokrycie złożoności kodu testami poniżej 80% ●  Nadmierna złożoność metod/klas 25 https://hub.docker.com/_/sonarqube/

Slide 26

Slide 26 text

26

Slide 27

Slide 27 text

27

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

29

Slide 30

Slide 30 text

Powody zmiany oprogramowania ●  Dodanie funkcjonalności ●  Naprawa błędu ●  Refaktoryzacja ●  Optymalizacja 30

Slide 31

Slide 31 text

31

Slide 32

Slide 32 text

32 1.  Jakie zmiany musimy wprowadzic? 2.  Skad bedziemy wiedziec, ze dokonalismy poprawnych zmian? 3.  Skad bedziemy wiedziec, ze nic nie popsulismy?

Slide 33

Slide 33 text

Algorytmy zmiany kodu 1.  Znajdź miejsca zmiany 2.  Znajdź miejsca do przetestowania 3. Spraw aby kod był testowalny 4. Napisz testy 5. Dokonaj zmiany 33 1.  Dokonaj zmiany 2.  Módl się vs. 1.  Jakie zmiany musimy wprowadzic? 2.  Skad bedziemy wiedziec, ze dokonalismy poprawnych zmian? 3.  Skad bedziemy wiedziec, ze nic nie popsulismy?

Slide 34

Slide 34 text

+ Dobra zmiana 34 Do it right the second time ●  Dodanie funkcjonalności ●  Naprawa błędu Refaktoryzacja

Slide 35

Slide 35 text

Refaktoryzacja - Przybornik ●  VCS ●  PHP 7 ●  IDE ●  Wytyczne do formatowania kodu: PSR 1/2 + php-cs-fixer ●  Composer (autoloader) 35

Slide 36

Slide 36 text

Refaktoryzacja - Brak globalnych zależności ●  Brak zmiennych globalnych global $user; ●  Brak super globalnych zmiennych $_GET, $_SESSION, $COOKIE 36

Slide 37

Slide 37 text

class Foo { public function __construct() { } public function getBar() { global $user; return $user[‘name’]; } } 37 class Foo { private $user; public function __construct($user) { $this->user = $user; } public function getBar() { return $this->user[‘name’]; } }

Slide 38

Slide 38 text

class Foo { public function __construct() { } public function getBar() { return $_COOKIE[‘user’]; } } 38 class Foo { private $request; public function __construct( ServerRequestInterface $request ) { $this->request = $request; } public function getBar() { return $this->request ->getCookieParams()[‘user’]; } }

Slide 39

Slide 39 text

Refaktoryzacja - Odwrócenie sterowania (Dependency Injection) 39 ●  Usługi ●  Fabryki Obiekt może tworzyć inne obiekty albo operować na nich. Nigdy jedno i drugie na raz!

Slide 40

Slide 40 text

class Foo { public function __construct() { } public function bar() { $stats = new Statistics(); $stats->process(); } } 40 class Foo { private $statistics; public function __construct( Statistics $stats ) { $this->statistics = $stats; } public function bar() { return $this->statistics ->process(); } }

Slide 41

Slide 41 text

class Foo { public function __construct() { } public function bar($data) { $collection = []; foreach ($data as $row) { $collection[] = new Item($row); } return $collection; } } 41 class Foo { private $factory; public function __construct( ItemFactory $factory ) { $this->factory = $factory; } public function bar($data) { $collection = []; foreach ($data as $row) { $collection[] = $this->factory ->newInstance($row); } return $collection; } }

Slide 42

Slide 42 text

Refaktoryzacja - automatyzacja testów 42 ●  Jeśli możesz napisać test, który sprawdza tylko wynik działania metody, powinieneś to zrobić ●  Jeśli możesz napisać test funkcjonalny albo integracyjny, który oddziałuje na baze danych, połączyenie sieciowe, czy system plików, powinieneś to zrobić ●  Jeśli możesz napisać luźny test jednostkowy, który testuje konkretne klasy (bez pełnej izolacji), powinieneś to zrobić ●  Jeśli możesz napisać prawidłowy test, który wykorzystuje imitacje testowe (test doubles), żeby w pełni odizolować testowaną klasę, powinieneś to zrobić An imperfect test today is better than a perfect test someday.

Slide 43

Slide 43 text

Refaktoryzacja - Separacja danych ●  Gateways 43

Slide 44

Slide 44 text

class Foo { private $db; public function __construct( Db $db ) { $this->db = $db; } public function getBar($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } } 44 class Foo { private $gateway; public function __construct( BarGateway $gateway ) { $this->gateway = $gateway; } public function getBar($id) { return $this->gateway ->findById($id); } } ...

Slide 45

Slide 45 text

class Foo { private $db; public function __construct( Db $db ) { $this->db = $db; } public function getBar($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } } 45 ... class BarGateway { private $db; public function __construct( Db $db ) { $this->db = $db; } public function findById($id) { $sql = “SELECT * FROM bar WHERE id = :id”: return $db->fetchRow($sql, [‘:id’ => $id]); } }

Slide 46

Slide 46 text

Refaktoryzacja - Separacja prezentacji 46 ●  Prezenters

Slide 47

Slide 47 text

hasDistrict()): ?> getCityName()?>. getDistrictName()?> getCityName()?> 47 class StandardLocationPresenter { private $location; public function __construct( Location $location ) { $this->location = $location; } public function getName(): string { ... } } ... getName()?>

Slide 48

Slide 48 text

Refaktoryzacja - Architektura 48

Slide 49

Slide 49 text

49

Slide 50

Slide 50 text

Strategia Refaktoryzuj do Open-Closed 50 Robot +fire() +getWeapon(): Cannon ... $robot = new Robot(); $robot->fire();

Slide 51

Slide 51 text

Strategia Refaktoryzuj do Open-Closed 51 Robot +fire() +getWeapon(): Cannon ... RobotWithBfg +fire() +getWeapon(): BFG9000 ... $robot = new RobotWithBfg(); $robot->fire();

Slide 52

Slide 52 text

Strategia Refaktoryzuj do Open-Closed 52 Robot +fire() +getWeapon(): Cannon ... RobotWithBfg +fire() +getWeapon(): BFG9000 ... $robot = new RobotWithBfg(); $robot->fire();

Slide 53

Slide 53 text

Strategia Refaktoryzuj do Open-Closed 53 Robot +fire() ... Cannon +fire() +getWeapon(): Cannon ... $robot = new Robot(); $robot->fire();

Slide 54

Slide 54 text

Strategia Refaktoryzuj do Open-Closed 54 Robot +fire() ... Cannon +fire() ... $robot = new Robot(); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ...

Slide 55

Slide 55 text

Strategia Refaktoryzuj do Open-Closed 55 Robot +fire() ... Cannon +fire() ... $robot = new Robot(); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ... BFG900 +fire() ...

Slide 56

Slide 56 text

Strategia Refaktoryzuj do Open-Closed 56 Robot +fire() ... Cannon +fire() ... $weapon = new BFG9000(); $robot = new Robot($weapon); $robot->fire(); Weapon +fire() +getWeapon(): Weapon ... BFG900 +fire() ...

Slide 57

Slide 57 text

Strategia Duszenie systemu 57 BookingService +book() ... $service = new BookingService(); $service>book();

Slide 58

Slide 58 text

Strategia Duszenie systemu 58 BookingService +book() ... $service = new BookingService(); $service>book(); BookingServiceV2 +bookItem() +cancelItemBooking() ...

Slide 59

Slide 59 text

Strategia Duszenie systemu 59 BookingService +book() ... $service = new BookingService(); $service>book(); $service = new BookingServiceV2( new BookingService(); ); $service>bookItem(); class BookingServiceV2 { ... public function bookItem() { $this->legacyService->book(); } } BookingServiceV2 +bookItem() +cancelItemBooking() ...

Slide 60

Slide 60 text

Strategia Duszenie systemu 60 BookingService +book() ... $service = new BookingServiceV2(); $service>bookItem(); class BookingServiceV2 { ... public function bookItem() { // new implementation } } BookingServiceV2 +bookItem() +cancelItemBooking() ...

Slide 61

Slide 61 text

Strategia Duszenie systemu 61 $service = new BookingServiceV2(); $service>bookItem(); class BookingServiceV2 { ... public function bookItem() { // new implementation } } BookingServiceV2 +bookItem() +cancelItemBooking() ...

Slide 62

Slide 62 text

Strategia Flaga funkcjonalności 62 BookingService +book() ... $service = new BookingService(); $service>book();

Slide 63

Slide 63 text

Strategia Flaga funkcjonalności 63 BookingService +book() ... $isNewBookingFeatureActive; $service = new BookingService( $isNewBookingFeatureActive ); $service>book(); class BookingService { ... public function book() { if ($this ->isNewBookingFeatureActive) { return ... } ... } }

Slide 64

Slide 64 text

Legacy Code praca z Strategie Podsumowanie 64 Da się z nim żyć! Nie działaj chaotycznie! - Korzystaj ze strategii Do it right the second time! - jesli dostarczasz zbyt szybko - zaplanuj refaktoryzację! zmiana 1. 2. 3.

Slide 65

Slide 65 text

65 Q & A