SlideShare ist ein Scribd-Unternehmen logo
1 von 69
Downloaden Sie, um offline zu lesen
Clean Programming
Principi guida, tecniche e consigli per produrre codice facile da
scrivere, leggere e manutenere
25 January 2020
Davide Muzzarelli
Developer consultant
Premessa
2
In quale stanza si lavora meglio?
L'unica metrica per misurare la qualità del codice 3
Definizione di Clean Code
Clean code is code that is easy to understand and easy to change 4
Code sink
Code sink (1)
Pochi o nessun commento
Commenti ridondanti o completamente inutili
Pezzi di codice commentato in giro
Nessun test
Ok, qualche test, ma nesunno unit test
Funzioni con troppi metodi
Funzioni che modificano le variabili di input
Funzioni troppo lunghe
Troppi livelli d'innestamento
Due o più linguaggi nello stesso file
Righe troppo lunghe 6
Code sink (2)
One liners e codice complicato
Nomi troppo corti e acronimi
Nomi troppo astratti o non esplicativi
Inconsistenza nei nomi
Classi e funzioni che fanno troppe cose
Sovra-ingegnerizzazione
Variabili globali
Valori hard coded
Espressioni in forma negativa
Abuso di mixin ed ereditarietà
Notazione ungherese 7
Il codice che ci piace
Leggibile
Manutenibile
Riusabile
Testabile 8
La più importante è la leggibilità
"Any fool can write code that a computer can understand. Good programmers write
code that humans can understand." - Martin Fowler
"If you want your code to be easy to write, make it easy to read" - Robert C. Martin 9
Esperienza personale
Risultati ottenuti su piccoli team (fino a 15 persone l'uno):
Codice più efficiente
Test notevolmente più veloci
Riduzione dei bug
Meno richieste di riscrivere codice
Riduzione dei tempi di sviluppo
Stesso numero di righe
Meno WTF che volavano
Più voglia di lavorare 10
L'importanza dei nomi
L'importanza dei nomi
12
L'importanza dei nomi
13
L'importanza dei nomi - Lunghezza (1)
Nomi sintetici, ma abbastanza lunghi per essere esaustivi.
Evitare gli acronimi. Eccezioni che confermano la regola: acronimi universali.
Bad:
inv = ... // Invoice o Inventory?
pnt = ... // Point o Pointer?
cnt = ... // Count o Counter?
srv = ... // Server o Service?
parser = ... // Di cosa?
bid = ... // Company ID o Customer ID oppure un'offerta?
tmp = ... // Senza significato
Good:
i18n = ... // Internazionalization
cmd = ... // Command
server = ...
sql_parser = ...
customer_id = ...
14
L'importanza dei nomi - Lunghezza (2)
Quando il linguaggio è tipato, e la funzione fa solo una cosa, si possono usare variabili di
una sola lettera.
func main() {
r := strings.NewReader("Clear is better than clever")
p := make([]byte, 4)
for {
n, err := r.Read(p)
if err == io.EOF {
break
}
fmt.Println(string(p[:n]))
}
}
15
L'importanza dei nomi - Notazione ungara
Evitare la notazione ungara (prefisso che indica il tipo di variabile).
Bad:
// Pointer (p) to a zero-terminated (z) string (s) buffer
var pszBuffer *Buffer := &new(Buffer())
Good:
var buffer *Buffer := &strings.Buffer()
Con tutto il rispetto per Charles Simonyi 16
L'importanza dei nomi - Funzioni (1)
Le funzioni esprimono azioni.
Usare i verbi dove possibile.
count_users()
enable_feature()
activate()
execute()
17
L'importanza dei nomi - Funzioni (2)
Non abusare dei get.
Con get ci si aspetta di ottenere una variabile già a disposizione.
Alternative che rivelano cosa fanno:
fetch()
download()
find()
compute()
search()
18
L'importanza dei nomi - Booleani
Usare affermazioni o stati.
Usare la forma positiva ove possibile.
Ecco alcune idee:
is_active = True
available = False
can_skip = True
use_ssl = False
keep_logs = True
19
L'importanza dei nomi - Interfacce
Preferire il suffisso -er.
Interfacce:
Writer
Reader
Serializer
Speaker
20
L'importanza dei nomi - Librerie
Evitare nomi che potrebbero voler usare gli utilizzatori della libreria.
Bad:
buffer
buffers
cache
datetime
Good:
buffering
iobuf
caching
clock
21
L'importanza dei nomi - Ultimi consigli
Evitare battute e nomi ridicoli
Usare delle metafore condivise col team
Quando una funzione fa troppe cose è difficile dargli un nome, conviene dividerla 22
Estetica
Estetica - Consigli
Usare lo stile scelto in maniera consistente in tutto il team.
Gli occhi leggono più velocemente quando la formattazione è uniforme e verticale.
Scegliere una guida di riferimento come quelle di Google
Impostare un code formatter ad ogni salvataggio
Sfruttare lo spazio in verticale, non in orizzontale
Rimanere entro le 80 colonne 24
Estetica - Codice compatto
Il codice compatto consuma energie mentali.
Bad:
fibonacci = lambda n: (n in (0,1) and [n] or [fib(n-1) + fib(n-2)])[0]
Good:
def fibonacci(n):
if n in (0,1):
return n
return fib(n-1) + fib(n-2)
25
Singolo scopo
Singolo scopo
27
Singolo scopo
Ogni elemento del programma deve fare una e una cosa sola.
Il codice risulta più breve
Si riesce a riusare un sacco di codice
I test diventano facili da scrivere e veloci da eseguire
E' più facile nominare le cose
Il refactoring diventa facilissimo 28
Singolo scopo - Astrazione
Immagina una view che fa troppe cose:
1. Prende i dati dal form
2. Valida i dati
3. Esegue delle operazioni
4. Salva i dati su database
5. Renderizza l'HTML finale
Ogni passaggio è un buon candidato per diventare una funzione separata.
Mantenersi sullo stesso livello di astrazione delegando i dettagli. 29
Singolo scopo - Esempio
Bad:
def users_view(request):
if request.method == 'GET':
users = User.objects.get_all()
...
return Template.render('users.html', context)
elif request.method == 'POST':
for user_data in request.POST[users]:
user = User(name=user_data.name, email=user_data.email)
user.save()
...
return Template.render('users_uploaded.html', context)
Good:
def users_view(request):
if request.method == 'GET':
return list_users()
elif request.method = 'POST':
return add_new_users(request)
30
Variabili
Variabili - Uso breve
Usare le variabili per il più breve tempo possibile:
Liberano la memoria più velocemente
Alta probabilità di sfruttare lo stack
Si riduce il rischio di memory leak
A volte i compilatori e gli interpreti sono in grado di ottimizzarle ulteriormente
NB: Spesso copiare una variabile è più efficiente che usare un puntatore. 32
Variabili - Loop
Non assegnare il valore di una variabile fintanto che non la si usa.
Creare le variabili di stato immediatamente prima del loop.
Bad:
counter = 1
... // Some code
for user in users:
...
counter += 1
Good:
33
... // Some code
counter = 1
for user in users:
...
counter += 1
Variabili - Globali
Evitarle.
Riducono la leggibilità del codice perché lontane da dove vengono usate
L'accesso è più lento per via dello scoping
Spesso non sono utilizzabili in ambiente multi-thread o multi-process
Possono richiedere un meccanismo di sincronizzazione rallentando così
l'applicazione
Rendono il debugging più difficile perché è più complicato trovare cosa ha
modificato il valore
Costringono all'uso di integration test invece dei più semplici unit test
Rendono i refactoring molto difficili
Sono rari i casi in cui sono necessari; logging e metering sono due di questi. 34
Variabili - Costanti
Evitare i valori hard coded. Usare sempre le costanti.
Bad:
worker.pay_per_hour * 8 * 5
Good:
HOURS_PER_DAY = 8
WORKING_DAYS_PER_WEEK = 5
worker.pay_per_hour * HOURS_PER_DAY * WORKING_DAYS_PER_WEEK
35
Funzioni
Funzioni - Parametri (1)
Quando i parametri sono tanti i test si complicano e il codice non si riesce più a riusare.
Le migliori funzioni hanno zero parametri.
Tre parametri cominciano ad essere troppi.
Dividere la funzione in più funzioni specializzate.
Unire i parametri in oggetti. 37
Funzioni - Parametri (2)
Quando i parametri sono troppi:
Forse si vogliono fare troppe cose, dividere il codice in più funzioni
Raccogliere i parametri omogenei in un oggetto:
make_circle(x, y, radius) # Bad
make_circle(Point, radius)) # Good
La funzione potrebbe avere più senso dentro ad un piccolo oggetto
In caso di flag, dividere la funzione in due diverse:
# Bad
render(json=False)
# Good
render_html()
render_json()
38
Funzioni - Effetti collaterali
Evitare gli effetti collaterali.
Bad:
def check_password(user, password):
encrypted = crypt.crypt(password, PREFIX + SALT)
if encrypted != self.password:
return False
user.is_logged = True # <-- Bad bad bad!!
return True
Se una funzione deve cambiare lo stato di un oggetto è il caso di implementare un
metodo. 39
Classi
Classi
Una classe è definita per il suo comportamento, non per i suoi valori.
E' perfettamente lecito avere classi anche con un solo metodo.
Le classi senza metodi si chiamano DTO (Data Transfer Object).
Evitare i fat objects (errore comune usando gli ORM).
E' più facile testare e riutilizzare il codice quando è diviso in tante piccole classi. 41
Classi - Gerarchia
La gerarchia migliore è nessuna gerarchia.
Evitare di ereditare due o più classi contemporaneamente.
Evitare le classi mixin: complicano il codice e lo rendono estremamente rigido.
Soluzione: comporre è meglio che ereditare. 42
File sorgenti
Evitare di usare più linguaggi nello stesso file.
Il codice si complica
Spesso si finisce per identare troppo
Leggere è più faticoso
L'evidenziazione della sintassi può non essere disponibile
Modifiche e rifattorizzazioni richiedono più tempo
Non si può lavorare in due sullo stesso codice 43
Controllo del flusso
Controllo del flusso - Yoda
Evitare la Yoda notation.
Bad:
if (NULL == user)
Good:
if (user == NULL)
45
Controllo del flusso - Precedenza (1)
Negli if-else preferire i casi positivi all'inizio.
Bad:
if (a != b):
# Not equal
else:
# Equal
if (!user.is_admin()):
# Not admin
Good:
if (a == b):
# Equal
else:
# Not equal
if (user.is_admin()):
# Admin
46
Controllo di flusso - Nesting (1)
Come risolviamo obobri del genere?
47
Controllo del flusso - Nesting (2)
Francesco Cirillo - Tecnica del pomodoro e Anti-IF® Design Course 48
Controllo del flusso - Nesting (3)
Pratichiamo il fast exit.
Bad:
if http_port_is_open:
if ssl_key is Null:
server.listen(80)
else:
server.listen(80, ssl=True)
else:
raise Error('Port already used')
Good:
if !http_port_is_open:
raise Error('Port already used')
use_ssl = ssl_key is not Null
server.listen(80, ssl=use_ssl)
49
Controllo di flusso - Nesting (4)
Separiamo questo garbuglio:
def collect_emails(users):
emails = set()
for user in users:
for friend in user.friends:
if friend.email is None:
continue
else:
emails.add(friend.email)
return emails
in due funzioni:
def collect_emails(users):
emails = []
for user in users:
emails += collect_friends_emails(user.friends)
return set(emails)
def collect_friends_emails(friends):
return [for x in friends if x is not None]
50
Controllo di flusso - Legge di Demetrio
Un oggetto non dovrebbe esporre la sua struttura interna.
Ovvero: parla con gli amici, non con gli sconosciuti.
Bad:
return Template.load('templates/home.html').compile().render(context)
Good:
engine = TemplateEngine('templates/')
html = engine.render('home.html', context)
return html
Vantaggio collaterale: sembra che nel secondo caso gli IDE performino meglio. 51
Interfacce
Interfacce
Definire il numero minimo di metodi.
Non definire le proprietà, piuttosto incapsularle in metodi.
Le migliori interfacce hanno un solo metodo.
Se il linguaggio li mette a disposizione, usare i protocolli.
Come si chiamano i protocolli nei vari linguaggi:
Python: protocol
JavaScript: protocol
Go: interface
Java: trait (devono essere dichiarati)
Swift: protocol (devono essere dichiarati)
Rust: trait (devono essere dichiarati) 53
Test
Test
Quando il codice è breve:
Ci sono meno combinazioni da provare
I test diventano più piccoli
Capita raramente di dover modificare un test
E' molto più difficile rompere i test durante i refectoring 55
Test - Esempio
Quanto è testabile questa funzione?
def please_test_me(user, check_password, post):
message = ''
if check_password:
password = post.get('password')
if password is None:
raise Error('No password')
else:
if user.password_is_valid(password):
message = 'allowed'
else:
message = 'not_allowed'
else:
if user.is_admin():
message = 'allowed'
elif user.is_staff():
message = 'can_view'
else
message = 'not_allowed'
return render('response.html', user, message)
56
Test - Soluzione
Divisa così è più testabile.
def extract_password(post):
password = post.get('password')
if password is None:
raise Error('No password')
return password
def message_by_status(user):
if user.is_admin():
return 'allowed'
elif user.is_staff():
return 'can_view'
else
return 'not_allowed'
57
Commenti
Commenti
59
Commenti - Come e quando
Precisi e compatti
Spiegare il codice complicato
Motivare dettagli non intutivi
Commentare solo quando serve
I commenti sono inutili se:
Il codice è già tipizzato
Classi e funzioni sono brevi e fanno una sola cosa
Sono usati nomi chiari ed esplicativi 60
Commenti - Codice
Quando il codice si commenta da solo:
Bad:
...
# Check if client is billable
if user.billable_hours > 20 or (today - last_invoice_date) > 30:
...
Good:
if user.isBillable():
...
61
Commenti - Superflui
Quando i commenti sono superflui:
class User:
"""A user."""
_is_active # The user is active
def is_admin(self) -> bool:
"""Return True if User has admin privileges. If not return False."""
...
62
Strumenti
Strumenti
Guide di stile http://google.github.io/styleguide/
Linter: individuano potenziali bug, errori di stile, pattern sospetti
Code formatter: formattano il codice seguendo uno standard uguale per tutti
Race detector: segnalano le race condition durante l'esecuzione di un programma
Code review: obbligano la revisione del codice da parte di altri sviluppatori 64
Bibliografia
65
Thank you
Davide Muzzarelli
Developer consultant
davide@webforce.it(mailto:davide@webforce.it)
https://it.linkedin.com/in/davidemuzzarelli(https://it.linkedin.com/in/davidemuzzarelli)

