Tiszta kód – 2. rész: függvények

Bevezető

Minden valamirevaló kódsor végső soron egy függvényben köt ki. Valójában ez a kódunk rendezésének első szintje. Ezért nagyon fontos néhány szempontot figyelembe venni.
Mitől lesz tiszta egy függvény? Ez a poszt erről fog szólni.

Első szabály

A függvények legyenek rövidek!

Második szabály

Még annál is rövidebbek!
A nyolcvanas években a kimondatlan szabály az volt, hogy : “Férjen el egy képernyőre.”
Ez manapság már nem helytálló. Akkoriban ugyanis egy képernyő még csak 20 sort tudott megjeleníteni. Manapság ha megdöntjük a képernyőt és kellően kicsire állítjuk a betűméretet, akár 100 sor is elfér a képernyőn.
Mit mondanak a profik?

  • Uncle Bob: “A 4-5 soros függvények még rendben vannak. A 10 soros már nincsen.”
  • Bjarne Stroustrup: „A ’nem fér a képernyőre’ egy elég gyakorlatias megfogalmazása annak, hogy túl hosszú a függvény. Az 1-5 soros függvényeket tekinthetjük normálisnak.”

Sokan gondolhatjátok, hogy ez túlzás, de tény, hogy a nagyon rövid függvényeknek vannak előnyei. Hogy mik ezek?

  • Gyakorlatilag nincs behúzás, ezért a kód olvasásakor kisebb “vermet” kell felépíteni agyunkban, ezért könnyebben átlátjuk a kódot.
  • Egy for vagy while ciklus magjában gyakorlatilag csak 1 sort enged meg a szabály, ezért ha annak az egysoros függvénynek, ami a ciklus magjában van jó nevet adunk, könnyű átlátni, hogy mit is csinál a ciklus.
  • Ha könnyen érthető a logika, akkor a hibák is nehezebben bújnak el a sorok között.
  • Nem kell a kódban görgetni, a navigációban az IDE segít. (Általában Ctrl katt.)

Saját kódjaimban igyekszem tartani az 5-6 soros szabályt, de én nem vagyok annyira radikális, hogy a kapcsos zárójeleket is beleszámítsam.

Miért szeret(jük/tük) a hosszú függvényeket?

A programozók nagy része férfi. Mi férfiak pedig igen csak büszkék vagyunk a tájékozódó képességünkre.
A nagy függvényeket ha megdöntjük, nagyon hasonlítanak egy hegyekkel, völgyekkel szabdalt terepre. Nekünk férfiaknak pedig arra van kitalálva az agyunk, hogy terepen tájékozódjunk. Az alábbi képen az Alpok hegységet és egy kódrészletet láthatunk. (A hasonlóság nem a véletlen műve.)

alpok_kod
Továbbá a kódunkat ismerjük. Tudjuk, ha változtatni kell rajta, azt adott esetben a második legnagyobb hegy utáni kis dombon kell megtenni. Tudjuk, hogy egy adott hibajegy a harmadik nagy while ciklusunk magjának végén kerül kiírásra.
Kényelmesen átlátjuk, hiszen a kód fejlődése és írása közben memorizáljuk a kód szerkezetét is.

Mi a probléma mégis?

A probléma akkor kerül felszínre, amikor nem ismerjük a kód szerkezetét. Talán olyan régen írtuk, hogy már nem emlékszünk, vagy mások levetett hegyvonulatait örököltük meg.
Ilyenkor gyakran hosszas fejvakargatás után az egész függvényt nulláról újraírjuk. A problémák nagyon gyakran tehát nem is fejlesztési időben, hanem a karbantartás során jönnek elő.

A hosszú függvények kórtörténete

Minden függvény rövidnek indul. Általában az a mentalitás duzzasztja, hogy a fejlődése során mindig csak egy – két sort adunk hozzá, azért pedig “nem érdemes új függvényt írni”.
Jellemzően tehát megírjuk azt a pár sort, és ellátjuk egy megjegyzéssel.

Produktivitás problémája

Egy új csapattársnak igen nehéz egy hosszú függvényben tájékozódni. Ha még megjegyzések sincsenek, majdnem lehetetlen a küldetése. Mindenesetre sok időre van szüksége ahhoz, hogy produktívvá tudjon válni. Ellenben ha mindenhol jól elnevezett függvényekkel találkozik, az olyan, mintha jelzőtáblákkal találkozna út közben. Hamar rájön, hogy a kódnak melyik része érdekes a feladata kapcsán, és hamar produktívvá tud válni. Csapatmunkánál tehát sokszorosan felértékelődik a rövid függvények szabálya.

