Consequences and Principles of Software Quality v1.0

yanngaelgueheneuc 60 views 153 slides Mar 06, 2025
Slide 1
Slide 1 of 153
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66
Slide 67
67
Slide 68
68
Slide 69
69
Slide 70
70
Slide 71
71
Slide 72
72
Slide 73
73
Slide 74
74
Slide 75
75
Slide 76
76
Slide 77
77
Slide 78
78
Slide 79
79
Slide 80
80
Slide 81
81
Slide 82
82
Slide 83
83
Slide 84
84
Slide 85
85
Slide 86
86
Slide 87
87
Slide 88
88
Slide 89
89
Slide 90
90
Slide 91
91
Slide 92
92
Slide 93
93
Slide 94
94
Slide 95
95
Slide 96
96
Slide 97
97
Slide 98
98
Slide 99
99
Slide 100
100
Slide 101
101
Slide 102
102
Slide 103
103
Slide 104
104
Slide 105
105
Slide 106
106
Slide 107
107
Slide 108
108
Slide 109
109
Slide 110
110
Slide 111
111
Slide 112
112
Slide 113
113
Slide 114
114
Slide 115
115
Slide 116
116
Slide 117
117
Slide 118
118
Slide 119
119
Slide 120
120
Slide 121
121
Slide 122
122
Slide 123
123
Slide 124
124
Slide 125
125
Slide 126
126
Slide 127
127
Slide 128
128
Slide 129
129
Slide 130
130
Slide 131
131
Slide 132
132
Slide 133
133
Slide 134
134
Slide 135
135
Slide 136
136
Slide 137
137
Slide 138
138
Slide 139
139
Slide 140
140
Slide 141
141
Slide 142
142
Slide 143
143
Slide 144
144
Slide 145
145
Slide 146
146
Slide 147
147
Slide 148
148
Slide 149
149
Slide 150
150
Slide 151
151
Slide 152
152
Slide 153
153

About This Presentation

Examples of (bad) consequences of a lack of software quality and some solutions. This presentation presents some examples of (bad) consequences of a lack of software quality, in particular how poor software quality led to the direct deaths of 89 people. It then provides some background on software q...


Slide Content

Yann-Gaël Guéhéneuc
This work is licensed under a Creative
Commons Attribution-NonCommercial-
ShareAlike 3.0 Unported License
Software Quality
[email protected]
Version 1.0
2025/03/01

2/153
Academic articles
Discussions
Blogs

3/153
Outline 
Context

Quality

Principles

Dependencies

Domain

Tools

Practicality

4/153
CONTEXT

5/153
Context 
Consequences of Poor Quality
–Toyota Software Glitches Killed 89 People
–Reasons for Poor Quality
–Why bad code exists

A Message to the Future

Write Code as If You Had to Support It for
the Rest of Your Life

6/153
Consequences of Poor Quality
We all heard horror stories…

7/153
Consequences of Poor Quality
We all heard horror stories…

8/153
Consequences of Poor Quality Just a few examples among many 
The Therac-25 incident: a
cautionary tale in
software errors

UK Post Office software
bug led to convicting 736
innocent employees

TUI airline miscalculated
flight loads 
Citibank UX caused a
$500 million failure

Knight Capital Group
$440M loss due to bad
trades

Toyota software glitches
killed 89 people 
Uber sued for $45 million
because of a notification
showing after log-out

Nest Thermostat update
left users in the cold
because of software bugs
https://softwaremill.com/bad-software-examples-how-much-can-poor-code-hurt-you/

9/153
Toyota Software Glitches
Killed 89 People
https://www.cbsnews.com/news/toyota-unintended-acceleration-has-killed-89/

10/153
Reasons for Poor Quality 
MISRA C
–Motor Industry Software Reliability Association
–Software development guidelines

Toyota not always followed guidelines
–MISRA C
–Its own
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

11/153
Reasons for Poor Quality 
Electronic Throttle Control System (ETCS)
https://static.nhtsa.gov/odi/inv/2014/INRD-DP14003-60186P .pdf

12/153
Reasons for Poor Quality 
Post-mortem analyses
–NASA
• 14 of 35 rules violated for 7,134 violations
• E.g., 105/343 “switch” blocks without “default”
–Barr’s team
• 80,000 violations of MISRA C
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

13/153
Reasons for Poor Quality 
McCabe Cyclomatic Complexity metric of
ETCS code
–67 functions with complexity over 50
–Throttle angle function complexity of 146
• 1,300 lines long, no unit test plan
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

14/153
Reasons for Poor Quality 
McCabe Cyclomatic Complexity metric of
ETCS code
–67 functions with complexity over 50
–Throttle angle function complexity of
146
•1,300 lines long, no unit test plan
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

15/153
Reasons for Poor Quality 
Global variables in ETCS code
–Ideally, 0
–4,720 read-only & text variables
–11,253 read/write variables
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

16/153
Reasons for Poor Quality 
Global variables in ETCS code
–Ideally,
0
–4,720 read-only & text variables –11,253
read/write variables
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

17/153
Reasons for Poor Quality 
Concurrency bugs
–Difficult to identify, reproduce
–Nested scheduler unlocks
–Shared global variables
• Not all “volatile”
• Not all masked
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

18/153
Reasons for Poor Quality 
Concurrency bugs
–Difficult to identify, reproduce
–Nested scheduler unlocks
–Shared global variables
• Not all “volatile”
• Not all masked
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

19/153
Reasons for Poor Quality 
Recursion and overflow
–Safety rules typically forbid recursion
–Stack 94% full + any recursion
–Overflow not always causing reset
–Memory after stack is OSEK RTOS
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