Weitere ähnliche Inhalte

Was ist angesagt?

Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Università degli Studi di Trieste
 
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Università degli Studi di Trieste
 
Lezione 4 (7 marzo 2012)
Lezione 4 (7 marzo 2012)Lezione 4 (7 marzo 2012)
Lezione 4 (7 marzo 2012)STELITANO
 
Design Pattern Comportamentali
Design Pattern ComportamentaliDesign Pattern Comportamentali
Design Pattern ComportamentaliRiccardo Cardin
 
Dai delegati a LINQ con C#
Dai delegati a LINQ con C#Dai delegati a LINQ con C#
Dai delegati a LINQ con C#Manuel Scapolan
 
Corso Programmazione Java Base
Corso Programmazione Java BaseCorso Programmazione Java Base
Corso Programmazione Java BaseK-Tech Formazione
 
C# e la Framework Class Library
C# e la Framework Class LibraryC# e la Framework Class Library
C# e la Framework Class LibraryManuel Scapolan
 
2011.02.19 Introducing F#
2011.02.19 Introducing F#2011.02.19 Introducing F#
2011.02.19 Introducing F#Marco Parenzan
 
Corso pratico di C# - 2013
Corso pratico di C# - 2013Corso pratico di C# - 2013
Corso pratico di C# - 2013Matteo Valoriani
 