Gyakori ellenvetések

Elveszünk a sok apró függvényben.

Ha jó neveket választunk,inkább segítenek a tájékozódásban és nem gátolnak. Beszélni fogunk arról is, hogy a fájlok és osztályok méretét is szabályoznunk kell, ez is segíteni fog.

Illusztrálva a jelenséget, attól tartunk, hogy az első ábrán látható információ rengetegben találjuk magunkat. Egy hosszú függvényben azonban csak azt látjuk, hogy fentről indul és lefelé tart. Létezik azonban arany középút is.

A függvény hívások lassítják a programot.

Igen. A 70-es években ez a plusz idő mikroszekundumokban volt mérhető, manapság nanoszekundumos nagyságrendben van. Beágyazott rendszerek esetén ugyan lehet kritikus, de a legtöbb alkalmazás esetén nem helytálló ez az érv. Ráadásul a mai fordítók olyan jók, hogy lehet, hogy ki is optimalizálják a kódból a problémás részt.
A kódot tehát érdemes inkább olvashatóságra optimalizálni. (Beágyazott rendszereknél pedig a főciklusban lehet in-line-olni.)

Sok időbe telik és nehéz így kódolni

Egy függvény kiemelése kíván némi időt, de a mai fejlesztő eszközökben létezik a “metódus kiemelése (extract method)” refaktorálási lehetőség, így ez az időköltség is minimális. Hosszú távon pedig rengeteg időt spórolunk meg.

funcextract

Az angol “methodize” szó jelentése szó szerint rendszerezést jelent. Ha az Interneten rákeresel erre a szóra, ilyesmi képeket találsz. Pont ebbe a posztba illenek. Te melyikben éreznéd jobban magad?

bathroom-closet-before-after1

Egy hosszú függvényben osztály(ok) lapulhat(nak) meg.

hosszu_fuggvenyMi egy osztály nagyon leegyszerűsítve? Egy kupac függvény, melyek közös változókon keresztül kommunikálnak egymással. Másrészt egy hosszú függvény több funkcionális részből áll, melyeket a főbb indentálások jelölnek, és azért hosszú, mert ezek a funkcionális részek azonos adathalmazt kell manipuláljanak. Ezért tehát a legtöbb hosszú függvény egy, vagy több osztályra bontható.

Egy dolgot csináljon!

Fontos szabály, hogy egy függvény egy dolgot csináljon. Ha több funkcionális részre bontható, akkor több dolgot csinál. Ha több absztrakciós szinten dolgozik, szintén több dolgot csinál.

Honnan tudjuk biztosan hogy több dolgot csinál?

  • Ha új metódust lehet kiemelni, akkor definíció szerint több dolgot csinál.
  • Uncle Bob : “Ha kapcsos zárójelet kell használni a kódban, az már lehetőséget rejt új függvény kiemelésére.”

Mi a teendő?

Addig emeljünk ki új függvényeket, amíg csak értelmesen tudunk. (Extract till you drop!)

Lássunk már kódot!!!

A következő kód egy elképzelt képfeldolgozó funkcióval is rendelkező szoftver részletét mutatja be. Az init függvény célja, hogy példányosítson egy képfeldolgozó objektumot. Néha előfordulhat, hogy valami osztott erőforrás zárolása miatt nem sikerül létrehozni a példányt. Ezt az eshetőséget kezelni kell. Ezen kívül az képfeldolgozást több szálon szeretnénk futtatni, tehát több ilyen objektumot kell példányosítani. Tegyük fel, hogy a szálkészlet tervezési mintát alkalmazzuk. Ehhez készítünk feldolgozó (worker) osztályt.
A kód valós környezetből kerül ide, a problémát általánosítottam, hogy ne legyen felismerhető.