20/153
Reasons for Poor Quality 
Recursion and overflow
–Safety rules typically forbid recursion
–Stack 94% full
+ any recursion
–Overflow not always causing reset
–Memory after stack is OSEK RTOS
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

21/153
Reasons for Poor Quality 
And also
–Wrongly coded watchdog
–Ignoring RTOS error codes
–Major task excluded from watchdog
–No single responsibility
–Many large functions
–No configuration management
–No bug tracking system
https://users.ece.cmu.edu/~koopman/pubs/koopman14_toyota_ua_slides.pdf

22/153
Why Bad Code Exists? 
No continuous (active) review process

No editing process

Insufficient technical knowledge

Time pressure

Inadequate technical controls/fear

Lack of care
https://www.industriallogic.com/blog/why-bad-code-exists/

23/153
Why Bad Code Exists? 
No continuous (active) review process
–“[G]osh, that looks good”
–Post facto (not active) review process vs. mob or
pair programming 
“This bad code exists because it was the
product of one person’s cleverness, rather
than a solution the entire team has blessed.”
https://www.industriallogic.com/blog/why-bad-code-exists/

24/153
Why Bad Code Exists? 
No editing process
–“All writers, even the great ones, review and edit
their prose.”
–“The best writing is rewriting.”—Elwyn B. White

“This bad code exists because it[is] an initial
draft, and not an edited final product.”
https://www.industriallogic.com/blog/why-bad-code-exists/

25/153
Why Bad Code Exists? 
Insufficient technical knowledge
–“Undisciplined code resulting from a defective or
incomplete understanding of”
• Technical stack
• Quality practices
• Design principles

“This bad code exists because [of] people
who didn’t know enough to build it well.”
https://www.industriallogic.com/blog/why-bad-code-exists/

26/153
Why Bad Code Exists? 
Time pressure
– Efficiency vs.Fear
– Shortcuts
– Resentment
– Judgement
– Not learning

“This bad code exists because it was the quickest
thing that a person under pressure could
accomplish without learning anything new.”
https://www.industriallogic.com/blog/why-bad-code-exists/

27/153
Why Bad Code Exists? 
Inadequate technical controls/fear
–Parameterisation vs. Redesign
• “Bad code got you into this mess. More bad code
won’t get you out of it.”
–Fight unknow and testing regressions
• Characterisation tests 
“This bad code exists because […] someone
[was] afraid to do the right thing.”
https://www.industriallogic.com/blog/why-bad-code-exists/

28/153
Why Bad Code Exists? 
Lack of care
–Bread-and-butter job
–Sillinesses
• Timecards, status reports, (after-the-fact) unit tests,
pull requests, etc.

“This bad code exists because someone
didn’t care about you or your teammates.”
https://www.industriallogic.com/blog/why-bad-code-exists/

29/153
Conclusion?

30/153
A Message to the Future
(By Linda Rising)

31/153
A Message to the Future
(By Linda Rising)

32/153
A Message to the Future
(By Linda Rising)

“Think of every line of code you write as a
message for someone in the future”
– “Pretend you're explaining to this smart [programmer]
how to solve this tough problem”

“That the smart programmer in the future [should]
see your code and say, 'Wow! This is great!”
– “I can understand perfectly what's been done here and
I'm amazed at what an elegant”
– “I'm going to show the other folks on my team”

33/153
Write Code as If You Had to Support
It for the Rest of Your Life
(By Yuriy Zubarev)

“[H]ow do you keep up with all the best
practices you've learned and how do you
make them an integral part of your
programming practice?”

“I think the answer lies in your frame of mind
or, more plainly, in your attitude.”

34/153
QUALITY

35/153
Quality 
Quality Without a Name

Technical Debt

Beautify is Simplicity

The Boy Scout Rule

You Gotta Care About the Code

The Longevity of Interim Solutions

36/153
Quality Without a Name



37/153
Quality Without a Name
“There is a point you reach in tightening a nut, wh ere you
know that to tighten just a little more might strip the thread,
but to leave it slightly looser would risk having t he hut
coming off from vibration. If you've worked with yo ur hands
a lot, you know what this means, but the advice its elf is
meaningless.”
—Robert Pirsig, circa. 1972

38/153
Quality Without a Name
“There is a point you reach in tightening a nut, wh ere you
know that to tighten just a little more might strip the thread,
but to leave it slightly looser would risk having t he hut
coming off from vibration. If you've worked with yo ur hands
a lot, you know what this means, but the advice its elf is
meaningless.”
—Robert Pirsig, circa. 1972

39/153
Quality Without a Name
“There is a point you reach in tightening a nut, wh ere you
know that to tighten just a little more might strip the thread,
but to leave it slightly looser would risk having t he hut
coming off from vibration. If you've worked with yo ur hands
a lot, you know what this means, but the advice its elf is
meaningless.”
—Robert Pirsig, circa. 1972
“[T]oo often software developers spend their days g rinding
away for pay at programs they neither need nor love . But
not in the Linux world -which may explain why the
average quality of software originated in the Linux
community is so high.”
—Eric Raymond, 1998

40/153
Quality Without a Name

41/153
Quality Without a Name
ニワトリのヒナの雌雄鑑別
(chick sexing)


http://discovermagazine.com/2011/sep/18-your-brain-knows-l ot-more-than-you-realize

42/153
Quality Without a Name
ニワトリのヒナの雌雄鑑別
(chick sexing)
“The master would stand over the
apprentice and watch. The student
would pick up a chick, examine its
rear, and toss it into one bin or the
other. The master would give
feedback: yes or no.”
—Eagleman, 2011
http://discovermagazine.com/2011/sep/18-your-brain-knows-l ot-more-than-you-realize