Was ist angesagt? (12)

OOP with C#
OOP with C#OOP with C#
OOP with C#
 
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
 
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
Progetto e realizzazione di uno strumento per la modifica sistematica di codi...
 
Lezione 4 (7 marzo 2012)
Lezione 4 (7 marzo 2012)Lezione 4 (7 marzo 2012)
Lezione 4 (7 marzo 2012)
 
Design Pattern Comportamentali
Design Pattern ComportamentaliDesign Pattern Comportamentali
Design Pattern Comportamentali
 
Dai delegati a LINQ con C#
Dai delegati a LINQ con C#Dai delegati a LINQ con C#
Dai delegati a LINQ con C#
 
Corso Programmazione Java Base
Corso Programmazione Java BaseCorso Programmazione Java Base
Corso Programmazione Java Base
 
C# e la Framework Class Library
C# e la Framework Class LibraryC# e la Framework Class Library
C# e la Framework Class Library
 
2011.02.19 Introducing F#
2011.02.19 Introducing F#2011.02.19 Introducing F#
2011.02.19 Introducing F#
 
C# Language Evolution
C# Language EvolutionC# Language Evolution
C# Language Evolution
 
Corso pratico di C# - 2013
Corso pratico di C# - 2013Corso pratico di C# - 2013
Corso pratico di C# - 2013
 