void ImageProcessingWorker::init()
{
    int try_count = 0;
    int max_try_count = 10;
    bool imageProcessingInited = false;
    while (!imageProcessingInited && (max_try_count > try_count) )
    {
        try
        {
            try_count++;
            ip = new ImageProcessor();
            int maxlen = 1024;
            char* errorstring = new char[maxlen];
            memset(errorstring, 0, maxlen);
            int errorcode = 0;
            ip_geterror(&errorcode, errorstring, maxlen-1);
            if ( !ip->IsValid() || (errorcode != ip::IP_NOERR))
            {
                cout << "ImageProcessor module creation FAILED "
                     << ", error code is: " <<  errorcode
                     << ", error string is: " << errorstring << endl;
                delete ip;
                ip = NULL;
            }
            else
            {
                imageProcessingInited = true;
            }
            delete[] errorstring;
            errorstring = NULL;
        }
        catch(IpException &e)
        {
            logIpError("init", e);
            imageProcessingInited = false;
        }
        if (!imageProcessingInited)
        {
            usleep(500000); //sleep 500ms
        }
    }
}

Ezzel a kóddal sok probléma van. Egyrészt ez a függvény nagyon hosszú. Másrészt több absztrakciós szinten dolgozik. Van itt minden; számlálástól kezdve hibakezelésen át az alacsony szintű (C-ből örökölt) memset-ig. Az ip_geterror metódus léte tervezési hibára utalhat, hiszen a használt API nem kényszeríti ki ennek a metódusnak a hívását. (Vajon mi történik, ha kétszer hívjuk egymás után???)
Ha vesszük a fáradtságot és végigböngésszük a kódot, a következőket deríthetjük ki.

  • A fő ciklus maximum 10 alkalommal fut le.
  • A célja, hogy sikeresen példányosítson egy FaceRecognizer objektumot.
  • Kezeli a hibákat.

Mennyivel könnyebb ezt a kódot megérteni:

void ImageProcessingWorker::init()
{
    for (int i = 0; i < 10; i++)
    {
        tryToCreateImageProcessor();
        if (imageProcessorCreationOk())
        {
            return;
        }
        imageProcessor.clear();
        usleep(500000);
    }
    throw TimeoutException("Failed to create image processor.");
}

Az eredeti kód kis munkával átalakítható erre. A teljes megoldást a poszt végén találjátok. Röviden összefoglalva a következő észrevételeket tehetjük:

  • A hibakód kezelése saját függvényt kapott
  • A nehézkesen értelmezhető while ciklusunk egy egyszerű for ciklusra cserélhető.
  • A try-catch blokk elegánsan leválasztható.
  • A kód egy kivétellel jelzi, ha tízszeri próbálkozásra sem sikerül az osztály példányosítása. Az eredeti változatban ez hiányzott, a program ilyenkor érvénytelen állapotba került.

A tisztesség kedvéért megjegyzem, hogy a refaktorált kód nem teljesen ekvivalens az eredetivel. Az egyik változtatás, hogy több helyen a Qt okos mutatóit használom. Ez a kódot átláthatóvá és biztonságossá teszi.

A kód fenti refaktorált verziója még közel sem az optimális megoldás, de már megmutatja a függvénykiemelésben rejlő lehetőségeket.

//
// ImageProcessingWorker.cpp
//

void ImageProcessingWorker::init()
{
    for (int i = 0; i < 10; i++)
    {
        tryToCreateImageProcessor();
        if (imageProcessorCreationOk())
        {
            return;
        }
        imageProcessor.clear();
        usleep(500000);
    }
    throw TimeoutException("Failed to create image processor.");
}

void ImageProcessingWorker::tryToCreateImageProcessor()
{
    try
    {
        imageProcessor = 
            QSharedPointer<ImageProcessor> (new ImageProcessor());
    }
    catch(IpException &e)
    {
        logIpException("init error", e);
    }
}
 
bool ImageProcessingWorker::hasIpCreateError()
{
    IpCreationResult result = IpCreationResult.getLatest();
    if(result.isError())
    {
        logIpCreationError(result);
    }
    return result.isError();
}

void ImageProcessingWorker::logIpCreationError(IpCreationResult result)
{
    cerr << "ImageProcessor module creation FAILED "
         << ", error code is: " <<  result.errorCode
         << ", error string is: " << result.errorMessage << endl;
}

bool ImageProcessingWorker::imageProcessorCreationOk()
{
    return (imageProcessor->IsValid() && !hasIpCreateError());
}

//
// IpCreationResult.hpp
//

class IpCreationResult
{
private:
    IpCreationResult(int _errorCode, std::string _errorMessage)
    {
        errorCode = _errorCode;
        errorMessage = _errorMessage;
    }

public:
    static IpCreationResult getLatest()
    {
        const int maxlen = 1024;
        QScopedArrayPointer<char> errorStr(new char[maxlen]);
        memset(errorStr.data(), 0, maxlen);
        int errorCode = 0;
        ip_geterror(&errorcode, errorStr.data(), maxlen-1);
        std::string errorMessage;
        errorMessage.assign(errorStr.data());
        return IpCreationResult(errorCode, errorMessage);
    }

