SoftwareEngineeringPrinciples
Softwareengineeringiswhathappenstoprogrammingwhenyouaddtimeand
otherprogrammers.
JaapGroeneveld-jGroeneveld.de
SoftwareEngineeringdealswiththe
challengesof
Communicationbetweenpeople
Communicationovertime
Changingrequirements
Tamingcomplexity
Cleancodeismoreabouthumansthanits
abouttech.
2
Whyinvestincleancode?
Minimizestimespendonreadingandunderstanding
Easiertogrowinthefuture
Lesshuntingforbugs
Easieronboarding.
=>MessycodeisTechnicaldebt.Youwillpayforitdowntheroad.
Messycodehastheuncannyabilitytoslowdownanydeveloperandmakehiswork
muchharder
3
4
BadCodesmells
Rigidity.Thesoftwareisdifficulttochange.Asmallchangecausesacascadeof
subsequentchanges.
Fragility.Thesoftwarebreaksinmanyplacesduetoasinglechange.
Immobility.Youcannotreusepartsofthecodeinotherprojectsbecauseofinvolved
risksandhigheffort.
Opacity.Thecodeishardtounderstand.
NeedlessComplexity.
NeedlessRepetition.
5
6
Principles
Keepitsimple,stupid(KISS)
Youarenʼtgonnaneedit(YAGNI)&PrematureOptimization
Don'trepeatyourself(DRY),singlesourceoftruth.
Don'tconfusesyntacticandsemanticdry-ness
Clearisbetterthanclever.
"Short"doesnotmeaneasytoreadandunderstand.
Avoid"magic"(unobviousbehavior).
Practiceconsistency.
Keepthetestsclean.
Ifyouletthetestsrot,thenyourcodewillrottoo.
Prepareforchange
AlwaysaskhowwillthiscodebehaveifXchanges(butkeepYAGNIinmind) 7
CleanCode-Namingguidelines
Namesareforhumans
Choosedescriptiveandunambiguousnames.
Makemeaningfuldistinction.
Usepronounceablenames.
Usesearchablenames.
Replacemagicnumberswithnamedconstants.
Avoidencodings.Don'tappendprefixesortypeinformation.
8
CleanCode-Namingguidelines
Example
d := 12 // elapsed time in days
// vs
elapsedTimeInDays := 12
9
CleanCode-Explanatoryvariables
func doSomething() {
defer StartTimer()()
// ...
}
// vs
func doSomething() {
stopTimer := StartTimer()
defer stopTimer()
// ...
}
Yesitsonelinemore...
10
CleanCode-Onewordperconcept
Havingaclearterminologyiskeytonotgetconfused.
Itsmoreimportanttobeconsistentthentobeperfect.
Examples?
11
CleanCode-Functionguidelines
Small.
Doone"thing".(SRP-SingleResponsibilityPrinciple)
Usedescriptivenames.
Havenosideeffectsifpossible.
Preferfewerarguments.
Don'tuseflagarguments.Splitmethodintoseveralindependentmethodsthatcanbe
calledfromtheclientwithouttheflag.
12
CleanCode-Commentingguidelines
Onlycommentthewhyandonlyifnotobvious
Alwaystrytoexplainyourselfincode.
Useasexplanationofintent.
Useasclarificationofcode.
Useaswarningofconsequences.
Don'tcommentoutcode.Justremove.
13
StepdownRule
Codeshouldbereadfromtoptobottomlikeanarticle.
Wewanttoreaditasasetof"paragraphs"eachdescribingthecurrentlevelofabstraction
andreferencingdown.
Placefunctionsinthedownwarddirection.
Declarevariablesclosetotheirusage.
Keeplinesshort.
Usewhitespacetoassociaterelatedthingsanddisassociateweaklyrelated.
SeealsoCohesion...
14
Stepdown-Example
package api
func CreatePost(w http.ResponseWriter, r *http.Request) {
post, err := getPostFromRequest(r)
if err != nil {...}
err = validatePost(post)
if err != nil { ... }
err = savePost(post)
if err != nil { ... }
http.WriteHeader(201)
}
func getPostFromRequest(r *http.Request) (Post, error) { ... }
func validatePost(post Post) error { ... }
func savePost(post Post) error { ... }
15
Lineofsight
Alignthehappypathtotheleft;youshould
quicklybeabletoscandownonecolumntosee
theexpectedexecutionflow
see src/lineofsight.go
https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88 16
Lineofsight
Donʼthidehappypathlogicinsideanestofindentedbraces
Exitearlyfromyourfunction
Avoidelsereturns;considerflippingtheifstatement
Putthehappyreturnstatementastheverylastline
Extractfunctionsandmethodstokeepbodiessmallandreadable
Ifyouneedbigindentedbodies,considergivingthemtheirownfunction
https://medium.com/@matryer/line-of-sight-in-code-186dd7cdea88 17
CleanCode-Dedicatedvaluetypesoverprimitivetype.
func getUserData(userID string) {}
// vs
type UserID string
func getUserData(userID UserID) {}
Impossibleto"accidentally"assignsomethingelse
EasytofindwhereUserIDsareused(searchabilitythroughstaticanalysis)
Preventspositionalproblemsinfunctions
Allowsaddingfunctionstothetype
see typed_primitives.go
18
CleanCode-Avoidimplicitdependency
type Processor struct {
SomeData []string
}
func (p *Processor) Init() {
p.SomeData = []string{"foo", "bar"}
}
func (p *Processor) Step() {
element := p.SomeData[0]
// do something with element
}
Youhavetoknowthatyouhavetocall Init beforecalling Step ,otherwisetheprogram
willcrash.
19
CleanCode-Avoidimplicitdependency
func NewProcessor(someData []string) *Processor {
return &Processor{
SomeData: someData,
}
}
type Processor struct {
SomeData []string
}
func (p *Processor) Step() {
element := p.SomeData[0]
// do something with element
}
See http.Request vs http.NewRequest
20
CleanCode-Avoidimplicitdependency
Anotherexample
func GetFoo(w http.ResponseWriter, r *http.Request) {
dataStore := r.Context().Value("FooDataStore").(FooDataStore)
all := dataStore.All()
_ = json.NewEncoder(w).Encode(all)
}
func main() {
err := http.ListenAndServe(":8080", http.HandlerFunc(GetFoo))
if err != nil {
log.Fatal(err)
}
}
panic:interfaceisnil
21
CleanCode-Avoidimplicitdependency
Basicallyifyouhaveto"know"somethingthatmightbeanimplicitdependency
func GetFoo(dataStore FooDataStore) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
all := dataStore.All()
_ = json.NewEncoder(w).Encode(all)
})
}
func main() {
dataStore = NewFooDataStore()
err := http.ListenAndServe(":8080", GetFoo(dataStore))
if err != nil {
log.Fatal(err)
}
}
Weenforcecorrectusage 22
SOLIDPrinciples
RobertC.Martin
SingleResponsibilityPrinciple
Open-ClosedPrinciple
LiskovSubstitutionPrinciple
InterfaceSegregationPrinciple
DependencyInversionPrinciple
23
SRP/Separationofconcerns
Abstraction,increasemodularity
Lessneedtoknow/workonmultiplepartsofthecode(cognitiveload)
Easiertochangeimplementations
Easiertoisolatebugs
Seealsolayerarchitecture,coupling
24
CouplingvsCohesion
25
CouplingvsCohesion
Couplingreferstotheinterdependenciesbetweenmodules,whilecohesiondescribes
howrelatedthefunctionswithinasinglemoduleare
Highcoupling,lowcohesion=>
Lowcoupling,highcohesion=>
26
27
Highcohesion
SeeStepdownRule
Declarevariablesclosetotheirusage.
Dependentfunctionsshouldbeclose.
Similarfunctionsshouldbeclose(samefileorsamepackage).
SRPishelpful
28
Lowcoupling
DependencyinversionPrinciple
High-levelmodulesshouldnotdependonlow-levelmodules.Bothshoulddepend
onabstractions(e.g.interfaces).
InterfaceSegregationPrinciple
Thebiggertheinterface,theweakertheabstraction
29
DependencyinjectioninGo
Implicitinterfaces
DuckTyping
Avoidcyclicdependencies
30
DependencyinjectioninGo
package cli
type DataStore interface {
GetData() model.Data
}
func Run(dataStore DataStore) {}
package persistence
type FileDataStore struct {}
func (s *FileDataStore) GetData() model.Data
package main
func main() {
dataStore := &persistence.FileDataStore{}
cli.Run(dataStore)
}
31
Literature
Lessonslearntfrom“TheCleanCode”
RottingdesignBINGO!
SixShadesofCoupling
TheforgottenrealmofCohesion
32

Jaap Groeneveld - Software Engineering Principles