T7 librerie
T7 librerieT7 librerie
T7 librerie
 

Ähnlich wie Clean programming 2020-01-25 @ Modena Tech Summit

Una fugace occhiata al Test Driven Development (2006)
Una fugace occhiata al Test Driven Development  (2006)Una fugace occhiata al Test Driven Development  (2006)
Una fugace occhiata al Test Driven Development (2006)Roberto Bettazzoni
 
Detailed Model Capture
Detailed Model CaptureDetailed Model Capture
Detailed Model Capturefcospito
 
Detailed Model Capture
Detailed Model CaptureDetailed Model Capture
Detailed Model Capturefcospito
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptLuca Pagliaro
 
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in DelphiMarco Breveglieri
 
Continous Delivery & HQ Code
Continous Delivery & HQ CodeContinous Delivery & HQ Code
Continous Delivery & HQ CodeDaniele Mondello
 
Introduzione al Test Driven Development
Introduzione al Test Driven DevelopmentIntroduzione al Test Driven Development
Introduzione al Test Driven DevelopmentEnnio Masi
 
Introduzione a TypeScript
Introduzione a TypeScriptIntroduzione a TypeScript
Introduzione a TypeScriptSinergia Totale
 
Deep diving C# 4 (Raffaele Rialdi)
Deep diving C# 4 (Raffaele Rialdi)Deep diving C# 4 (Raffaele Rialdi)
Deep diving C# 4 (Raffaele Rialdi)DotNetMarche
 
Delphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingDelphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingMarco Breveglieri
 
PowerMock TDD User Group Milano
PowerMock TDD User Group MilanoPowerMock TDD User Group Milano
PowerMock TDD User Group MilanoMassimo Groppelli
 
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...LeonardoIurada
 

Ähnlich wie Clean programming 2020-01-25 @ Modena Tech Summit (20)

Una fugace occhiata al Test Driven Development (2006)
Una fugace occhiata al Test Driven Development  (2006)Una fugace occhiata al Test Driven Development  (2006)
Una fugace occhiata al Test Driven Development (2006)
 
Detailed Model Capture
Detailed Model CaptureDetailed Model Capture
Detailed Model Capture
 
Detailed Model Capture
Detailed Model CaptureDetailed Model Capture
Detailed Model Capture
 
Slide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScriptSlide evento Code Refactoring JavaScript
Slide evento Code Refactoring JavaScript
 
Testing
TestingTesting
Testing
 
Unit Testing
Unit TestingUnit Testing
Unit Testing
 
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi
"Non Aprite Quella Unit!" ovvero "Scrivere Clean Code in Delphi
 
Continous Delivery & HQ Code
Continous Delivery & HQ CodeContinous Delivery & HQ Code
Continous Delivery & HQ Code
 
Software Testing e TDD
Software Testing e TDDSoftware Testing e TDD
Software Testing e TDD
 
Ruby in 25 minuti
Ruby in 25 minutiRuby in 25 minuti
Ruby in 25 minuti
 
Introduzione al Test Driven Development
Introduzione al Test Driven DevelopmentIntroduzione al Test Driven Development
Introduzione al Test Driven Development
 
Introduzione a TypeScript
Introduzione a TypeScriptIntroduzione a TypeScript
Introduzione a TypeScript
 
