The document discusses principles and best practices for writing high-quality code, including keeping code simple, avoiding duplication, using object-oriented design principles like SOLID, giving variables and methods meaningful names, properly structuring classes, methods and variables, and applying principles of encapsulation and inheritance. It emphasizes that code quality is important to reduce development costs and improve productivity.
2. Motivation!
Why should we always improve our code?!
Why code that just works is not enough? !
3. Code Quality Control Law !
Improving code quality reduces development costs!
Why?!
Developer average productivity is!
10-50 lines per day!
Writing code – 10%!
Reading code, testing, debugging – 90%!
!
4. Productivity vs Time!
Productivity decreases with entropy increasing!
Time
Productivity
5. Software Development Paradox!
1. Low-quality code slows down development!
2. Devs reduce code quality to meet a deadline!
Paradox: “Devs have no time to work rapidly”!
The clue: #2 is wrong!
Impossible to successfully meet a deadline messing up the code!
Continuously maintain high code quality!
6. The Boy Scout Rule!
It’s not enough to write code well - prevent “rotting”!
!
“Leave the campground cleaner than you found it”!
!
8. Keep Code Simple!
KISS (Keep It Simple, Stupid)!
Simplicity is a key goal!
Components should be integrated tightly or weakly (?)!
Try to reduce influence between components!
Follow “Divide and Conquer” principle!
!
9. Division of Responsibilities!
• System → packages!
• Package → classes!
• Class → methods!
• Method → control statements!
Low coupling on each level!
10. Use Patterns!
Don’t reinvent the square wheel!
• Reduce complexity!
• Less mistakes!
• Simplify communication!
!
Examples: Repository, Factory, Adapter, Strategy, Observer!
11. No Unnecessary Code!
YAGNI (You Aren’t Gonna Need It)!
Implement things only when you really need them!
!
12. Avoid Duplication!
DRY (Don’t Repeat Yourself)!
!
“Every piece of knowledge must have a single, unambiguous,
authoritative representation within a system.”!
!
15. Open-Closed Principle!
Class should be opened to extension and closed for modification!
GraphicEditor
+drawShape
Shape
+draw
Rectangle
+draw
Circle
+draw
violation
+drawCircle
+drawRectangle
+drawTriangle
Triangle
+draw
16. Liskov Substitution Principle!
Objects implementing an interface can be used interchangeably!
!
GraphicEditor
+resize(IResizable)
IResizable
+bounds
+resize
Shape
+bounds
+resize
Image
+bounds
+resize
violation
resize(IResizable obj) {
if(obj is TextBlock)
return;
obj.resize();
}
TextBlock
+bounds
+resize
17. Interface Segregation Principle!
Clients should not depend on interfaces they don’t use!
!
Shape
+resize
+move
+select
IResizable
+resize
violation
IGraphic
+resize
+move
+select
ISelectable
+select
IMovable
+move
18. Dependency Inversion Principle!
Abstractions should not depend on details!
GraphicEditor
+logger: ILogger
ILogger
violation
GraphicEditor() {
logger = new FileLogger();
}
EmailLogger FileLogger
SystemLogger
20. Naming Principles!
Name should show your intention!
int
d;
//
elapsed
time
Is it good enough?!
Which name is better?!
int
elapsedTimeInDays;
int
daysSinceCreation;
!
!
21. Naming Principles!
What does the following code do?!
function
getItems(items)
{
var
list1
=
[];
for(var
i
=
0;
i
<
items.length;
i++)
{
var
x
=
items[i];
if(items[i]["Field"]
===
12)
{
list1.push(x);
}
}
return
list1;
}
!
22. Naming Principles!
What is about this one?!
function
getMarkedCells(cells)
{
var
result
=
[];
for(var
i
=
0;
i
<
cells.length;
i++)
{
var
cell
=
cells[i];
if(cell.Status
===
CellStatus.Marked)
{
result.push(cell);
}
}
return
result;
}
23. Naming Principles!
No implementation details!
HashSet<Account>
accountHashSet;
Is it good enough? Why?!
What if we decide to use List?!
Which name is better?!
HashSet<Account>
accounts;
!
24. Naming Principles!
Choose easy-to-pronounce name!
class
Cstmr
{
private
int
prsnid;
private
DateTime
genTstmp;
private
DateTime
modTstmp;
}
class
Customer
{
private
int
personId;
private
DateTime
generationTimestamp;
private
DateTime
modificationTimestamp;
}
!
25. Naming Principles!
• Single word for concept (avoid synonyms)!
fetch / retrieve / get controller / manager / driver!
• Names from patterns and algos!
RenderStrategy, JobQueue!
• Names from user stories!
Credit, Debit, Asset!
• Antonyms!
begin / end first / last min / max next / previous!
old / new opened / closed source / target up / down!
!
26. Naming Convention!
The most important is to follow to the convention!
Example:!
• CONSTANT
• ClassName
• localVariable
• _privateMember
27. Name Length!
What is the optimal length for a name?!
x → maximumNumberOfPointsInEachLine
short / long name pros & cons!
short names – lacks of information!
long names – harder to read and write!
!
10-16 characters is the optimal length!
if < 10 ask: is meaningful enough?!
if > 16 ask: isn’t verbose?!
29. Loop Variable Names!
• Variable used outside of the loop
int
recordCount
=
0;
while(moreRecords())
{
data[recordCount]
=
getNextRecord();
recordCount++;
}
//
…
recordCount
is
accessed
here
!
35. Declaration!
• Declare all variables!
• Turn off implicit variable declaration!
JavaScript:
“use
strict”
VB:
Option
Explicit
On
php:
error_reporting(E_STRICT);
!
36. Initialization!
Initialize variables at declaration!
var
total
=
0;
or at first occurrence in code!
Dim
total
As
Double
'
another
code
total
=
0.0
'
code
using
total
!
37. Lifetime and Scope!
Variables lifetime and scope should be minimal!
Why?!
Does code follow the principle?!
function
summarizeData()
{
var
oldData
=
getOldData();
var
totalOldData
=
summarize(oldData);
printData(totalOldData);
saveData(totalOldData);
var
newData
=
getNewData();
var
totalNewData
=
summarize(newData);
printData(totalNewData);
saveData(totalNewData);
}
38. Lifetime and Scope!
Code might look like!
function
summarizeData()
{
var
oldData
=
getOldData();
var
newData
=
getNewData();
var
totalOldData
=
summarize(oldData);
var
totalNewData
=
summarize(newData);
printData(totalOldData);
printData(totalNewData);
saveData(totalOldData);
saveData(totalNewData);
}
!
!
39. Lifetime and Scope!
• Single purpose principle!
int
value
=
getValue();
int
result
=
calcResult(value);
//
some
code
value
=
val1;
val1
=
val2;
val2
=
value;
• Avoid global variables!
• Prefer the most restrictive access level!
private → public!
var
swap
=
val1;
val1
=
val2;
val2
=
swap;
→
42. Principles!
Method should be small!
How many lines?!
not more than 20 lines and better even less!
Example:!
public
ReportContainer
BuildReport(IList<Order>
orders)
{
var
report
=
new
ReportContainer();
report.Lines
=
BuildReportLines(orders);
report.Total
=
BuildReportTotal(report.Lines);
return
report;
}
!
43. Principles!
Method should have!
• blocks (if, else, for, while) 1-3 lines long!
• indent level not more than 2!
private
IList<ReportLine>
BuildReportLines(IList<Order>
orders)
{
IList<ReportLine>
result
=
new
List<ReportLine>();
foreach
(Order
order
in
orders)
{
ReportLine
reportLine
=
BuildReportLine(order);
if
(reportLine.IsVisible)
result.Add(reportLine);
}
return
result;
}
!
44. Principles!
Single Thing Rule!
Function should do one thing, do it only, and do it well!
Examples:!
• BuildReport – makes report!
• BuildReportLines – prepares report lines!
• BuildReportTotal – summarizes report!
Criteria: function cannot be divided into sections!
46. Principles!
Better!
private
Report
BuildReport(Order
order)
{
var
result
=
new
Report();
result.Header
=
BuildReportHeader(order.Title);
result.Body
=
ReportHelper.TextToHtml(order.Description);
result.Footer
=
BuildReportFooter();
return
result;
}
Much Better!
private
Report
BuildReport(Order
order)
{
var
result
=
new
Report();
result.Header
=
BuildReportHeader(order.Title);
result.Body
=
BuildReportBody(order.Description);
result.Footer
=
BuildReportFooter();
return
result;
}
!
47. Principles!
Command-Query Separation (CQS)!
Method performs an action OR returns data!
Does code follow the principle?!
if
(SetAttribute("username",
"Admin"))
{
//
code
}
How to fix?
if
(HasAttribute("username"))
{
SetAttribute("username",
"Admin");
//
code
}
48. Order!
The Stepdown Rule!
Read code from top to bottom (by abstraction levels)!
public
Report
BuildReport(IList<Order>
orders)
{
//
uses
BuildReportLines
and
BuildReportTotal
}
private
Report
BuildReportLines(IList<Order>
orders)
{
//
uses
BuildReportLine
}
private
Report
BuildReportLine(IList<Order>
orders)
{
//
code
}
private
Report
BuildReportTotal(IList<Order>
orders)
{
//
code
}
49. Arguments!
How many parameters should a method have?!
• Ideally 0!
• Avoid methods with more than 2 arguments!
Why?!
• Harder to read (remember passing values)!
• Increase coupling!
• Harder to test (combinatorial growth of variants)!
50. Arguments!
Avoid boolean arguments!
Why?!
• Usually can be divided into two sections!
• Hard to understand a purpose of an argument!
BuildReportLine(order,
true);
private
Report
BuildReportLine(Order
order,
bool
isMarked)
{
if
(isMarked)
{
//
build
marked
report
line
}
else
{
//
build
simple
report
line
}
}
52. Purposes!
• Simplify code
• Improve readability!
• Limit the scope of changes!
• Hide implementation details (encapsulation)!
• Allow to prepare generic solution (abstraction)!
• Eliminate duplication (delegation & inheritance)!
• Provide extensibility (inheritance)!
• Allow to work with real domain entities!
53. Principles!
• Encapsulation!
Hide as much as possible!
• Compactness!
Reduce class responsibilities (SRP)!
• Abstraction!
Program to an interface, not an implementation!
• High Cohesion!
Fields and methods should belong together!
54. Interface!
Methods should be on the same abstraction level!
Does code follow the principle? Why?!
class
Program
{
public
void
InitializeCommandStack();
public
void
PushCommand(Command
command);
public
Command
PopCommand();
public
void
ShutdownCommandStack();
public
void
InitializeReportFormatting();
public
void
FormatReport(Report
report);
public
void
PrintReport(Report
report);
public
void
InitializeGlobalData();
public
void
ShutdownGlobalData();
}
55. Interface!
Programming over semantic!
• Programming – can be checked by compiler!
Amount of arguments!
Argument types!
• Semantic – cannot be checked by compiler!
Method A should be called after method B!
Method A throws exception when argument is not initialized!
!
56. Encapsulation!
• Not public should be private !
private → public!
• No direct access to fields!
private
decimal
_x
=
0;
public
decimal
X
{
get
{
return
_x;
}
set
{
_x
=
value;
}
}
• No assumptions about class clients!
//
param2
should
be
>
0,
in
other
case
method
fails
public
int
Method(int
param1,
int
param2)
!
57. Inheritance!
Should be able to say that child ‘is’ parent!
Circle is Shape HtmlParser is Parser!
Does code follow the principle?!
class
Customer
{
public
int
ID
{
get;
set;
}
public
string
Name
{
get;
set;
}
}
class
Bank:
Customer
{
public
string
CorrespondentAccount
{
get;
set;
}
}
Depends on whether a bank can be a customer or not!
!
58. Inheritance!
Move common methods to base class!
abstract
class
Stream
{
public
void
Write(byte[]
bytes);
public
byte[]
Read();
}
class
SecureStream:
Stream
{
public
void
Init(StreamConfig
config);
}
!
59. Inheritance!
Avoid following situations:!
• Parent class has only one child!
• Child overrides method leaving it empty!
• Too many hierarchy levels!
better not more than 3 levels!
61. Commenting Principles!
• Comments do not redress bad code!
clean code much better than bad code with comments!
• Do not comment tricky code – rewrite it!
tricky code = bad code!
• Try to explain with code!
//
is
employee
eligible
for
bonus
if
((employee.Schedule
==
HOURLY_SCHEDULE)
&&
employee.Age
>
65)
{
…
}
!
Better!
if
(employee.IsEligibleForBonus())
{
…
}
62. Commenting Principles!
Comment says “why” not “how”!
• How!
//
if
flag
equals
to
zero
if
(accountFlag
==
0)
• Why!
//
if
new
account
created
if
(accountFlag
==
0)
• Code instead of comment!
if
(accountType
==
AccountType.NewAccount)
63. Bad Comments!
• Redundant comments!
obvious and repeating code!
//
closing
connection
Connection.Close();
• Commented code!
remove unnecessary code, VCS is for the rescue
InputStream
response
=
new
InputStream();
response.SetBody(formatter.ResultStream,
formatter.BytesCount);
//InputStream
resultStream
=
formatter.ResultStream;
//StreamReader
reader
=
new
StreamReader(resultStream);
//response.SetContent(reader.Read(formatter.BytesCount));
64. Good Comments!
• Legal comments!
license, copyright, author!
• Explanations!
provide info about inevitable tricks
//
NOTE:
postpone
execution
to
refresh
UI
setTimeout(function()
{
//
code
});
• TODOs!
notes for future to avoid “broken windows”
//
TODO:
get
rid
of
tricky
workaround
function
badFunction()
{
//
tricky
workaround
here
});
66. References!
• Steve McConnell Code Complete!
• Robert C. Martin Clean Code!
• Hunt A., Thomas D. The Pragmatic Programmer !
• Craig Larman Applying UML and Patterns!
• Kent Beck Implementation Patterns!
• Eric Evans Domain-Driven Design!