Consequences and Principles of Software Quality v1.0
yanngaelgueheneuc
60 views
153 slides
Mar 06, 2025
Slide 1 of 153
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
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...
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 quality, especially the concept of Quality Without a Name. It then discusses many principles, their usefulness, and their positive consequences on software quality. Some of these principles are well-known in object-oriented programming while many others are taken from the book 97 Programmers. They include: abstraction, encapsulation, inheritance, types, polymorphism, SOLID, GRASP, YAGNI, KISS, DRY, Do Not Reinvent the Wheel, Law of Demeter, Beware of Assumptions, Deletable Code, coding with reason, and functional programming. They pertain to dependencies, domains, and tools. Concrete application on a real-world software systems, with examples and discussions.
(In details: Beautify is Simplicity, The Boy Scout Rule, You Gotta Care About the Code, The Longevity of Interim Solutions, Beware the Share, Encapsulate Behaviour not Just State, Single Responsibility Principle, WET Dilutes Performance Bottlenecks, 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, 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)
Size: 2.5 MB
Language: en
Added: Mar 06, 2025
Slides: 153 pages
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
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/
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
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.”
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
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;
}
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
);
}
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
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