Deep diving C# 4 (Raffaele Rialdi)
Deep diving C# 4 (Raffaele Rialdi)Deep diving C# 4 (Raffaele Rialdi)
Deep diving C# 4 (Raffaele Rialdi)
 
Low Level Software Security
Low Level Software SecurityLow Level Software Security
Low Level Software Security
 
Kotlin hexagonal-architecture
Kotlin hexagonal-architectureKotlin hexagonal-architecture
Kotlin hexagonal-architecture
 
Delphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del TestingDelphi & Dintorni Webinar - Diventa un mago del Testing
Delphi & Dintorni Webinar - Diventa un mago del Testing
 
PowerMock TDD User Group Milano
PowerMock TDD User Group MilanoPowerMock TDD User Group Milano
PowerMock TDD User Group Milano
 
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...Summary of “The Case for Writing Network Drivers in High-Level Programming La...
Summary of “The Case for Writing Network Drivers in High-Level Programming La...
 
Diagrammi di Sequenza
Diagrammi di SequenzaDiagrammi di Sequenza
Diagrammi di Sequenza
 
Repository pattern
Repository patternRepository pattern
Repository pattern
 

Kürzlich hochgeladen

Presentzione Matematica similitudini circonferenze e omotetie.pptx
Presentzione  Matematica similitudini circonferenze e omotetie.pptxPresentzione  Matematica similitudini circonferenze e omotetie.pptx
Presentzione Matematica similitudini circonferenze e omotetie.pptxfilippoluciani9
 
GIORNATA TECNICA DA AQP 18/04 | ZONNO Serena
GIORNATA TECNICA DA AQP 18/04 | ZONNO SerenaGIORNATA TECNICA DA AQP 18/04 | ZONNO Serena
GIORNATA TECNICA DA AQP 18/04 | ZONNO SerenaServizi a rete
 
GIORNATA TECNICA 18/04 | BENANTI Alessandro
GIORNATA TECNICA 18/04 | BENANTI AlessandroGIORNATA TECNICA 18/04 | BENANTI Alessandro
GIORNATA TECNICA 18/04 | BENANTI AlessandroServizi a rete
 
GIORNATA TECNICA 18/04 | SPIZZIRRI Massimo
GIORNATA TECNICA 18/04 | SPIZZIRRI MassimoGIORNATA TECNICA 18/04 | SPIZZIRRI Massimo
GIORNATA TECNICA 18/04 | SPIZZIRRI MassimoServizi a rete
 
GIORNATA TECNICA DA AQP 18/04 | MOTTA Simone
GIORNATA TECNICA DA AQP 18/04 | MOTTA SimoneGIORNATA TECNICA DA AQP 18/04 | MOTTA Simone
GIORNATA TECNICA DA AQP 18/04 | MOTTA SimoneServizi a rete
 
Descrizione della struttura architettonica Eretteo.pptx
Descrizione della struttura architettonica Eretteo.pptxDescrizione della struttura architettonica Eretteo.pptx
Descrizione della struttura architettonica Eretteo.pptxtecongo2007
 
GIORNATA TECNICA 18/04 | LITTERIO Raffaele
GIORNATA TECNICA 18/04 | LITTERIO RaffaeleGIORNATA TECNICA 18/04 | LITTERIO Raffaele
GIORNATA TECNICA 18/04 | LITTERIO RaffaeleServizi a rete
 
GIORNATA TECNICA 18/04 | DE ROSA Roberto
GIORNATA TECNICA 18/04 | DE ROSA RobertoGIORNATA TECNICA 18/04 | DE ROSA Roberto
GIORNATA TECNICA 18/04 | DE ROSA RobertoServizi a rete
 
GIORNATA TECNICA 18/04 | DE LEO Antonio
GIORNATA TECNICA 18/04  | DE LEO AntonioGIORNATA TECNICA 18/04  | DE LEO Antonio
GIORNATA TECNICA 18/04 | DE LEO AntonioServizi a rete
 

Kürzlich hochgeladen (9)

Presentzione Matematica similitudini circonferenze e omotetie.pptx
Presentzione  Matematica similitudini circonferenze e omotetie.pptxPresentzione  Matematica similitudini circonferenze e omotetie.pptx
Presentzione Matematica similitudini circonferenze e omotetie.pptx
 
GIORNATA TECNICA DA AQP 18/04 | ZONNO Serena
GIORNATA TECNICA DA AQP 18/04 | ZONNO SerenaGIORNATA TECNICA DA AQP 18/04 | ZONNO Serena
GIORNATA TECNICA DA AQP 18/04 | ZONNO Serena
 
GIORNATA TECNICA 18/04 | BENANTI Alessandro
GIORNATA TECNICA 18/04 | BENANTI AlessandroGIORNATA TECNICA 18/04 | BENANTI Alessandro
GIORNATA TECNICA 18/04 | BENANTI Alessandro
 
GIORNATA TECNICA 18/04 | SPIZZIRRI Massimo
GIORNATA TECNICA 18/04 | SPIZZIRRI MassimoGIORNATA TECNICA 18/04 | SPIZZIRRI Massimo
GIORNATA TECNICA 18/04 | SPIZZIRRI Massimo
 
