You are on page 1of 24

GOOD CODE,

BAD CODE
by Tom Long
TABLE OF CONTENTS

01 03
Code quality Other engineers and code
contracts

02 04
Layers of Errors
abstraction
01
CODE QUALITY
High-quality Low-quality
code code

Original software Not fully met due to


Fully met
requirements unhandled edge cases

Minor additional Major re-engineering


A change in requirements
work required and refactoring required

System enters undefined


System recovers
An error occurs state. Data potentially
or fails gracefully
corrupted
Handled despite System enters
not having been An unforeseen scenario undefined state. Data
explicitly foreseen potentially corrupted
System enters undefined
System stays in safe
System under attack state. Potentially
state. Not compromised.
compromised.
The software is reliable,
The software is unreliable,
easy to maintain,
difficult to maintain, and full
and contains very few
of bugs.
bugs.
GOALS TO ACHIEVE WHEN
WRITING CODE

It should keep
It should work
working

It should be
adaptive to
It should not reinvent
changing
the wheel
requirements
The pillars of code quality

Pillar 1 Pillar 2 Pillar 3


Make code readable Avoid surprises Make code hard to
misuse

Pillar 4 Pillar 5 Pillar 6


Make code modular Make code reusable Make code testable
and generalizable and test it properly
02
LAYERS OF ABSTRACTION
Why create layers of
abstraction?
If we do a good job of recursively breaking
a problem down into subproblems and creating
layers of abstraction, then no individual piece of
code will ever seem particularly complicated,
because it will be dealing with just a few easily
comprehended concepts at a time. This should be
our aim when solving a problem as a software
engineer.
LAYERS OF CODE

Functions
The code inside each function
should ideally read like a single,
short, well-written sentence.

API
Classes
application programming interface ● Number of lines
● Cohesion
● Separation of concerns

Interfaces
Use an interface where it provides an
appreciable benefit, but not to do it just
for the sake of doing it.
When layers get too thin
It’s hard to come up with a single rule or
piece of advice that will tell us definitively
whether a layer is too thick, as it will often
depend on the nature of the real-world problem
that we’re solving. The best advice is to use our
judgment and to think carefully about whether the
layers we have created will ensure that the code is
readable, reusable, generalizable, modular, and
testable.
03
OTHER ENGINEERS AND CODE
CONTRACTS
YOUR CODE AND OTHER
ENGINEERS’ CODE
Understanding Conflicts Your memory
Things that are obvious to you Other engineers will inadvertently try In time, you will forget
are not obvious to others. to break your code. about your own code.
How will others figure
out how to use your
code?
 Look at the names of things (functions, classes,
enums etc.).
 Look at the data types of things (function and
constructor parameter types and return value types).
 Read any documentation or function-/class-level
comments.
 Come and ask you in person, or over chat/email
PROGRAMMING BY CONTRACT

Under this philosophy, engineers think about the interactions


between different pieces of code as though they were a contract:
callers are required to meet certain obligations, and in return, the
code being called will return a desired value or modify some state.
Nothing should be unclear or a surprise because everything should
be defined in this contract.
SMALL PRINT IN CONTRACTS

Everyone knows that they really should read the small print of
every contract they enter, but most people don’t. Using small print
is, therefore, not a reliable way to convey the contract of a piece of
code. Relying too much on small print is likely to produce fragile
code that is too easy to misuse, and that causes surprises
ALTERNATIVE METHODS

Checks Assertions
These are additional logic that When the code is compiled in
check that a code’s contract has a development mode, or when
been adhered to tests are run a loud error or
exception will be thrown if a
condition is broken.

The key difference between assertions and checks is that assertions are
normally compiled out once the code is built for release, meaning no loud failure
will happen when the code is being used in the wild.
04
ERRORS
There are broadly two types of error:

Those that a system can recover from

Those that a system cannot recover from


Failure
VS
Robustness
.
WAYS OF SIGNALING ERRORS

EXPLICIT IMPLICIT
 Checked exceptions • Unchecked exceptions
 Nullable return type • Promise of future
 Result return type • Returning a magic value
 Outcome return type
CODE THAT CAUSES A COMPILER WARNING
class UserInfo {
private final String realName;
private final String displayName;

UserInfo(String realName, String


displayName) {
this.realName = realName;
this.displayName = displayName;
}

String getRealName() {
return realName;
}

String getDisplayName() {
return realName;
}
}
“High-quality code helps create high-quality
software and identifying what we’re
fundamentally trying to achieve helps us
judge code quality more objectively.”

—TOM LONG
THANKS!
Do you have any questions?
barskyiandriy@gmail.com
+380 67 584 255 4

CREDITS: This presentation template was created by


Slidesgo, incluiding icons by Flaticon, and
infographics & images by Freepik.
Made by Andrii Barskyi

You might also like