    bool isError()
    {
        return errorCode != ip::ip_NOERR;
    }

    int errorCode;
    std::string errorMessage;
};

Most akkor tényleg 40 sorból csináltunk 80-at?

Felmerülhet a kérdés, hogy miért jó egy negyven soros kódot nyolcvan sorossá refaktorálni. A kérdésre két rövid válasz van.

  • A refaktorált kódot úgy is sokkal könnyebb megérteni, hogy hosszabb, hiszen nem egy rejtvény.
  • Ha minket nem a teljes működési logika érdekel fejlesztés közben, akkor nem is kell elolvasni mind a 80 sort. A rendszerezetlen kódrészletet, ahhoz, hogy tovább tudjuk fejleszteni el kell olvasni és meg is kell érteni. Igen, az egészet.

Források

  1. Clean Code: Functions
  2. Clean Code – A Handbook of Agile Software Craftsmanship — Martin, Robert C. Prentice Hall, 2009
  3. C++ Core Guidelines
Tiszta kód – 2. rész: függvények

Tiszta kód – 1. rész: nevek

Bevezető

A nevek mindenhol szerepelnek a kódunkban. Nevet adunk a változóknak, függvényeknek, osztályoknak, enumoknak, fájloknak, stb. Talán érdemes írni róla, hogy mitől lesznek a nevek jók. A következő pontokban ezt igyekszem összefoglalni. Ne feledjük: a neveket azért adjuk, hogy kommunikáljunk velük.

wtf-per-minute

A példákat a Tiszta kód sorozatból válogattam. A post elolvasása nem helyettesíti az élményt!

Érthető cél

Minden névnek az a célja, hogy kommunikálja írója szándékát.

int d; // elapsed time in days

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

A fenti példában látható d név nem túl informatív. Ha a változót sok helyen használjuk, előfordulhat, hogy a kontextusból bizonyos helyeken nem lehet érteni, hogy mit is takar az valójában. Fontos szabály, hogy egy név kommunikálja célját, és ne kelljen a kód más részeit átfésülni azért, hogy egy adott változó vagy név jelentését kiderítsük. Ha nem érthető azonnal egy név jelentése, akkor az a név rossz, jobbat kell választani. Az utána következők célravezetőbbek.

Vajon az alábbi kód milyen programban található?

list1 = []
for x in theList:
	if x[0] == 4:
		list1 += x;
return list1

Ha csak annyit változtatunk rajta, hogy értelmes neveket adunk fogalmainknak, már sokkal egyértelműbb a dolog.

flaggedCells = []
for cell in theBoard:
	if cell[STATUS_VALUE] == FLAGGED:
		flaggedCells += cell
return flaggedCells

Igen. Ez egy aknakereső kódjának részlete. De lehet még fokozni egy kicsit.

flaggedCells = []
for cell in theBoard:
	if cell.isFlagged():
		flaggedCells += cell
return flaggedCells

Ez már valóban jól olvasható. Magyar fordításban:

“Iteráljunk végig a tábla összes celláján. Ha egy cella “zászlós”, rakjuk be a tárolónkba, végül adjuk vissza a tárolót a hívónak.”

Kerüljük a félrevezetést

Ne vezesd félre a programozót!

Ha ezt a változót látod valahol egy kódban…

accountList;

akkor joggal várhatod el, hogy az tényleg egy lista legyen. Vagyis a következő függvényeknek az adott objektumra hívhatónak kell lenniük:

  • append()
  • erase()
  • insert()
  • C++ esetében például a sort() is

Így ha a fenti változó például csak egy egyszerű tömböt jelöl, a választott név nem jó. Jobb választás pl. az accounts név.

Hasonló változónevek

A szemünk hajlandó átverni, ha nagyon hasonló neveket használunk. Boldog hibakeresést!

XYZControllerForEfficientHandlingOfStrings
XYZControllerForEfficientStorageOfStrings

Radáusl léetizk egy iylen jleneésg is. Szvóal fnoots, hgoy ne hszalánj hsaolnó szvaakat.

1 szó/fogalom

Egy szót egy fogalommal társítsunk. Egy rossz példa az alábbi.