GIORNATA TECNICA DA AQP 18/04 | MOTTA Simone
GIORNATA TECNICA DA AQP 18/04 | MOTTA SimoneGIORNATA TECNICA DA AQP 18/04 | MOTTA Simone
GIORNATA TECNICA DA AQP 18/04 | MOTTA Simone
 
Descrizione della struttura architettonica Eretteo.pptx
Descrizione della struttura architettonica Eretteo.pptxDescrizione della struttura architettonica Eretteo.pptx
Descrizione della struttura architettonica Eretteo.pptx
 
GIORNATA TECNICA 18/04 | LITTERIO Raffaele
GIORNATA TECNICA 18/04 | LITTERIO RaffaeleGIORNATA TECNICA 18/04 | LITTERIO Raffaele
GIORNATA TECNICA 18/04 | LITTERIO Raffaele
 
GIORNATA TECNICA 18/04 | DE ROSA Roberto
GIORNATA TECNICA 18/04 | DE ROSA RobertoGIORNATA TECNICA 18/04 | DE ROSA Roberto
GIORNATA TECNICA 18/04 | DE ROSA Roberto
 
GIORNATA TECNICA 18/04 | DE LEO Antonio
GIORNATA TECNICA 18/04  | DE LEO AntonioGIORNATA TECNICA 18/04  | DE LEO Antonio
GIORNATA TECNICA 18/04 | DE LEO Antonio
 

