2. O mnie
• Marek Keżel
• 7 lat doświadczenia w PHP (5.6, 7, 8, 9, 10, 11, 12 J)
• Programista w Innovation Software Sp. z o. o.
• Niezależny konsultant ds. oprogramowania w Specific Sp. z o. o.
• NDProdukcja – twórca wewnętrznego systemu ERP jednego z
największych producentów mebli tapicerowanych w Polsce
• Mąż i Tata
3. Agenda
•Czym jest refaktoryzacja
•Szybki przegląd technik refaktoryzacji
•Omówienie 8 wybranych technik
•Czemu nie zawsze będziesz refaktoryzował kod
•Pytania, sugestie, ewentualne wygwizdanie prelegenta
5. Czym jest refaktoryzacja ?
Refaktoryzacja to wymyślony proces przez rozpieszczonych
programistów, którzy myślą, że głupi ludzie zapłacą im ponownie za tą
samą pracę albo co gorsze za udawanie, że coś robią.
Janusz, dyrektor operacyjny, członek zarządu
6. Czym jest refaktoryzacja ?
The main purpose of refactoring is to fight technical debt. It transforms
a mess into clean code and simple design.
https://refactoring.guru/refactoring/what-is-refactoring
In computer programming and software design, code refactoring is the
process of restructuring existing computer code—changing the
factoring—without changing its external behavior. Refactoring is
intended to improve the design, structure, and/or implementation of
the software (its non-functional attributes), while preserving its
functionality.
https://en.wikipedia.org/wiki/Code_refactoring
7. Czym jest refaktoryzacja ?
Głównym celem przeprowadzania refaktoryzacji jest walka z długiem
technologicznym*, przeistoczenie brzydkiego kodu w czysty* ZROZUMIAŁY DLA
INNYCH.
*Dług technologiczny (Technical Debt) jest naszym mniej lub bardziej świadomym
działaniem, które ciągnie nasze oprogramowanie w dół. Ignorujemy dobre praktyki
w imię wyższych celów (tak nam się wydaje…) jak np. szybszy release przez co
pomijamy pisanie testów, nie kontrolujemy zależności, tworzymy niezrozumiałe
nazwy pól oraz metod. Czasami jesteśmy „dłużni” ponieważ robimy to
nieświadomie – nie mamy wiedzy jak coś zrobić lepiej. Czym się objawia dług?
Wydłużonym czasem wprowadzania zmian w kodzie, większą ilość błędów, coraz
większą ilością próśb o przeniesienie do innego projektu.
*Czysty kod (Clean Code) to taki który jest łatwy w utrzymaniu, zrozumiały dla
innych programistów (bez żadnej magii, dziwacznego nazewnictwa, rozdętych
metod oraz klas), bez zduplikowanego kodu, pozbawiony tzw. zapaszków (Code
Smells). Czysty kod czyta się jak dobrą książkę z uśmiechem a nie z przerażeniem w
stylu „co to $%^& jest !?”.
9. Kiedy wykonywać refaktoryzację ?
• Kiedy patrzymy na kod i czujemy, że skręca nas w środku na
sam widok – to jest idealny moment!
• Kiedy dodajemy nową funkcjonalność do naszego programu
(obie czynności traktujemy oddzielnie!)
• Kiedy naprawiamy jakiś problem
• Kiedy robimy codereview
• Kiedy robimy coś i mamy Déjà vu, że coś takiego już robiliśmy
10. Kiedy NIE wykonywać refaktoryzacji ?
• Kiedy nie rozumiemy kodu nad którym pracujemy
• Kiedy kod już działa produkcyjnie i nie ma z nim żadnych
problemów
• Kiedy projekt jest w stylu napisz i zapomnij
• Kiedy nie mamy pokrycia w testach tego co chcemy
zrefaktoryzować*
*dopiszemy te testy lub każdy z nas podejmie super ważną decyzje mając z
tyłu głowy złotą zasadę (przetestujemy na produkcji) J
11. Jak wykonać refaktoryzację ?
„Refaktoryzacja powinna być wykonana jako seria małych zmian które czynią
nasz kod lepszym, pozostawiając jednocześnie program w stanie gotowości”
• Jeżeli kod po wykonaniu refaktoryzacji dalej jest „brzydki” wracamy do
początku i robimy to jeszcze raz.
• Podczas refaktoryzacji nie powinny powstawać żadne nowe funkcjonalności
(proces refaktoryzacji powinien być oddzielny)
• Wszystkie istniejące testy muszą zostać poprawnie wykonane (czasami to
testy są winne niepowodzeń np. są zbyt nisko poziomowe, przez co musimy
dokonać refaktoryzacji samych testów)
https://refactoring.guru/refactoring/how-to
13. Kompozycja metod
• Extract Method
• Inline Method
• Extract Variable
• Inline Temp
• Replace Temp with Query
• Split Temporary Variable
• Remove Assignments to
Parameters
• Replace Method with Method
Object
• Substitute Algorithm
14. Przenoszenie funkcjonalności
• Move Method
• Move Field
• Extract Class
• Inline Class
• Hide Delegate
• Remove Middle Man
• Introduce Foreign Method
• Introduce Local Extension
15. Ogranizacja danych
• Change Value to Reference
• Change Reference to Value
• Duplicate Observed Data
• Self Encapsulate Field
• Replace Data Value with Object
• Replace Array with Object
• Change Unidirectional
Association to Bidirectional
• Change Bidirectional Association
to Unidirectional
• Encapsulate Field
• Encapsulate Collection
• Replace Magic Number with
Symbolic Constant
• Replace Type Code with Class
• Replace Type Code with
Subclasses
• Replace Type Code with
State/Strategy
• Replace Subclass with Fields
16. Upraszczanie wyrażeń regulanych
• Consolidate Conditional
Expression
• Consolidate Duplicate
Conditional Fragments
• Decompose Conditional
• Replace Conditional with
Polymorphism
• Remove Control Flag
• Replace Nested Conditional with
Guard Clauses
• Introduce Null Object
• Introduce Assertion
17. Upraszczanie wywołań metod / funkcji
• Add Parameter
• Remove Parameter
• Rename Method
• Separate Query from Modifier
• Parameterize Method
• Introduce Parameter Object
• Preserve Whole Object
• Remove Setting Method
• Replace Parameter with Explicit
Methods
• Replace Parameter with Method
Call
• Hide Method
• Replace Constructor with Factory
Method
• Replace Error Code with
Exception
• Replace Exception with Test
18. Upraszczanie abstrakcji
• Pull Up Field
• Pull Up Method
• Pull Up Constructor Body
• Push Down Field
• Push Down Method
• Extract Subclass
• Extract Superclass
• Extract Interface
• Collapse Hierarchy
• Form Template Method
• Replace Inheritance with
Delegation
• Replace Delegation with
Inheritance
20. UWAGA!
W celu uproszczenia a zarazem
zmieszczenia się w limicie
czasowym, nie wszystkie drobne
kroki refaktoryzacji zostaną
pokazane na slajdach.*
*Ale prowadzący o nich wspomni :)
22. Extract Method
Kiedy? Metoda ma za dużo odpowiedzialności (narusza SRP). Czujemy,
że coś nam nie pasuje.
Co zyskamy? Ograniczymy odpowiedzialność metody, zwiększymy
czytelność kodu.
Jak to zrobić? Tworzymy nową metodę (modyfikator dostępu?),
przenosimy fragment kodu do nowej metody, jeżeli kod korzysta ze
zmiennych zadeklarowanych wcześniej – przekazujemy je, w bazowej
metodzie odwołujemy się do nowo utworzonej, wykonujemy testy
Metoda odwrotna? Inline Method
23. Extract Method - przykład
class OptymalizerkaBoki
{
public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void
{
$formatkiWezglowia = [];
foreach ($formatki as $formatka) {
if ($formatka->czyWezglowie() === false) {
continue;
}
$formatkiWezglowia[] = $formatka;
}
$optymalizerkaWiadomosc = new OptymalizerkaWiadomosc();
$optymalizerkaWiadomosc->setFormatki($formatkiWezglowia);
$this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc);
}
}
24. Extract Method - przykład
class OptymalizerkaBokiRefactoryzacja
{
public function wyslijFormatkiNaOptymalizerke(array $formatki, int $optymalizerkaID): void
{
$formatkiWezglowia = $this->wyfiltrujFormatkiWezglowia($formatki);
$optymalizerkaWiadomosc = new OptymalizerkaWiadomosc();
$optymalizerkaWiadomosc->setFormatki($formatkiWezglowia);
$this->getBus($optymalizerkaID)->wyslij($optymalizerkaWiadomosc);
}
private function wyfiltrujFormatkiWezglowia(array $formatki): array
{
$formatkiWezglowia = [];
foreach ($formatki as $formatka) {
if ($formatka->czyWezglowie() === false) {
continue;
}
$formatkiWezglowia[] = $formatka;
}
return $formatkiWezglowia;
}
}
26. Exctract Class
Kiedy? Klasa posiada wiele odpowiedzialności, robi za dużo, narusza zasadę
SRP (Single Responsibility Principle), jest mniej czytelna i trudna w analizie
Co zyskamy? Zbliżymy się albo osiągniemy zgodność z zasada SRP,
zwiększymy tolerancję na zmiany, poprawimy czytelność oraz zrozumienie
kodu
Jak to zrobić? Tworzymy nową klasę oraz tworzymy relację między klasami
(najlepiej jednokierunkową) przenosimy do niej docelową funkcjonalność
(korzystamy z technik Move Field i Move Method) uważamy na
modyfikatory dostępu (zaczynamy od prywatnych), wykonujemy testy
Wady? Zbyt często wykorzystywana, powoduje mnóstwo nowych klas często
małych i nie koniecznie z odpowiednimi odpowiedzialnościami
Metoda odwrotna? Inline Class
27. Extract Class - przykład
class SaturnService
{
public function __construct()
{
$this->client = new SoapClient(self::SATURN_API_WSDL, $this->options);
}
public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void
{
// coś robimy z zamówieniem oraz pobieramy docID
$response = $this->potwierdzZamowienieWyslijRequest($saturnDocID);
//coś robimy z odpowiedzą dalsza cześc logiki
}
private function potwierdzZamowienieWyslijRequest(string $saturnDocID): array
{
$result = $this->client->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]);
// cos robimy z rezultatem
return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode];
}
}
28. Extract Class - przykład
class SaturnApi implements SaturnApiInterface
{
public function __construct()
{
$this->soapClient = new SoapClient(self::SATURN_API_WSDL, $this->options);
}
public function potwierdzZamowienieWyslijRequest(string $saturnDocID): array
{
$result = $this->soapClient->ZdConfirm(['ZdConfirm' => ['DocID' => $saturnDocID]]);
// cos robimy z rezultatem
return ['stauts' => $result->status, 'confirmCode' => $result->confirmCode];
}
}
29. Extract Class - przykład
class SaturnService
{
public function __construct(SaturnApiInterface $api)
{
$this->apiClient = $api;
}
public function potwierdzZamowienieDoDostawcy(Zamowienie $zamowienie): void
{
// cos robimy z zamówieniem oraz pobieramy docID
$saturnDocID = '';
$response = $this->apiClient->potwierdzZamowienieWyslijRequest($saturnDocID);
//coś robimy z odpowiedzą dalsza cześc logiki
}
}
31. Replace Array With Object
Kiedy? Jak z danymi w tablicy musimy zrobić coś więcej niż tylko je
wyświetlić.
Co zyskamy? Ułatwimy pracę ze strukturą danych (dopilnujemy
właściwych danych we właściwych miejscach). Uzyskamy enkapsulację
– połączymy dane z funkcjonalnością – OOP.
Jak to zrobić? Tworzymy strukturę nowej klasy w oparciu o źródłową
tablicę, zmieniamy w kodzie każde użycie tablicy na nasz nowy obiekt
(jeżeli nie możemy, to musimy zaimplementować funkcjonalność tablicy
w naszej nowej klasie)
Wady? Obiekty są „wolniejsze” zajmują więcej pamięci*.
*https://stackoverflow.com/questions/3709578/using-arrays-vs-objects-for-storing-data
33. Replace Array With Object - przykład
class CentrumService
{
public function wyslijFormatkeDoCentrum(array $formatka): void
{
//czy struktura tablicy $formatka jest poprawna ?
$kubatura = $this->wyliczKubature($formatka);
$polePowierzchni = $this->wyliczPolePowierzchni($formatka);
...
}
public function wyliczKubature(array $formatki): float
public function wyliczPolePowierzchni(array $formatki): float
}
34. Replace Array With Object - przykład
class WyliczonaFormatka
{
private int $id;
private string $typ;
private string $nazwa;
…
public static function utworzZTablicy(array $formatka): self
{
//sprawdzenie poprawnosci otrzymanej tablicy
return new self($formatka['id'], $formatka['typ'], $formatka['nazwa']);
}
public function __construct(int $id, string $typ, string $nazwa)
public function getKubatura(): float
public function getPolePowierzchni(): float
}
35. Replace Array With Object - przykład
class CentrumService
{
public function wyslijFormatkeDoCentrum(WyliczonaFormatka
$wyliczonaFormatka): void
{
$kubatura = $wyliczonaFormatka->getKubatura();
$polePowierzchni = $wyliczonaFormatka->getPolePowierzchni();
...
}
}
37. Consolidate Duplicate Conditional Fragments
Kiedy? Posiadamy zduplikowany kod w różnych gałęziach warunkowych
Co zyskamy? Usuniemy zduplikowany kod J
Jak to zrobić? Przenosimy zduplikowany kod przed lub za (zależnie od
jego lokalizacji) instrukcje warunkowe
38. Consolidate Duplicate Conditional Fragments - przykład
public function actionCreate()
{
$zamowienie = new Zamowienie();
$zamowienie->load($this->request->post('zamowienie'));
if ($this->request->post('lozko')) {
$zamowienie->load($this->request->post('lozko'));
$zamowienie->save();
return $this->redirect(['index']);
} elseif ($this->request->post('szafka')) {
$zamowienie->load($this->request->post('szafka'));
$zamowienie->save();
return $this->redirect(['index']);
} else {
$zamowienie->save();
return $this->redirect(['index']);
}
}
39. Consolidate Duplicate Conditional Fragments - przykład
public function actionCreate()
{
$zamowienie = new Zamowienie();
$zamowienie->load($this->request->post('zamowienie'));
if ($this->request->post('lozko')) {
$zamowienie->load($this->request->post('lozko'));
} elseif ($this->request->post('szafka')) {
$zamowienie->load($this->request->post('szafka'));
}
$zamowienie->save();
return $this->redirect(['index']);
}
41. Replace Nested Conditional With Guard Clauses
Kiedy? Mamy mocno zagnieżdżone instrukcje warunkowe, które tworzą
kształt grotu strzały. Ciężko jest określić który warunek gdzie się kończy i
co robi
Co zyskamy? „Wyprostujemy” nasz kod, zlikwidujemy głębokie
zagnieżdżenia przez co zwiększymy czytelność kodu
Jak to zrobić? Wprowadzamy w każdej instrukcji warunkowej tzw.
Klauzule Ochronne (Guard Clauses), które automatycznie zwracają
jakąś wartość lub przerywają działanie programu wyjątkiem
42. Replace Nested Conditional With Guard Clauses - przykład
if ($zamowienie->czyZakonczone()) {
if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 Sp. z. o.') {
$tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura());
$promocyjnaCena = $this->cennik->standardowaCena() * 5;
$faktura = $this->rozliczenia->wygenerujFakture($zamowienie);
$this->printer->drukuj($faktura);
} else {
$cena = $this->cennik->standardowaCena();
$this->mailer->wyslijZamowienie($zamowienie, $cena);
}
return $this->redirect(['index']);
} else {
throw new Exception('Tylko zakończone zamówienia.');
}
43. Replace Nested Conditional With Guard Clauses - przykład
if ($zamowienie->czyZakonczone() === false) {
throw new Exception('Tylko zakończone zamówienia.');
}
if ($zamowienie->getFirma()->nazwa === 'Januszex 2022 sp. z. o.') {
$tranposrt = $this->spedytor->zarezerwujKubature($zamowienie->getKubatura());
$promocyjnaCena = $this->cennik->standardowaCena() * 5;
$faktura = $this->rozliczenia->wygenerujFakture($zamowienie);
$this->printer->drukuj($faktura);
return $this->redirect(['index']);
}
$cena = $this->cennik->standardowaCena();
$this->mailer->wyslijZamowienie($zamowienie, $cena);
return $this->redirect(['index']);
45. Introduce Parameter Object
Kiedy? Mamy powiązane ze sobą parametry metody, które na dodatek
pojawiają się w różnych metodach. Uwaga!
Co zyskamy? Pozbywamy się zduplikowanego kodu który jest związany
z logiką parametrów, zmniejszamy liczbę parametrów przekazywanych
do metody, zwiększamy czytelność kodu
Jak to zrobić? Tworzymy klasę, która będzie reprezentować nasze
parametry, metody które używały naszych parametrów zmieniamy tak
aby przyjmowały zamiast nich obiekt typu klasy którą stworzyliśmy,
zmieniamy odwołania do parametrów wewnątrz metod na pola,
metody z nowego obiektu
Wady? Technika ta czasami prowadzi do tworzenia klas pozbawionych
funkcjonalności – pojemników na same dane. Co jest nie zgodne z
zasadami OOP.
46. Introduce Parameter Object - przykład
class PracownikUrlop
{
public function getIloscDniPracujacych(
DateTime $poczatek,
DateTime $koniec): int
{
$iloscDniPracujacych = 0;
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
$period = new DatePeriod($poczatek,
new DateInterval('P1D'),
$koniec);
foreach ($period as $date) {
if ($date->format('N') < 6) {
$iloscDniPracujacych++;
}
}
}
return $iloscDniPracujacych;
}
public function getUrlop(DateTime $poczatek,
DateTime $koniec): array
{
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
...
}
}
47. Introduce Parameter Object - przykład
class ZakresDat
{
public function __construct(DateTime
$poczatek, DateTime $koniec)
{
if ($poczatek > $koniec) {
throw new Exception('Błędny zakres dat.');
}
}
public function getPoczatek(): DateTime
public function getKoniec(): DateTime
public function getPeriod(): DatePeriod
{
return new DatePeriod($this->poczatek,
new DateInterval('P1D'),
$this->koniec);
}
}
class PracownikUrlop
{
public function getIloscDniPracujacych(
ZakresDat $zakresDat): int
{
$iloscDniPracujacych = 0;
foreach ($zakresDat>getPeriod() as $date) {
if ($date->format('N') < 6) {
$iloscDniPracujacych++;
}
}
return $iloscDniPracujacych;
}
public function getUrlop(ZakresDat $zakresDat): array
{
...
}
}
49. Replace Error Code with Exception
Kiedy? Chcemy zwrócić jakąś wartość do kodu klienckiego oraz w przypadku
błędu zakomunikować to razem z kodem błędu, lecz nie wiemy do końca jak
to zrobić i sięgamy do sposobów z proceduralnego podejścia tylko, że
zapominamy, że obracamy się w obiektowym świecie, poza tym np. nasza
metoda zwraca więcej niż jeden typ.
Co zyskamy? Uciekniemy od proceduralnego paradygmatu i wrócimy w
ramiona OOP, uściślimy zwracany typ danych z metody, wyjątki oprócz kodów
błędów mogę przechowywać dodatkowe informacje np. opis błędu
Jak to zrobić? Zmieniamy wszystkie sprawdzanie zwracanych kodów błędów
na bloki try-catch, wewnątrz metody rzucamy wyjątki zamiast zwracania
kodów błędów
Wady? Używanie wyjątków do przekazywania komunikatów powoduje wystp
wielu bloków try-catch. Pamiętajmy, że wyjątki służą do komunikowania o
błędach!
50. Uwaga!
Technika to operuje na zwracanym
kodzie błędu – l. całkowita. W
przykładzie posłużę się zamiast tego
tablicą – niestety dość często
stosowanym rozwiązaniem w PHP
51. Replace Error Code* with Exception - przykład
public function wyliczCene(ZamowieniePozycja $pozycja): array
{
if ($pozycja->getFirma()->nazwa === 'Januszex 2022 sp. z o. o.') {
return [
'status' => 'error',
'msg' => 'Tej firmy nie obsługujemy!'
];
}
if ($pozycja->czyLozko() === false) {
return [
'status' => 'error',
'msg' => 'Wyliczamy cenę tylko dla łóżek'
];
}
return [
'status' => 'success',
'cena_netto' => $this->bardzoZaawansowanaLogika($pozycja),
];
}
52. Replace Error Code* with Exception - przykład
$cenaNetto = $cennik->wyliczCeneNetto($pozycja);
if ($cenaNetto['status'] === 'error') {
//tutaj obsługujemy błąd
}
$pozycjaFaktury->cenaNetto = $cenaNetto['cena_netto'];
53. Replace Error Code* with Exception - przykład
class Cennik
{
public function wyliczCeneNetto(ZamowieniePozycja $pozycja): float
{
if ($pozycja->getFirma()->naz === 'Januszex 2022 sp. z o. o.') {
throw new TejFirmyNieObslugujemyException(
'Tej firmy nie obslugujemy!'
);
}
if ($pozycja->czyLozko() === false) {
throw new BlednyTypPozycjiException(
'Wyliczamy cenę tylko dla łóżek'
);
}
return $this->bardzoZaawansowanaLogika($pozycja);
}
}
56. Extract Subclass
Kiedy? Nasza klasa posiada pola oraz metody używane tylko w jednym
określonym przypadku. Mimo że ten konkretny przypadek wygląda na
odrębny to jest powiązany w jakiś sposób z ogólnym kontekstem przez
co nie może zostać wyodrębniony do w pełni niezależnej klasy.
Co zyskamy? Konkretyzację przypadków z wyodrębnioną logiką
powiązaną właśnie z tym przypadkiem, będziemy mogli łatwo tworzyć
nowe przypadki jako podklasy
Jak to zrobić? Zmieniamy
Wady? Ta technika jest niemożliwa lub trudna w implementacji w
przypadku bardzo rozbudowanych hierarchii klas.
57. Extract Subclass - przykład
class Akord
{
private $tkaninaID;
private $ramaLozkaID;
private $czyOtwieranaPufa;
public function wyliczCzas(): int
{
$this->przypiszParametryDoWzorow();
$this->wylicz();
$this->optymalizujCzas();
return $this->wyliczonyCzas;
}
private function przypiszParametryDoWzorow(): void
{
//logika przypisywania
if ($this->pozycja->czyLozko()) {
$this->przypiszParametryLozkaDoWzorow();
}
}
private function optymalizujCzas(): void
}
58. Extract Subclass - przykład
class Akord
{
protected int $tkaninaID;
public function wyliczCzas(): int
{
$this->przypiszParametryDoWzorow();
$this->wylicz();
$this->optymalizujCzas();
return $this->wyliczonyCzas;
}
protected function przypiszParametryDoWzorow(): void
{
//logika przypisywania
}
private function optymalizujCzas(): void
}
class AkordLozko extends Akord
{
private int $ramaLozkaID;
private bool $czyOtwieranaPufa;
protected function przypiszParametryDoWzorow(): void
{
parent::przypiszParametryDoWzorow();
//specjalna logika oparta o typ pozycji = łózko
}
}
60. Czemu nie zawsze będziesz refaktoryzował kod ?
Ponieważ:
• Nie będziesz miał na to czasu – szybki realease, szybka poprawka
błędu itd.
• Nie będziesz potrafił tego zrobić - mam nadzieje, że ta prezentacja
trochę Ci pomoże
• Nikt nie będzie chciał Ci za to zapłacić – a wiadomo za friko to można
pisać ToDo listy ucząc się nowego frameworka w JS’ach
• Nie będziesz umiał uargumentować zasadności refaktoryzacji ze
strony biznesowej dla przełożonego, zleceniodawcy - postaram się coś
podpowiedzieć
• Nie zrobisz tego, bo nie! - podejmiesz świadomą decyzję z którą
będziesz musiał żyć :)
61. Jak przekonać biznes do refaktoryzacji
• Zrozumieć płaszczyznę, po której porusza się biznes – pieniądze!
• Jeżeli Twoja praca nie przyniesie pieniędzy (zysku) to po co szef / inwestor ma za
to płacić ?
• Pamiętaj, że Twój czas to ich pieniądze !
• Przekonaj ludzi od biznesu, że refaktoryzacja jest inwestycją pełną zysków, tylko,
że w dłuższym terminie.
• Termin ten to nic innego jak przyszłe wdrożenia nowych funkcjonalności lub
utrzymanie istniejącego kodu.
• Owszem na początku ”trzymanie się zasad” wydłuża czas wytwarzania
oprogramowania przez co więcej kosztuje, ale w późniejszym czasie te wartości
się odwracają
• Uwaga! Jeżeli produkt nad którym pracujesz nie ma w planach być rozwijany,
utrzymywany albo jest MVP (minimum viable product) – odpuść refaktoryzację,
oni mają racje, że nie chcą za to zapłacić J