43/153
Quality Without a Name
public void
diso()
throws
CorruptIndexException, FileNotFoundException, IOException
DISCO
disco
= DISCO.load(
"cc.en.300-COL.denseMatrix"
);
float
sim
=
disco
.semanticSimilarity(
"shopping"
,
"product"
, DISCO.getVectorSimi
System.
out
.println(
"++++++++++++++++++++++++++++++++++++++++++similarity betwee
// get word vector for "Haus" as map Map<String,Float>
wordVectorHaus
=
disco
.getWordvector(
"shopping"
);
// get word embedding for "Haus" as float array float
[]
wordEmbeddingHaus
= ((DenseMatrix)
disco
).getWordEmbedding(
"shopping"
);
// solve analogy x is to "Frau" as "König" is to "Mann" try
{
List<ReturnDataCol>
result
= Compositionality.solveAnalogy(
"bride"
,
"groom"
,
"c
}
catch
(WrongWordspaceTypeException
e
) {
//
TODO
Auto-generated catch block
e
.printStackTrace();
}
}

44/153
Technical Debt 
"As [a] program is continually changed, its
complexity, […] deteriorating structure, increases
unless work is done to maintain or reduce it."
— Meir Manny Lehman, 1980

“Every minute spent on not-quite-right code counts
as interest on that debt. Entire engineering
organizations can [stop] under the debt load[…]"
— Ward Cunningham, 1992”

45/153
Beautify is Simplicity
(By Jørn Ølmheim)
“Beauty of style and harmony and grace and
good rhythm depends on simplicity.”
—Plato

“Plato is telling us that the enabling factor for
all of these qualities is simplicity.”

46/153
The Boy Scout Rule
(By Uncle Bob aka Robert C. Martin)

“Always leave the campground cleaner than
you found it.”

“[L]eaving a mess in the code should be as
socially unacceptable as littering.”

“Teams help each other, and clean up after
each other. They follow the Boy Scout rule
because it's good for everyone”

47/153
You Gotta Care About the Code
(By Pete Goodliffe)

“No programmer is an island.”
https://www.creativefabrica.com/it/product/palm-tree-with -small-island/

48/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/

49/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks

50/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code

51/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests

52/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code

53/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code

54/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code
Correct code

55/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code
Correct code
Work with others

56/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code
Correct code
Work with others
Not look clever

57/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code
Correct code
Work with others
Not look clever
Leave it better

58/153

https://www.creativefabrica.com/it/product/palm-tree-with -small-island/
No hacks
Elegant code
Good tests
Discoverable code
Maintainable code
Correct code
Work with others
Not look clever
Leave it better
Learn new languages, practices, techniques

59/153
The Longevity of Interim Solutions
(By Klaus Marquardt)

Why?
–“It is useful.”

Consequences?
–“[I]nterim solution remains in place. Forever.”
–Not “with accepted production quality”

What to do then?
–“Change the forces that influence the decision”

60/153
PRINCIPLES

61/153
Principles 
Abstraction, Encapsulation, Inheritance,
Polymorphism

Missing Opportunities for Polymorphism

SOLID and Other Principles

Functional Programming Principles

Write Small Functions Using Examples

Coding with Reason

Intellectual Control

62/153
Abstraction, Encapsulation,
Inheritance, Polymorphism

Fundamental concepts
–Encapsulation
–Inheritance, types
–Polymorphism (overriding, overloading)
–Abstraction, types

63/153
Encapsulation 
In the 1980s, object-oriented programming
languages became popular, in part because they
propose
encapsulation

Encapsulation is
– A language mechanism for restricting access to some of
the object's components
• Modifiers public, protected, private…
– A language construct to bundle data with the methods
operating on that data
• Classes and objects

64/153
Encapsulation 
In the 1980s, object-oriented programming
languages became popular, in part because they
propose
encapsulation

Encapsulation is
– A language mechanism for restricting access to some of
the object's components
• Modifiers public, protected, private…
– A language construct to bundle data with the methods
operating on that data
• Classes and objects
Fundamental to OO
programming languages

65/153
Inheritance, Types 
Reuse and subtyping
combined
– To factor out code,
e.g., in PADL
– To create a taxonomy,
e.g., in Java libraries

66/153
Polymorphism 
Definition
–Subtype polymorphism, almost universally called
polymorphism in object-oriented programming
–The ability to create a variable, a function, or a n
object that has more than one form
–Superset of overloading, overriding
Problem: change behaviour of concrete objects
Solution: polymorphism

67/153
Overriding, Overloading 
Overriding
–A language feature that allows a subclass or a
child class to provide a
specific
implementation
of a method that is already
provided by one of its super / parent classes

Overloading
–Methods that have the
same name
but
different
signatures
inside the same class or a class
hierarchy

68/153
Abstraction, Types 
From worst to best
BankAccount
1.ArrayList
getBankAccounts()
2.List
getBankAccounts()
3.Iterator
getBankAccounts()
Shape
1.Vector
getPoints()
2.Vector
getPoints()
3.Iterator
getPoints()

69/153
Missing Opportunities for
Polymorphism
(By Kirk Pepperdine)

“[P]olymorphic coding style will yield a
smaller, more readable and less fragile code
base. The number of missed opportunities is
a simple count of the if-then -else statements
in our code.”

70/153
Missing Opportunities for
Polymorphism
(By Kirk Pepperdine)
https://refactoring.com/catalog/replaceConditionalWithPo lymorphism.html
switch
(bird.type) {
case
'EuropeanSwallow’
:
return
"average"
;
case
'AfricanSwallow’
:
return
(bird.numberOfCoconuts > 2) ?
"tired"
:
"average"
;
case
'NorwegianBlueParrot’
:
return
(bird.voltage > 100) ?
"scorched"
:
"beautiful"
;
default
:
return
"unknown"
;
}

71/153
class
EuropeanSwallow
implements
Bird {
String getPlumage() {
return
"average"
;
}
// ...
}class
AfricanSwallow
implements
Bird {
String getPlumage() {
return
(
this
.numberOfCoconuts > 2) ?
"tired"
:
"average"
;
}
// ... }class
NorwegianBlueParrot
implements
Bird {
String getPlumage() {
return
(
this
.voltage > 100) ?
"scorched"
:
"beautiful"
;
}
// ...
}
https://refactoring.com/catalog/replaceConditionalWithPo lymorphism.html
switch
(bird.type) {
case
'EuropeanSwallow’
:
return
"average"
;
case
'AfricanSwallow’
:
return
(bird.numberOfCoconuts > 2) ?
"tired"
:
"average"
;
case
'NorwegianBlueParrot’
:
return
(bird.voltage > 100) ?
"scorched"
:
"beautiful"
;
default
:
return
"unknown"
;
}

72/153
SOLID and Other Principles 
SOLID

GRASP

YAGNI, KISS, and DRY

Do Not Reinvent the Wheel

Law of Demeter

Beware of Assumptions

Deletable Code

73/153
SOLID 
Single-responsibility principle

Open–closed principle

Liskov substitution principle

Interface segregation principle

Dependency inversion principle

74/153
GRASP 
General Responsibility
Assignment Software
Principles
– Creator
– Information Expert
– Low Coupling
– Controller
– High Cohesion
– Indirection
– Polymorphism
– Protected Variations
– Pure Fabrication

75/153
YAGNI, KISS, DRY 
You Aren’t Gonna Need It

Keep It Simple, Stupid

Don’t Repeat Yourself

76/153
Do Not Reinvent the Wheel 
Before implementing a feature, always look
for simpler, existing solutions
–Existing piece of code?
–Existing libraries?

Beware when reusing an existing solution
–Licensing issues
–Dependency issues
–Ratio cost / benefit
in the long term

77/153
Law of Demeter 
Ian Holland in 1987

Demeter Project

Andrew Hunt and Dave Thomas in 2000
https://www.khoury.northeastern.edu/home/lieber/LoD.ht ml

78/153
Law of Demeter 
Principle of least knowledge
–Only talk to your friends

Empirically validated
–https://ieeexplore.ieee.org/document/6079847/
https://www.khoury.northeastern.edu/home/lieber/LoD.ht ml
objectA.getObjectB().getObjectC().[...].doSomething();

79/153
Law of Demeter 
Principle of least knowledge
–Only talk to your friends

Empirically validated
–https://ieeexplore.ieee.org/document/6079847/
https://www.khoury.northeastern.edu/home/lieber/LoD.ht ml
objectA.getObjectB().getObjectC().[...].doSomething();
objectA.doSomething();

80/153
Beware of Assumptions

81/153
Deletable Code “Write code that is easy to delete, not easy to ext end”
– Step 0: Don’t write code
– Step 1: Copy-paste code
– Step 2: Don’t copy paste code
– Step 3: Write more boilerplate
– Step 4: Don’t write boilerplate
– Step 5: Write a big lump of code
– Step 6: Break your code into pieces
– Step 7: Keep writing code

82/153
Functional Programming Principles
(By Edward Garson)

Referential transparency

Short-lived objects

Immutability

Don’t over do it, though…
–Domain model vs. user interfaces

83/153
Write Small Functions Using
Examples (By Keith Braithwaite)

Potentially, 4.3 ×10
9
tests, to be compared
with the following, with one and only test
boolean
atari(
final int
libertyCount
) {
return
libertyCount
< 2;
}
private enum
LibertyCount {
ONE
,
TWO
,
THREE
,
FOUR
} boolean
atari(
final
LibertyCount
libertyCount
) {
return
libertyCount
== LibertyCount.
ONE
;
}

84/153
Coding with Reason
(By Yechiel Kimchi)

“Trying to reason about software correctness
by hand results in a formal proof that is
longer than the code and is more likely to
contain errors than the code.”


[D]ivide all the code […] into short sections
–from a single line […] to blocks of less than
ten lines –and argu[e] about [them].”

85/153
Intellectual Control
(By George Fairbanks)

“[O]nce a team loses intellectual control, it’s
difficult and expensive to recover.”

“[S]imple testing […] obscures the signal,
making it hard to tell that the practices are
lousy until [it’s too complicated]”

Intellectual Control vs. Statistical Control
G. Fairbanks, "Testing Numbs Us to Our Loss of Intellectua l Control," in IEEE
Software, vol. 37, no. 3, pp. 93-96, May-June 2020, doi: 10.1109/MS.2020.2974636

86/153
DEPENDENCIES

87/153
Dependencies 
Beware the Share

Encapsulate Behavior, not Just State

Single Responsibility Principle

WET Dilutes Performance Bottlenecks

88/153
Beware the Share
(By Udi Dahan)

“The code review that I had felt so ready for
came as a rude awakening —reuse was
frowned upon!”

“Context. […] Up until I had pulled out those
libraries of shared code, these parts were
not dependent on each other. Each could
evolve independently.”

89/153
Encapsulate Behavior, not Just State
(By Einar Landre)

“[D]evelopers might decide to wrap all the
business rules into an object very often
referred to as OrderManageror
OrderService. In these designs, Order,
Customer, and Itemare treated as little more
than record types.

90/153
Single Responsibility Principle
(By Uncle Bob aka Robert C. Martin)

“Gather togetherthose things that change
for the same reason and separate those
things that change for different reasons.”
public class
Employee {
public
Money calculatePay() {
//...
}public
String reportHours() {
//...
}public void
save() {
//...
}
}

91/153
Single Responsibility Principle
(By Uncle Bob aka Robert C. Martin)

“Gather together those things that change
for the same reason and separate those
things that change for different reasons.”
public class
Employee {
public
Money calculatePay() {
//...
}public
String reportHours() {
//...
}public void
save() {
//...
}
}
public class
Employee {
public
Money calculatePay() {
//...
}
}
public class
EmployeeReporter {
public
String reportHours(Employee
e
) {
//...
}
}
public class
EmployeeRepository {
public void
save(Employee
e
) {
//...
}
}

92/153
WET Dilutes Performance
Bottlenecks (By Kirk Pepperdine)

“The performance implications of DRY
versus WET become very clear when you
consider their […] effects on [performance].”
private
ArrayList<Customer>
allCustomers = ...
;
// ... public
ArrayList<Customer> findCustomersThatSpendAtLeast(Money
amount
) {
ArrayList<Customer>
customersOfInterest
=
new
ArrayList<Customer>();
for
(Customer
customer
:
allCustomers
) {
if
(
customer
.spendsAtLeast(
amount
))
customersOfInterest
.add(
customer
);
}
return
customersOfInterest
;
}

93/153
WET Dilutes Performance
Bottlenecks (By Kirk Pepperdine)

“The performance implications of DRY
versus WET become very clear when you
consider their […] effects on [performance].”
public class
CustomerList {
private
ArrayList<Customer>
customers
=
new
ArrayList<Customer>();
private
SortedList<Customer>
customersSortedBySpendingLevel
=
new
SortedList<Customer>();
// ... public
CustomerList findCustomersThatSpendAtLeast(Money
amount
) {
returnnew
CustomerList(
customersSortedBySpendingLevel
.elementsLargerThan(
amount
));
}
}

94/153
DOMAIN

95/153
Domain 
Convenience Is Not an -ility

Code in the Language of the Domain

Comment Only What the Code Cannot Say

Distinguish Business Exception from
Technical

Prefer Domain-specific Types to Primitive
Types

96/153
Convenience Is Not an -ility
(By Gregor Hohpe)

“Most experienced programmers have
learned that a good API follows a consistent
level of abstraction, exhibits consistency and
symmetry, and forms the vocabulary for an
expressive language.”
parser.processNodes(text,
false
);

97/153
Code in the Language of the Domain
(By Dan North)

“Making domain concepts explicit in your
code means other programmers can gather
the intent of the code much more easily than
by trying to retrofit an algorithm into what
they understand about a domain.”
if
(portfolioIdsByTraderId
.get(trader.getId())
.containsKey(portfolio.getId())) {
// ...
}
if
(trader.canView(portfolio)) {
// ...
}
VS.

98/153
Comment Only What the Code
Cannot Say (By Kevlin Henney)

“"a comment is of zero (or negative) value if
it is wrong."”

“Comments that parrot the code offer
nothing extra to the reader”

“Comments should say something code
does not and cannot say.”

“Comment what the code cannot say, not
simply what it does not say.”

99/153
Distinguish Business Exception from
Technical (By Dan Bergh Johnsson)

“An unresolvable technical problem can
occur when there is a programming error.”

“In contrast to these, we have the situation
where you cannot complete the call for a
domain-logical reason.”

“Mixing technical exceptions and business
exceptions in the same hierarchy blurs the
distinction and confuses the caller about
what the method contract is”

100/153
Prefer Domain-specific Types to
Primitive Types (By Einar Landre)

“In Java, C++, Python, and
[others,] the abstract data
type is known as class.”

“Using classes such as
Velocity_In_Knotsand
Distance_In_Nautical_
Milesadds a lot of […]
code quality”

The code becomes more
readable as it expresses
concepts of a domain, not
just Floator String.

The code becomes more
testable as the code
encapsulates behavior that
is easily testable.

The code facilitates reuse
across applications and
systems.

101/153
TOOLS

102/153
Tools 
Automate Your Coding Standards

Code Layout Matters

Before You Refactor

Improve Code by Removing It

Put the Mouse Down and Step Away from
the Keyboard

103/153
Automate Your Coding Standards
(By Filip van Laenen)

“Well-formatted code
doesn't earn you points
with a customer that wants
more functionality.”

“Make sure code
formatting is part of the
build process, so that
everybody runs it
automatically every time
they compile the code.

Use static code analysis
tools to scan the code for
unwanted anti-patterns. If
any are found, break the
build.”

104/153
Code Layout Matters
(By Steve Freeman)

“People are really good at visual pattern
matching”

“The code's layout is part of this
expressiveness too.”

“The more I can get on a screen, the more I
can see without breaking context”

105/153
Before You Refactor
(By Rajith Attapattu)

“[Start] by taking stock of the existing
codebase and the tests written against that
code.”

“Avoid the temptation to rewrite everything.”

“Many incremental changes are better than
one massive change.”

“After each iteration, it is important to ensure
that the existing tests pass.”

106/153
Improve Code by Removing It
(By Pete Goodliffe)

“What are you working on right now? Is it all
needed?”

107/153
Put the Mouse Down and Step Away
from the Keyboard (By Burk Hufnagel)

“[While] you're coding, the logical part of
your brain is active, and the creative side is
shut out. It can't present anything to you until
the logical side takes a break.”

108/153
PRACTICALITY

109/153
Concretely 
Analysis of the source code of the
middleware
–https://github.com/ptidejteam/tools4cities-
middleware/releases/tag/v1.1.0-beta

110/153
Concretely 
Meta

Architecture

Design

Implementation

111/153
Meta 
mvn testshould produce
–No warnings
[WARNING] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must
be unique: org.springframework.boot:spring-boot-starter:jar -> duplicate
declaration of version (?) @ line 68, column 15
[WARNING] 'dependencies.dependency.(groupId:artifactId:type:classifier)' must
be unique: org.springframework.boot:spring-boot-starter-test:jar -> duplicate
declaration of version (?) @ line 74, column 15

112/153
Meta 
mvn testshould produce
–No warnings
–No output
–No exception!
Run started!
No operations to apply
{}
404
Run completed!
Run started!
No operations to apply
java.nio.file.NoSuchFileException: .\citydata-files\[...].citydata
[...]
at ca.concordia.encs.citydata.datastores.DiskDatastore.get(DiskDatastore.java:56)
[...]
at ca.concordia.encs.citydata.core.ApplyController$1.run(ApplyController.java:40)

113/153
Meta “[O]nlytwo hard things in Computer Science:
cache invalidation and naming things.”
—Phil Karlton
*

Caching
–Not yet implemented but still…

Naming
–CityLayervs. Middleware vs. citydata...
*
https://martinfowler.com/bliki/TwoHardThings.html

114/153
Meta 
Never use Strings as keys
–Must use something parsable/verifiable
• UUIDs
–For example, in the middleware
• ca.[...].citydata.operations.AverageOperation
–Also, in Ptidej
• Like "padl.example.relationship.MethodDump
p.java.lang.Integer,java.lang.Integer
compareTo.int (java.lang.Integer)"

115/153
Meta 
Quickly merge branches
–Branch
develop
–Branch
master

116/153
Meta 
Use JavaDoc

117/153
Meta 
Licensing
–GPL v2 or v3

118/153
Meta 
Use Ptidej preferences
–E.g.,
else
statement below the
}
closing the
if
–Lint-like tools

119/153
Architecture 
Use the JPMS!
–Cf. slides on the new features of Java
–https://www.ptidej.net/tutorials/newjava
module
Middleware {
requires
spring.boot;
...
requires
com.google.gson;
}

120/153
Architecture 
Distinguish rawand specialisedproducers
– ca.[...].citydata.producers.CSVProducer
– ca.[...].citydata.producers.
EnergyConsumptionProducer

121/153
Design 
Used sealedclasses
–Cf. slides on the new features of Java
–https://www.ptidej.net/tutorials/newjava

122/153
Design 
Class ca.[...].core.MiddlewareEntity
–Class vs. Interface
–Naming

123/153
Design 
Put all the controllers in their own package
–Also keep only interfaces in
.core
–Also move util class(es) in their own package

124/153
Design 
Method ca.[...].citydata.core.AbstractOperation
.apply(ArrayList<E>)
–Should not be implementedif not supposed to
be invoked on instances of
AbstractOperation

Use more abstract (i.e.,interface) types
– List
vs.
ArrayList
@Override public
ArrayList<E> apply(ArrayList<E>
input
) {
if
(
input
==
null
||
input
.isEmpty()) {
throw new
Exceptions.InvalidOperationParameterException(
"Input data is null [...]"
);
}
System.
out
.println(
"Unimplemented method! This method must be implemented by a subclass."
);
return null
;
}

125/153
Design 
Method
ca.concordia.encs.citydata.core.Abstra
ctProducer.notifyObservers()
–Hides the exception
–Deals with a checked exceptions
• Should use unchecked exceptions, RuntimeException
@Override public void
notifyObservers() {
try
{for
(
final
Iterator<IRunner>
iterator
=
this
.
runners
.iterator();
iterator
.hasNext();) {
final
IRunner
runner
=
iterator
.next();
runner
.newDataAvailable(
this
);
}
}
catch
(
final
Exception
e
) {
e
.printStackTrace();
}
}

126/153
Design 
Do not have methods specialising others
– Object ca.concordia.encs.citydata
.core.AbstractProducer.getResult()
public
String getResultJSONString() {
JsonArray
jsonArray
=
new
JsonArray();
if
(
this
.
result
==
null
) {
return null
;
}
else if
(
this
.
result
.size() > 0 &&
this
.
result
.get(0)
instanceof
JsonObject) {
for
(E
el
:
this
.
result
) {
jsonArray
.add((JsonObject)
el
);
}
}
else if
(
this
.
result
.size() > 0 &&
this
.
result
.get(0)
instanceof
JsonElement) {
for
(E
el
:
this
.
result
) {
jsonArray
.add((JsonElement)
el
);
}
}
else
{
JsonObject
result
=
new
JsonObject();
result
.addProperty(
"result"
,
this
.
result
.toString());
jsonArray
.add(
result
);
}
return
jsonArray
.toString();
}

127/153
Design 
Method
ca.concordia.encs.citydata.core.Abstra
ctProducer.applyOperation()
–How could the operation not exist?
–Should the observers be notified if it doesn't?
–Why not use
apply()
as in other classes?
@Override public void
applyOperation() {
// if an operation exists, apply it, notify anyway after done if
(
this
.
operation
!=
null
) {
this
.
result
=
this
.
operation
.apply(
this
.
result
);
}
this
.notifyObservers();
}

128/153
Design 
Why is there setOperation()?

Why is there getResultJSONString()?
public interface
IProducer<E> {
void
addObserver(
final
IRunner
aRunner
);
void
setOperation(
final
IOperation
operation
);
void
fetch()
throws
Exception;
void
applyOperation();
void
notifyObservers();
ArrayList<E> getResult();
String getResultJSONString();
}

129/153
Design 
Should Singletons be global variables?
–In
ca.concordia.encs.citydata.core.
ApplyController.sync(String)
InMemoryDataStore
store
= InMemoryDataStore.getInstance();

130/153
Design 
Does IDataStore<IProducer>make sense?

ShoulduseUUIDinsteadofStrings
public interface
IDataStore<E> {
void
set(String
key
, E
value
);
E get(String
key
);
void
delete(String
key
);
}

131/153
Design 
Why do these methods exist?
– applyNextOperation(IProducer<?>)
,
newDataAvailable(IProducer<?>)
,
newOperationApplied(IOperation<?>)
,
setAsDone()
,
storeResults(IProducer<?>)
public interface
IRunner {
void
runSteps()
throws
Exception;
void
applyNextOperation(IProducer<?>
producer
)
throws
Exception;
void
storeResults(IProducer<?>
producer
)
throws
Exception;
boolean
isDone();
void
setAsDone();
void
newOperationApplied(IOperation<?>
operation
);
void
newDataAvailable(IProducer<?>
producer
)
throws
Exception;
}

132/153
Implementation 
Remove all warnings
IOperation is a raw type. References to generic type IOperation<E> should be parameterizedType safety:
IOperation is a raw type. References to generic type IOperation<E> should be parameterized
Type safety: The expression of type IOperation needs unchecked conversion to conform to IOperation<JsonObject>
Type safety: Unchecked cast from ArrayList<capture#3-of ?> to ArrayList<JsonObject>
IOperation is a raw type. References to generic type IOperation<E> should be parameterized
Type safety: The expression of type IOperation needs unchecked conversion to conform to IOperation<String>
The serializable class Exceptions does not declare a static final serialVersionUID field of type long
The serializable class InvalidOperationException does not declare a static final serialVersionUID field of type lon
The serializable class InvalidOperationParameterException does not declare a static final serialVersionUID field of type long
The serializable class InvalidProducerException does not declare a static final serialVersionUID field of type long
The serializable class InvalidProducerParameterException does not declare a static final serialVersionUID field of type long
The serializable class UnsupportedParameterTypeException does not declare a static final serialVersionUID field of type long
The value of the field FirebaseProducer.databaseURL is not used
The value of the field FirebaseProducer.requestOptions is not used
IOperation is a raw type. References to generic type IOperation<E> should be parameterized
Type safety: The expression of type IOperation needs unchecked conversion to conform to IOperation<JsonObject>
IOperation is a raw type. References to generic type IOperation<E> should be parameterized
Type safety: Unchecked cast from ArrayList<capture#1-of ?> to ArrayList<String>
Project 'Middleware' has no explicit encoding set
IOperation is a raw type. References to generic type IOperation<E> should be parameterized
Type safety: The expression of type IOperation needs unchecked conversion to conform to IOperation<JsonObject>
Type safety: Unchecked cast from ArrayList<capture#3-of ?> to ArrayList<JsonObject>

133/153
Implementation 
Always prefix method calls/field accesses
– this
for instance ones
–Class names for static ones

Always use final

134/153
Implementation 
Always sort members
–For example, in
ca.[...].citydata.
datastores.InMemoryDataStore

135/153
Implementation 
Path names should always end with /
public class
DiskDatastore
extends
MiddlewareEntity
implements
IDataStore<
byte
[]> {
private static final
String
filePrefix
=
".citydata"
;
private static final
String
baseFolderPath
=
"./citydata-files"
;
private static final
DiskDatastore
storeInstance
=
new
DiskDatastore();
[...]
@Override public void
set(String
key
,
byte
[]
value
) {
String
path
=
baseFolderPath
+
"/"
+
key
+
filePrefix
;

136/153
Implementation 
All instance variables must be private
– ca.[...].citydata.core.RequestOptions
public class
RequestOptions {
public
String
method
;
public
String
requestBody
=
""
;
public
Boolean
returnHeaders
=
false
;
public
HashMap<String, String>
headers
=
new
HashMap<String, String>();
public void
addToHeaders(String
key
, String
value
) {
headers
.put(
key
,
value
);
}
}

137/153
Implementation 
Methods should either public or private
–Rarely protected
–Very rarely package protected
– ca.concordia.encs.citydata.core.
AbstractProducer.doHTTPRequest()

138/153
Implementation 
Use Optionalinstead of returning, and
testing!, for null
– java.util.Optional<T>

139/153
Implementation 
Clean up classes
– ca.concordia.encs.citydata.core.
AbstractOperation.AbstractOperation()
– ca.concordia.encs.citydata.producers.
CKANProducer.isFileSupported(String) –Extract
String
s
• Create constants
– Distinguish National Language Support using // $NON-NLS

140/153
Implementation 
Remove extra spaces
–Make the code “beautiful”
@Override public void
notifyObservers() {
for
(
final
Iterator<IRunner>
iterator
=
this
.
runners
.iterator();
iterator
.hasNext();) { final
IRunner
runner
=
iterator
.next();
runner
.newOperationApplied(
this
);
}
}

141/153
Implementation 
Instance variables
–Should be immutable as much as possible
–Should be initialised on instantiation
public abstract class
AbstractProducer<E>
extends
MiddlewareEntity
implements
IProducer<E> {
protected
String
filePath
;
protected
RequestOptions
fileOptions
;
public
IOperation<E>
operation
;
private
Set<IRunner>
runners
=
new
HashSet<>();
protected
ArrayList<E>
result
;
public
AbstractProducer() {
this
.setMetadata(
"role"
,
"producer"
);
}

142/153
Implementation 
Declare variables as close as possible to
their usages
public class
ApplyController {
@RequestMapping
(value =
"/sync"
, method = RequestMethod.
POST
)
public
ResponseEntity<String> sync(
@RequestBody
String
steps
) {
String
runnerId
=
""
;
String
errorMessage
=
""
;
HttpStatus
responseCode
= HttpStatus.
OK
;
try
{
JsonObject
stepsObject
= JsonParser.parseString(
steps
).getAsJsonObject();
SequentialRunner
deckard
=
new
SequentialRunner(
stepsObject
);
runnerId
=
deckard
.getMetadataString(
"id"
);

143/153
Implementation 
Why use arrays of bytes if these are files?
protected byte
[] fetchFromPath() {
try
{// If the file path is a URL and there are RequestOptions if
(
this
.
filePath
!=
null
&&
this
.
filePath
.contains(
"://"
) &&
this
.
fileOptions
!=
null
) {
return this
.doHTTPRequest();
}
// else, fetch from the filesystem return this
.readFile();

144/153
Implementation 
Why is there a setAsDone()method?
– Runner
s should set their own status
–The method name could be better
public abstract class
AbstractRunner
extends
MiddlewareEntity
implements
IRunner {
private boolean
isDone
=
false
;
public
AbstractRunner() {
this
.setMetadata(
"role"
,
"runner"
);
}
@Override public boolean
isDone() {
return this
.
isDone
;
}
@Override
public void
setAsDone() {
this
.
isDone
=
true
;
}
}

145/153
Implementation 
Do we need the main()method?
@SpringBootApplication public class
Application {
// initialize all datastore for later use InMemoryDataStore
memoryStore
= InMemoryDataStore.getInstance();
DiskDatastore
diskStore
= DiskDatastore.getInstance();
public static void
main(String[]
args
) {
SpringApplication.run(Application.
class
,
args
);
}
}

146/153
Implementation 
ca.concordia.encs.citydata.core.
ReflectionUtils
–Mixed of utils for JSON objects and others
–Split it in two

147/153
Implementation 
Rename PRODUCER_ROOT_PACKAGE
– PRODUCER_PACKAGE_PATH
– Same for the other

Rename ca.[...].citydata.core.HelloWorldController
– ca.concordia.encs.citydata.core.SanityController 
Rename ca.[...].citydata.core.exceptions.Exceptions
– ca.[...].citydata.core.exceptions.ExceptionBuilder
– Make it a real Singleton?

Rename ca.[...].citydata.producers.StringProducer
– ca.[...].citydata.producers.RandomStringProducer

148/153
Implementation 
Necessary?
– ca.concordia.encs.citydata.core.exceptions.GlobalE
xceptionHandle
– ca.concordia.encs.citydata.producers.ExceptionProd
ucer
– ca.concordia.encs.citydata.producers.RandomNumberP
roducer.setGenerationDelay(int)
– ca.concordia.encs.citydata.operations.AverageOpera
tion.setRoundingMethod(String)
– ca.concordia.encs.citydata.operations.JsonFilterOp
eration.setIsExactlyEqual(Boolean)
– ca.concordia.encs.citydata.operations.JsonReadOper
ation.setPath(String)

149/153
Implementation 
Could we use constructors instead of
setters?
– ca.concordia.encs.citydata.producers.CKAN
MetadataProducer.setDatasetName(String)

150/153
Implementation 
Make code flatter
–For example, in
ca.concordia.encs.citydata.operations.Json
ReadOperation.apply(ArrayList<JsonObject>)
@Override public
ArrayList<JsonObject> apply(ArrayList<JsonObject>
inputs
) {
final
ArrayList<JsonObject>
matchingJsonObjects
=
new
ArrayList<>();
final
String[]
pathParts
=
this
.
path
.split(
"\\."
);
if
(
pathParts
.
length
> 0) {
for
(JsonObject
input
:
inputs
) {
this
.
currentObject
=
input
;
for
(String
key
:
pathParts
) {
try
{// check if key contains [] final
Matcher
arrayAccessMatcher
=
this
.
containsArrayAccess
.matcher(
key
);
// if so, go to key and array position if
(
this
.
currentObject
.isJsonObject() &&
arrayAccessMatcher
.matches()) {
final
String
arrayKey
=
arrayAccessMatcher
.group(1);

151/153
Implementation 
Use consistent coding style, are streams
really better?
–https://stackoverflow.com/questions/44180101/in
-java-what-are-the-advantages-of-streams-over-
loops
– ca.concordia.encs.citydata.operations.
StringFilterOperation.apply(ArrayList<String>)
– ca.concordia.encs.citydata.operations.
JsonReadOperation.apply(ArrayList<JsonObject>)

152/153
Implementation 
Simplify code
– ca.[...].citydata.core.MiddlewareEntity
– ca.[...].citydata.core.
ListOperationsController
– ca.[...].citydata.core.
ListProducerController

Avoid ternary conditional operator

Do not use System.out.println(...);
– ca.concordia.encs.citydata.producers.
FirebaseProducer

153/153
Concretely
https://biratkirat.medium.com/step-8-the-boy-scout-rule-r obert-c-martin-uncle-bob-9ac839778385