Clean programming 2020-01-25 @ Modena Tech Summit

  • 1. Clean Programming Principi guida, tecniche e consigli per produrre codice facile da scrivere, leggere e manutenere 25 January 2020 Davide Muzzarelli Developer consultant
  • 3. In quale stanza si lavora meglio? L'unica metrica per misurare la qualità del codice 3
  • 4. Definizione di Clean Code Clean code is code that is easy to understand and easy to change 4
  • 6. Code sink (1) Pochi o nessun commento Commenti ridondanti o completamente inutili Pezzi di codice commentato in giro Nessun test Ok, qualche test, ma nesunno unit test Funzioni con troppi metodi Funzioni che modificano le variabili di input Funzioni troppo lunghe Troppi livelli d'innestamento Due o più linguaggi nello stesso file Righe troppo lunghe 6
  • 7. Code sink (2) One liners e codice complicato Nomi troppo corti e acronimi Nomi troppo astratti o non esplicativi Inconsistenza nei nomi Classi e funzioni che fanno troppe cose Sovra-ingegnerizzazione Variabili globali Valori hard coded Espressioni in forma negativa Abuso di mixin ed ereditarietà Notazione ungherese 7
  • 8. Il codice che ci piace Leggibile Manutenibile Riusabile Testabile 8
  • 9. La più importante è la leggibilità "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." - Martin Fowler "If you want your code to be easy to write, make it easy to read" - Robert C. Martin 9
  • 10. Esperienza personale Risultati ottenuti su piccoli team (fino a 15 persone l'uno): Codice più efficiente Test notevolmente più veloci Riduzione dei bug Meno richieste di riscrivere codice Riduzione dei tempi di sviluppo Stesso numero di righe Meno WTF che volavano Più voglia di lavorare 10
  • 13.
  • 15. L'importanza dei nomi - Lunghezza (1) Nomi sintetici, ma abbastanza lunghi per essere esaustivi. Evitare gli acronimi. Eccezioni che confermano la regola: acronimi universali. Bad: inv = ... // Invoice o Inventory? pnt = ... // Point o Pointer? cnt = ... // Count o Counter? srv = ... // Server o Service? parser = ... // Di cosa? bid = ... // Company ID o Customer ID oppure un'offerta? tmp = ... // Senza significato Good: i18n = ... // Internazionalization cmd = ... // Command server = ... sql_parser = ... customer_id = ... 14
  • 16. L'importanza dei nomi - Lunghezza (2) Quando il linguaggio è tipato, e la funzione fa solo una cosa, si possono usare variabili di una sola lettera. func main() { r := strings.NewReader("Clear is better than clever") p := make([]byte, 4) for { n, err := r.Read(p) if err == io.EOF { break } fmt.Println(string(p[:n])) } } 15
  • 17. L'importanza dei nomi - Notazione ungara Evitare la notazione ungara (prefisso che indica il tipo di variabile). Bad: // Pointer (p) to a zero-terminated (z) string (s) buffer var pszBuffer *Buffer := &new(Buffer()) Good: var buffer *Buffer := &strings.Buffer() Con tutto il rispetto per Charles Simonyi 16
  • 18. L'importanza dei nomi - Funzioni (1) Le funzioni esprimono azioni. Usare i verbi dove possibile. count_users() enable_feature() activate() execute() 17
  • 19. L'importanza dei nomi - Funzioni (2) Non abusare dei get. Con get ci si aspetta di ottenere una variabile già a disposizione. Alternative che rivelano cosa fanno: fetch() download() find() compute() search() 18
  • 20. L'importanza dei nomi - Booleani Usare affermazioni o stati. Usare la forma positiva ove possibile. Ecco alcune idee: is_active = True available = False can_skip = True use_ssl = False keep_logs = True 19
  • 21. L'importanza dei nomi - Interfacce Preferire il suffisso -er. Interfacce: Writer Reader Serializer Speaker 20
  • 22. L'importanza dei nomi - Librerie Evitare nomi che potrebbero voler usare gli utilizzatori della libreria. Bad: buffer buffers cache datetime Good: buffering iobuf caching clock 21
  • 23. L'importanza dei nomi - Ultimi consigli Evitare battute e nomi ridicoli Usare delle metafore condivise col team Quando una funzione fa troppe cose è difficile dargli un nome, conviene dividerla 22
  • 25. Estetica - Consigli Usare lo stile scelto in maniera consistente in tutto il team. Gli occhi leggono più velocemente quando la formattazione è uniforme e verticale. Scegliere una guida di riferimento come quelle di Google Impostare un code formatter ad ogni salvataggio Sfruttare lo spazio in verticale, non in orizzontale Rimanere entro le 80 colonne 24
  • 26. Estetica - Codice compatto Il codice compatto consuma energie mentali. Bad: fibonacci = lambda n: (n in (0,1) and [n] or [fib(n-1) + fib(n-2)])[0] Good: def fibonacci(n): if n in (0,1): return n return fib(n-1) + fib(n-2) 25
  • 29. Singolo scopo Ogni elemento del programma deve fare una e una cosa sola. Il codice risulta più breve Si riesce a riusare un sacco di codice I test diventano facili da scrivere e veloci da eseguire E' più facile nominare le cose Il refactoring diventa facilissimo 28
  • 30. Singolo scopo - Astrazione Immagina una view che fa troppe cose: 1. Prende i dati dal form 2. Valida i dati 3. Esegue delle operazioni 4. Salva i dati su database 5. Renderizza l'HTML finale Ogni passaggio è un buon candidato per diventare una funzione separata. Mantenersi sullo stesso livello di astrazione delegando i dettagli. 29
  • 31. Singolo scopo - Esempio Bad: def users_view(request): if request.method == 'GET': users = User.objects.get_all() ... return Template.render('users.html', context) elif request.method == 'POST': for user_data in request.POST[users]: user = User(name=user_data.name, email=user_data.email) user.save() ... return Template.render('users_uploaded.html', context) Good: def users_view(request): if request.method == 'GET': return list_users() elif request.method = 'POST': return add_new_users(request) 30
  • 33. Variabili - Uso breve Usare le variabili per il più breve tempo possibile: Liberano la memoria più velocemente Alta probabilità di sfruttare lo stack Si riduce il rischio di memory leak A volte i compilatori e gli interpreti sono in grado di ottimizzarle ulteriormente NB: Spesso copiare una variabile è più efficiente che usare un puntatore. 32
  • 34. Variabili - Loop Non assegnare il valore di una variabile fintanto che non la si usa. Creare le variabili di stato immediatamente prima del loop. Bad: counter = 1 ... // Some code for user in users: ... counter += 1 Good: 33
  • 35. ... // Some code counter = 1 for user in users: ... counter += 1
  • 36. Variabili - Globali Evitarle. Riducono la leggibilità del codice perché lontane da dove vengono usate L'accesso è più lento per via dello scoping Spesso non sono utilizzabili in ambiente multi-thread o multi-process Possono richiedere un meccanismo di sincronizzazione rallentando così l'applicazione Rendono il debugging più difficile perché è più complicato trovare cosa ha modificato il valore Costringono all'uso di integration test invece dei più semplici unit test Rendono i refactoring molto difficili Sono rari i casi in cui sono necessari; logging e metering sono due di questi. 34
  • 37. Variabili - Costanti Evitare i valori hard coded. Usare sempre le costanti. Bad: worker.pay_per_hour * 8 * 5 Good: HOURS_PER_DAY = 8 WORKING_DAYS_PER_WEEK = 5 worker.pay_per_hour * HOURS_PER_DAY * WORKING_DAYS_PER_WEEK 35
  • 39. Funzioni - Parametri (1) Quando i parametri sono tanti i test si complicano e il codice non si riesce più a riusare. Le migliori funzioni hanno zero parametri. Tre parametri cominciano ad essere troppi. Dividere la funzione in più funzioni specializzate. Unire i parametri in oggetti. 37
  • 40. Funzioni - Parametri (2) Quando i parametri sono troppi: Forse si vogliono fare troppe cose, dividere il codice in più funzioni Raccogliere i parametri omogenei in un oggetto: make_circle(x, y, radius) # Bad make_circle(Point, radius)) # Good La funzione potrebbe avere più senso dentro ad un piccolo oggetto In caso di flag, dividere la funzione in due diverse: # Bad render(json=False) # Good render_html() render_json() 38
  • 41. Funzioni - Effetti collaterali Evitare gli effetti collaterali. Bad: def check_password(user, password): encrypted = crypt.crypt(password, PREFIX + SALT) if encrypted != self.password: return False user.is_logged = True # <-- Bad bad bad!! return True Se una funzione deve cambiare lo stato di un oggetto è il caso di implementare un metodo. 39
  • 43. Classi Una classe è definita per il suo comportamento, non per i suoi valori. E' perfettamente lecito avere classi anche con un solo metodo. Le classi senza metodi si chiamano DTO (Data Transfer Object). Evitare i fat objects (errore comune usando gli ORM). E' più facile testare e riutilizzare il codice quando è diviso in tante piccole classi. 41
  • 44. Classi - Gerarchia La gerarchia migliore è nessuna gerarchia. Evitare di ereditare due o più classi contemporaneamente. Evitare le classi mixin: complicano il codice e lo rendono estremamente rigido. Soluzione: comporre è meglio che ereditare. 42
  • 45. File sorgenti Evitare di usare più linguaggi nello stesso file. Il codice si complica Spesso si finisce per identare troppo Leggere è più faticoso L'evidenziazione della sintassi può non essere disponibile Modifiche e rifattorizzazioni richiedono più tempo Non si può lavorare in due sullo stesso codice 43
  • 47. Controllo del flusso - Yoda Evitare la Yoda notation. Bad: if (NULL == user) Good: if (user == NULL) 45
  • 48.
  • 49. Controllo del flusso - Precedenza (1) Negli if-else preferire i casi positivi all'inizio. Bad: if (a != b): # Not equal else: # Equal if (!user.is_admin()): # Not admin Good: if (a == b): # Equal else: # Not equal if (user.is_admin()): # Admin 46
  • 50. Controllo di flusso - Nesting (1) Come risolviamo obobri del genere? 47
  • 51. Controllo del flusso - Nesting (2) Francesco Cirillo - Tecnica del pomodoro e Anti-IF® Design Course 48
  • 52. Controllo del flusso - Nesting (3) Pratichiamo il fast exit. Bad: if http_port_is_open: if ssl_key is Null: server.listen(80) else: server.listen(80, ssl=True) else: raise Error('Port already used') Good: if !http_port_is_open: raise Error('Port already used') use_ssl = ssl_key is not Null server.listen(80, ssl=use_ssl) 49
  • 53. Controllo di flusso - Nesting (4) Separiamo questo garbuglio: def collect_emails(users): emails = set() for user in users: for friend in user.friends: if friend.email is None: continue else: emails.add(friend.email) return emails in due funzioni: def collect_emails(users): emails = [] for user in users: emails += collect_friends_emails(user.friends) return set(emails) def collect_friends_emails(friends): return [for x in friends if x is not None] 50
  • 54. Controllo di flusso - Legge di Demetrio Un oggetto non dovrebbe esporre la sua struttura interna. Ovvero: parla con gli amici, non con gli sconosciuti. Bad: return Template.load('templates/home.html').compile().render(context) Good: engine = TemplateEngine('templates/') html = engine.render('home.html', context) return html Vantaggio collaterale: sembra che nel secondo caso gli IDE performino meglio. 51
  • 56. Interfacce Definire il numero minimo di metodi. Non definire le proprietà, piuttosto incapsularle in metodi. Le migliori interfacce hanno un solo metodo. Se il linguaggio li mette a disposizione, usare i protocolli. Come si chiamano i protocolli nei vari linguaggi: Python: protocol JavaScript: protocol Go: interface Java: trait (devono essere dichiarati) Swift: protocol (devono essere dichiarati) Rust: trait (devono essere dichiarati) 53
  • 57. Test
  • 58. Test Quando il codice è breve: Ci sono meno combinazioni da provare I test diventano più piccoli Capita raramente di dover modificare un test E' molto più difficile rompere i test durante i refectoring 55
  • 59. Test - Esempio Quanto è testabile questa funzione? def please_test_me(user, check_password, post): message = '' if check_password: password = post.get('password') if password is None: raise Error('No password') else: if user.password_is_valid(password): message = 'allowed' else: message = 'not_allowed' else: if user.is_admin(): message = 'allowed' elif user.is_staff(): message = 'can_view' else message = 'not_allowed' return render('response.html', user, message) 56
  • 60. Test - Soluzione Divisa così è più testabile. def extract_password(post): password = post.get('password') if password is None: raise Error('No password') return password def message_by_status(user): if user.is_admin(): return 'allowed' elif user.is_staff(): return 'can_view' else return 'not_allowed' 57
  • 63. Commenti - Come e quando Precisi e compatti Spiegare il codice complicato Motivare dettagli non intutivi Commentare solo quando serve I commenti sono inutili se: Il codice è già tipizzato Classi e funzioni sono brevi e fanno una sola cosa Sono usati nomi chiari ed esplicativi 60
  • 64. Commenti - Codice Quando il codice si commenta da solo: Bad: ... # Check if client is billable if user.billable_hours > 20 or (today - last_invoice_date) > 30: ... Good: if user.isBillable(): ... 61
  • 65. Commenti - Superflui Quando i commenti sono superflui: class User: """A user.""" _is_active # The user is active def is_admin(self) -> bool: """Return True if User has admin privileges. If not return False.""" ... 62
  • 67. Strumenti Guide di stile http://google.github.io/styleguide/ Linter: individuano potenziali bug, errori di stile, pattern sospetti Code formatter: formattano il codice seguendo uno standard uguale per tutti Race detector: segnalano le race condition durante l'esecuzione di un programma Code review: obbligano la revisione del codice da parte di altri sviluppatori 64
  • 69. Thank you Davide Muzzarelli Developer consultant davide@webforce.it(mailto:davide@webforce.it) https://it.linkedin.com/in/davidemuzzarelli(https://it.linkedin.com/in/davidemuzzarelli)