//1.
 void LoadSingleData();
 void FetchDataFiltered();
 void GetAllData();
//2.
 void SetDataToView();
 void SetObjectValue(int value);

Az első szakaszban a load, fetch, get szavak ugyanarra a fogalomra utalnak, míg a második szekcióban különböző fogalmak vannak azonos névvel jelölve. Ez így nem jó. Helyesen így festene:

//1.
 void GetSingleData();
 void GetDataFiltered();
 void GetAllData();
//2.
 void LoadDataToView();
 void SetObjectValue(int value);

Absztraktság szabálya

Az osztályok neve legyen annyira absztrakt, mint maga az osztály. Például a Protocol név absztrakt osztályt kell takarjon.

Átnevezés szabálya

Tegyük fel, hogy lelkiismeretes programozónk találó nevet adott egy függvénynek. A követelmények változása miatt a függvény törzse is változik, általában többet csinál mint azelőtt. Ilyenkor jó ha újraolvassuk a függvényt hívó kód részletét. Ha nem fedi az újdonsült igazságot, akkor át kell nevezni. Sok debug időt spórolhatunk meg, ha a nevek mindig aktualizálva vannak és az igazságot kommunikálják.

Kiejthető nevek

Vajon mit takarnak a következő nevek?

class DtaRcrd102 {
    private Date genymdhms;
    private Date modymdhms;
    private final String pszqint = "102";
};

Nem jobb így?

class Customer {
    private Date generationTimestamp;
    private Date modificationTimestamp;
    private final String recordId = "102";
};

Mellőzzük a magyar jelölést!

Ezt a jelölést annak idején ifjabbik Simonyi Károly (Charles Simonyi) fejlesztette ki a C nyelvhez. Akkoriban nehéz volt visszafejteni, hogy az egyes változóknak mi a típusa, ezért a változó nevébe kódolta.

Például a m_pszName egy struktúra saját változója volt (m_), ami egy nullvégződésű (z) sztringre (s) mutató pointer (p) volt és minden bizonnyal valamilyen névre mutatott.

Hogy miért is mellőzzük ezt a jelölést? Hát többek között ezért:

#include "sy.h"
extern int *rgwDic;
extern int bsyMac;
struct SY *PsySz(char sz[])
{
	char *pch;
	int cch;
	struct SY *psy, *PsyCreate();
	int *pbsy;
	int cwSz;
	unsigned wHash=0;
	pch=sz;
	while (*pch!=0)
		wHash=(wHash<>11+*pch++);
	cch=pch-sz;
	pbsy=&rgbsyHash[(wHash&077777)%cwHash];
	for (; *pbsy!=0; pbsy = &psy->bsyNext)
	{
		char *szSy;
		szSy= (psy=(struct SY*)&rgwDic[*pbsy])->sz;
		pch=sz;
		while (*pch==*szSy++)
		{
			if (*pch++==0)
				return (psy);
		}
	}
	cwSz=0;
	if (cch>=2)
		cwSz=(cch-2/sizeof(int)+1;
	*pbsy=(int *)(psy=PsyCreate(cwSY+cwSz))-rgwDic;
	Zero((int *)psy,cwSY);
	bltbyte(sz, psy->sz, cch+1);
	return(psy);
}

Ma már olyan fejlesztőeszközök vannak, hogy a változó típusát hamar megtudhatod, ha érdekel. Általában elég csak az egeret a változó fölé húzni és az IDE nagyon szívesen megmondja neked annak típusát.

Nevek és szófajok

Az alábbi táblázat szerint érdemes figyelni a nevek szófajára.

Nevek típusai szófaj példa
Változók, osztályok főnevek Vehicle, customer, employee
Metódusok igék bird.fly(), item.getPrice()
Bool változók, metódusok kifejezések list.isEmpty(),
buffer.hasTask(),
worker.isBusy()
Enum (Státuszt, vagy leírót tartalmaznak) gyakran melléknevek enum Color{Red, Green, Blue}
enum Status{Pending, Closed, Cancelled}

Ha jól figyelsz a szófajra, olvasmányos lesz a kódod.

if(employee.isLate()){
	employee.fire();
}

A rosszul választott szófaj bizonytalanságot szülhet.

if(set(username,"zsolt")){
	...
}

Vajon arra vagyunk kíváncsiak, hogy zsolt a username értéke, vagy beállítjuk zsolt-ra és azt vizsgáljuk, hogy sikeres volt-e a művelet? Ha az if-en belül például az “isSet” kifejezés lenne, fel sem merülne a kérdés.

Scope szabály

Változók

Kis scope-on használt változóknak rövid nevet érdemes adni, nagyobb scope-on használtaknak pedig bátran adhatunk hosszabb, kifejező nevet.

for (Cell cellToEvaluate: getLivingCells()) {
	int numberOfLivingNeighbors = countLivingNeighbors(cellToEvaluate);
	if (numberOfLivingNeighbors == 2 || numberOfLivingNeighbors == 3) {
		ng.add(cellToExamine);
	}
}

A fenti kódban zavaró, hogy nem tudjuk mit is takar az ng változó. Annak deklarációja sokkal feljebb található a kódban. Kevésbé zavaró, de szemet szúrhat, hogy a numberOfLivingNeighbours lokális változó összesen két sorban van használva. Ennek ellenére a neve nagyon hosszú, olvasása kissé kényelmetlen. Hasonló a helyzet a cellToEvaluate névvel is. A kód tisztább verziója így néz ki:

for (Cell cell: getLivingCells()) {
	int count = countLivingNeighbors(cell);
	if (count == 2 || count == 3) {
		nextGeneration.add(cell);
	}
}

A fenti kódrészletet alapján a témában jártas programozó felismerheti a Conway féle életjátéknak részletét.

Függvények, osztályok

Mivel a publikus függvényeket sok helyen használjuk, praktikusabb rövid, találó nevet adni nekik. A privát függvényeink kaphatnak hosszú nevet, ami jól leírja mit is csinál. Általában privát függvényeket amúgy sem hívunk túl sok helyről, nem lesz zavaró a hosszú név.

Az osztályok esetében egy leszármazott neve általában hosszabb elődjénél, hiszen működése sokkal konkrétabb. Ezért a neve is pontosításra szorul. Gyakran ősének valamilyen toldalékolt változata.

Források

