Writing maintainable code is not something that is easy to do. Not only is a pretty subjective, a lot of the techniques like Clean Code, SOLID and such can be misinterpreted resulting in unconstructive dogma. I've made it my career's goal to find a good balance between all those different patterns, principles, practices and guidelines.
In this talk, I'd like to share my own ideas on what it takes to write maintainable code. I'll talk about architecture, class design, automated testing and any consequences on your development process. If you care about your code, be there with me.
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
A lab around the principles and practices for writing maintainable code
1. Dennis Doomen | @ddoomen | Aviva Solutions | The Continuous Improver
2.
3.
4.
5.
6.
7. Readable
What does it try do and how?
Testable
Is it doing what it is supposed to do?
Isolated
Can I do this without affecting anything else?
Traceable
Why did we do this?
Discoverable
Where is this done?
12. Application
Functional Slice Functional Slice Functional Slice Functional Slice
Composition
Architectural boundaries
protected by PR expert
review
Each component
owns/exposes its
reference data
Does not
depend on
anything
except for
curated
shared APIs
Owns
schema and
storage
(may use
shared
database
abstraction)
DRY within
component,
not outside
May use an
internal
container
Does not
touch other
components
’ data
directly
Shared Services
Connects the
components and
serves as anti-
corruption layer
Only services you
really think should
not be duplicated
Designed according to
Dependency Inversion Principle
Covers
front-end
and back-
end
Component
is the scope
of unit &
integration
tests
Align folder
structure
with
boundaries.
Defines the
contracts
for the host
as well as
any services
it exposes
13. Functional Folders
“Unit” of (integration)
testing
DRY within boundaries
Can be used to clarify
“public” parts
Only unit tested for
specific reasons
Role-based interface
name
Apply Rule of Three.
16. …over
generic
interfaces
…to hide the
“internals” of a
collection of steps.
…and favour
composition
over
inheritance
Avoid base-
classes for
reuse…
Prefer small
and focused
interfaces…
Keep
relationships
uni-directional
Use the right
naming
conventions for
Design Patterns
Use first-class
collections and
primitives…
17.
18.
19. Long method
Not
immediately
clear what it
does
Magic
character
Deeply
nested “ifs”
Single entry,
single exit
Static mutable state
Multiple dots
Magic keys
No idea what to do
if this fails
Encapsulates
concerns
Multiple
levels of
indention
Use of else
Not using a
first-class
collection
Not using a first-
class collection
Not using a first
class type.
20.
21. Clear
algorithm
(Still) static mutable
state
Name
captures the
purpose
Same level of
abstraction
Methods
ordered by
execution
(a.k.a. reads
like a book)
Separation
of main data
and the
context
Encapsulation of
cyclic reference
detection
Static mutable
state is kept to a
minimum
Confusing
Boolean is
replaced
Clearer
instructions on
what to do if the
impossible
happens.
Encapsulation of
what depth
means
27. Design class
responsibilities
Write first unit
test
Generate stubs
Fail for the right
reason
Implement
real deal
Ensure test
succeeds
Identify
alternative
scenarios
Repeat twice
Refactor
38. // SMELL a smell code description
// REFACTOR an idea for refactoring
// NOTE a comment explaining a part of the code
39.
40.
41.
42.
43.
44.
45. Quality
Definition of
Done
Work
Breakdown
Decision Log
Small PRs
Focused
Commits
Continuous
Refactoring
Review Speed
Review by
non-team
member
Productivity
Natural Cause
of Refactoring
Review
Thoroughness
Traceability
Clean Source
Control
History
Knowledge
Sharing
Tech Meetings
Red Hot
Developer
Readable
Code
Clear (Unit)
Tests
Estimates
Opportunities
for swarming
Internal Blog