  1. Clean Code: Names
  2. Clean Code – A Handbook of Agile Software Craftsmanship — Martin, Robert C. Prentice Hall, 2009
  3. Életjáték
Tiszta kód – 1. rész: nevek

Tiszta kód – Bevezető

Velem történt

coverAmikor friss diplomásként kiléptem a munkaerőpiacra, szerencsémre olyan céghez kerültem, amely nagy hangsúlyt helyezett a munkaerő képzésére. Többek között szinte kötelező jelleggel el kellett végezni a Clean Code kurzust. Ez egy videósorozat, melynek főszereplője, Uncle Bob lépésről lépésre mesélte el vicces, könnyed hangulatban, hogy mi is tesz egy programozót jó programozóvá.

Nagyrészt ezt az anyagot, illetve a filmet megelőző könyvet szeretném feldolgozni. Úgy érzem, nekem nagyon sokat adott, remélem mindenkinek hasznára fog válni.

Ajánlás

Neked ajánlom a sorozatot, ha:

  • nehezen tudod régi kódjaidat megfejteni
  • sok megjegyzésre van szükséged kódolás közben
  • programjaidhoz nagyon nehéz új funkciót illeszteni
  • új funkciók hozzáadása után programod más részén érdekes dolgok történnek
  • az öröklődést elsősorban a kódismétlés elkerülésére használtad

Tudtad?

  • A kód tisztasága fontosabb a céged számára, mint az, hogy helyesen működik.
  • Egy átlagos programozó a munkaidejének 90%-át kód olvasásával tölti.
  • Egy átlagos programozó sokkal több időt tölt hibakereséssel, mint fejlesztéssel.

Idézetek szabad fordításban

“Bármilyen bolond képes olyan kód írására, amit egy gép megért. A jó programozók kódját más emberek is megértik.” — Martin Fowler

“Szeretem, ha a kódom elegáns és hatékony. A kód logikája egyértelmű kell legyen, hogy nehezen rejtőzzenek hibák a sorok között. A függőségek legyenek minimálisak, mert ez megkönnyíti a karbantartást. A hibakezelés legyen teljes és a kód teljesítménye közel optimális, így nem kísérti az olvasóját további optimalizálásra, amellyel a tisztaságát veszélyezteti. A tiszta kód egy dolgot csinál, és azt jól.” — Bjarne Stroustrup

“A tiszta kód direkt és egyszerű. A tiszta kód olyan könnyen olvasható, mint egy próza.” — Grady Booch

Tiszta kód – Bevezető