Clean Code principles have become a cornerstone of professional developer teams worldwide. But frameworks and languages have evolved, and so have the challenges we’re facing today while crafting modern applications. Single-page apps, extremely DRY code, microservices, excessive functional programming, and reactive flows have all taken their toll on our code quality. Hop aboard this roundtrip of the most damaging Code Smells as of 2023 and fill your toolbox with a load of practical tricks you can immediately apply to your day-to-day work. All in an entertaining show spiced with live-coding moments.
2. victorrentea.ro/training-offer
👋 Hi, I'm Victor Rentea 🇷🇴 PhD(CS)
Java Champion, 18 years of code, 10 years of teaching
Consultant & Trainer at 120+ companies:
❤ Clean Code, Architecture, Unit Tes3ng
🛠 Spring, Hibernate, Reac3ve
⚡ Java Performance, Secure Coding
EducaDve Conference Talks on YouTube
Founder of European So<ware Cra<ers Community (6K members)
🔥 Free 1-hour webinars, a;er work 👉 victorrentea.ro/community
Past events on youtube.com/vrentea
Father of 👧👦, servant of a 🐈, weekend gardener 🌼 VictorRentea.ro
3. 3 VictorRentea.ro
a training by
…does one thing well (SRP)
…reads like a story
...was wri3en by someone who cared
Reading a method body does not surprise you
Any fool can write code that a computer understands,
but few programmers know how to write
Clean Code …
Bjarne Stroustrup
inventor of C++
~ Grady Booch
inventor of UML
~ Michael Feathers
Working Effectively with Legacy Code
Martin Fowler
author of Refactoring
~ Ward Cunningham
inventor of Wiki, eXtreme Programming
COMMUNICATE,
DON’T CODE
code that a human can understand
MOV AX, BX
4. 4 VictorRentea.ro
a training by
International Unit of Measure
for Clean Code ?
wtf/min
Acronyms: What a Terrible Failure, What The Feature
6. VictorRentea.ro
6
Many Clean Code rules became Obvious
üDon't copy-paste code (DRY principle)
üMethod size < 1 screen
üA boulean parameter can violate SRP
üComments are not needed by expressive code
üCode forma>ng (team stylesheet)
üImmutability = ❤
Duh!!
7. VictorRentea.ro
7
FIND 5 DIFFERENCES!
This is code copy-pasted 2 years ago 😨
git blame!
Author left
the team 😞
Ask the
business!
They
forgot 😩
Feature?
(only here must cut it)
Bug?
(forgot to cut it here)
8. VictorRentea.ro
8
Duplicated Code
Is it a bug or a feature?
DRY
Don't Repeat Yourself
becomes horrible when it changes
Code Smells
Today tools can detect
duplicated code
(IDE, Sonar...)
git blame!
Author left
the team 😞
Ask the
business!
They
forgot 😩
9. 9 VictorRentea.ro
a training by
Where did you learn Clean Code from?
• IDE inspec'ons + Sonar/Linters checking code before commit + on CI
• Construc4ve Code Review by Mandatory PR reviewers
• Pair Programming = faster feedback + human interac'on = 🫶
• Books: Clean Code, Refactoring2nd, or Online Catalog: refactoring.guru
• Uncle Bob's Videos: CleanCoders.com (what got me addicted)
• Refactoring Exercises (kata-log.rocks): imitate > explore > +constraints
( x 2 = 😎 )
10. 10 VictorRentea.ro
a training by
#YOLO Developer
# Don't test your code, just ask someone else to do it.
# It works, so why refactor? It's a waste of time! YOLO!
# I don’t always test my code,
but when I do, I do it in production. YOLO!
# Some fix on SO worked but I don't care why! YOLO
git push --no-verify --force -u master YOLO
You Only Live Once !!
12. 12 VictorRentea.ro
a training by
FEAR
833| } // end if (leasePlan = null)
// TODO vrentea 2005-03-21 Temporary hack.
Should be removed befor Prod
if (x!=null && x.y!=null && x.y.z!=null)
// When I wrote this only God and I understood it.
Now, only God knows
12
// not sure if we need this,
but to scared to delete
godMethod(... , boolean param12)
THE DEATH OF YOUR CREATIVITY
13. 13 VictorRentea.ro
a training by
I will cover it with
reliable tests
I will NOT
Ship SH*T
I will not harm
a colleague
A task is NOT DONE unless it's CLEAN and TESTED
15. VictorRentea.ro
15
“If it stinks, change it.”
— Grandma Beck, discussing childrearing philosophy
Code Smells
Instinct tells you that
this code will hurt you later
18. 18 VictorRentea.ro
a training by
Functions Shall be SMALL
SmallerthanaSCREEN!
More complex,
è less lines
The max length should be inversely proportional to the complexity. - Linux coding style guide
≈ 20 lines
... or shorter
22. 22 VictorRentea.ro
a training by
Flat Functions Deeply Nested Func;ons
Most Changes
occur here
Fla2en Code
Huge Cycloma4c/Cogni4ve Complexity
A Sonar metric
23. 23 VictorRentea.ro
a training by
foo bar
§if
- Anemic else {} è flip if🔁 è Guard Clause:
- Superficial if è Split in 2 funcIons è
§for
- è Split Loop è use FP (Stream)
§switch
- ... is alone in a funcIon, with one-line / case
§catch
- è Global ExcepIon Handler
Fla+ening Func0ons
if (param == null) return/throw;
f() {
if (cond) {
foo
} else {
bar
}
}
24. VictorRentea.ro
24
Monster Method God Class Many Parameters
> 20* lines
*Tweak these numbers for your team comfort
Stuff that grew too big
SRP violation
more complex è shorter
Mul@-line Lambda
-> { ...
Code Smells
è Extract Explanatory Method
Extract only if you can
find a good name
depth > 3*
25. VictorRentea.ro
25 - extract from a clean code presentation proudly presented to us by a colleague in a workshop
26. VictorRentea.ro
26
Monster Method God Class Many Parameters
> 200* lines ⛔ > 4* ⛔
*Tweak these numbers for your team comfort
Stuff that grew too big
SRP violation
more complex è shorter
Mul@-line Lambda
-> { ...
è Extract reusable
Parameter Object
Code Smells
è Extract Explanatory Method
è Break method (SRP)
Mixed Layers of AbstracTon
Extract only if you can
find a good name
è Compute one inside
depth > 3*
> 20* lines
29. VictorRentea.ro
29
Data Clumps
data pieces that sIck together
(String, String, Integer) è Address
Tuple4<String, Long, Long, LocalDate> è PricedProduct
(in a dynamic language): array, {} è class
Lightweight data structures
Java: @Data/@Value, record
Kotlin: data class
Scala: case class
C#: record class
TS: class, interface, type
Code Smells
aka "Missing AbstracIon"
30. 30 VictorRentea.ro
a training by
Discover New Classes
Simplify Code
safer
Constraints
Spread Logic
OOP
Break Large En77es
Fewer Parameters
and more expressive
PracIce!🏋
31. VictorRentea.ro
31
Value Objects
è[Small] Immutable
- To change an a*ribute => produce a modified copy
èNo persistent idenFty (PK)
- Lack a "con:nuity of change", unlike En::es
- You cannot say "What part of '3' changed when I set it to '4'?"
èEquals uses all fields (and hashCode)
- Money {10, EUR} equals Money {10, EUR}
* Java Records are good way to implement a VO
32. VictorRentea.ro
32
!!...
int place = player.getPlace() + roll;
if (place !>= 12) {
place -= 12;
}
player.setPlace(place);
!!...
Data Classes
Data structures that contain only data, no behavior
Feature Envy
Logic operaTng heavily on state of another object
Code Smells
Prefer a Rich Model (DDD) over Anemic Structures:
- Logic inside
- Guarding domain constraints
- Null-safe geZers
OOP
Keep behavior next to state
...should go inside that object
player.advance(roll);
33. VictorRentea.ro
34
Map<Long, List<Long>> map
Map<Long, List<Long>> customerIdToOrderIds
void redeemCoupon(Long couponId, Long customerId, String phone)
what do these mean?
😨
Code Smells
redeemCoupon(dto.custId, request.cid, request.phone)
Caller:
Primi0ve Obsession
=code full of primitives without semantics
DeclaraTon:
34. 35 VictorRentea.ro
a training by
void redeemCoupon(Long couponId, Long customerId, String phone)
Map<Long, List<Long>> map
redeemCoupon(CustomerId cust, CouponId cup, PhoneNumber p)
Micro-Types
Map<CustomerId, List<OrderId>> orders
class CustomerId {
private final Long id;
...get/hash/equals
}
@lombok.Value
class CouponId {
Long id;
}
You can fight the by creaIng
PrimiTve Obsession
record PhoneNumber(String value){
String getAreaCode() {..}
}
Tuple3<CustomerId, RegionId, List<OrderId>>
Type-safe semanTcs
➖ early decision
➖ surprising
➖ can eat memory
Host bits of logic instead of a UTl
👍 Consider for central IDs
that you aggregate by,
put in Map<X, or Tuples
or extra-complex logic
36. VictorRentea.ro
37
Functional Programming
Code Smells
= funcQons are first-class ciQzens
I can directly pass behavior: f( -> ...)
instead of f(new Consumer<X>() { void accept(X x) {...} })
We can avoid mutaQng collecQons
newList = list.stream().filter( ->).map( ->).toList();
37. VictorRentea.ro
38
Code Smells
<T> Consumer<T> uncheck(ThrowingConsumer<T> f) {
return x -> {
try {
f.accept(x);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}; // what does this do ??!
}
.forEach(uncheck(fileWriter::write)); // caller
Higher-Order Func@ons are HARD
avoid it in non-pure funcIonal languages (like Java)
38. VictorRentea.ro
39
Functional Programming
FuncQons should be .........
Principles
Objects should be ...................
📽 More in my talk: Pure Func0ons and Immutable Objects
No Side Effects (changes)
Same input è Same output
(adj.)
(adj.)
= Programming without side effects
pure
immutable
42. VictorRentea.ro
44
!// sum up all active orders
int sum = 0;
orders.stream() !// stream is cool 😎
.filter(order -> order.isActive())
.forEach(order -> {
sum += order.getPrice();
});
effectively final ( )
(lambdas cannot change local variables on stack)
!// AtomicInteger sum 😱
sum.incrementAndGet(price);
!// int[] sum = {0}; 🤨
sum[0] += price;
!// turn local into field 🤢
this.sum += price;
Hacks: Move accumulator on Heap
Impera@ve FP
In FP: avoid Side Effects!
Code Smells
In FP: Compute and Return ✅
int sum = orders.stream()
.filter(Order!::isActive)
.mapToInt(Order!::getPrice)
.sum();
43. VictorRentea.ro
45
Impera@ve FP
Code Smells
stream.forEach(e -> ...):void
optional.ifPresent(e -> ...):void
👆 Are code smells if used to accumulate data:
❌ .forEach(e -> map.put(e.id(), e)); è .collect(toMap());
❌ .forEach(e -> adder.increment(e)); è.sum();
❌ .ifPresent(e -> list.add(e)); è .flatMap(OpQonal::stream)
OK to use them for external side effects:
✅ .forEach(m -> mailSender.send(m))
✅ .ifPresent(e -> repo.save(e))
46. VictorRentea.ro
48
Loop
Complex Loop
for (e : list) {
results.add(...)
total += ...
sideEffect(e);
}
Accumulator Loop
var results = new ArrayList();
for (e : list) {
results.add(...)
}
for (e : list) {total += ...}
var total = list.stream()..sum/reduce
for (e : list) sideEffect(e);
Split Loop
Refactoring
var results = list.stream()..collect
Violates SRP gathering data via a loop
Code Smells
🤔
list.forEach(this::sideEffect)
47. VictorRentea.ro
49
for (e : list) {
a(e);
b(e);
}
Split Loop
Refactoring
= List.of(1,2);
for (e : list) a(e);
for (e : list) b(e);
Thoughts? 🤔
😱 Performance Impact?
= Minimal in BE systems👇
measure it : h9ps://github.com/victorrentea/performance-jmh
⚠ Order of opera4ons changes
from: a(1) b(1) a(2) b(2)
into: a(1) a(2) b(1) b(2)
⚠ Mind the flow breakers:
return, throw, break, continue
48. VictorRentea.ro
50
List<Product> f() {
return orders.stream()
.filter(o -> o.getCreationDate().isAfter(now().minusYears(1)))
.flatMap(o -> o.getOrderLines().stream())
.collect(groupingBy(OrderLine!::getProduct,
summingInt(OrderLine!::getItemCount)))
.entrySet()
.stream()
.filter(e -> e.getValue() !>= 10)
.map(Entry!::getKey)
.filter(p -> !p.isDeleted())
.filter(p -> !productRepo.findByHiddenTrue().contains(p))
.collect(toList());
}
Code Smells
è extract explanatory
variables and methods
eg. aber every 3-4 operators
⚠ In Reac?ve chains, this is in fact
the recommended coding style
Excessive Chaining
🥇 single-expression funcIons
fun x(..) = ..
49. VictorRentea.ro
51
@Bean
public Function<Flux<LikeEvent>, Flux<LikedPosts!>> onLikeEvent() {
return flux -> flux
.doOnNext(event -> postLikes.put(event.postId(),event.likes()))
.doOnNext(event -> eventSink.tryEmitNext(event))
.map(LikeEvent!::postId)
.buffer(ofSeconds(1))
.flatMap(ids -> postRepo.findAllById(ids)
.map(Post!::title)
.collectList())
.map(LikedPosts!::new)
.onErrorContinue((x,e) ->log.error(STR."Ignore {x} for {e}"))
.doOnNext(message -> log.info("Sending: " + message));
}
☠ Reac4ve Programming ☠
⚠ In ReacTve chains, this is in fact
the recommended coding style
50. VictorRentea.ro
52
double average = 0;
for (Employee e : employees) {
average += e.getSalary();
}
average = average / employees.size();
1
2
3
4
5
6
Confused Variable
PRO: Don't reassign local variables
sum
sum
Variables should have a single meaning ✅
Code Smells
consider is ≠ 0
sum
The variable name
is lying at this line
double
here it means "sum" Split Variable: create a separate one
52. VictorRentea.ro
54
Code Smells
Dead Code
Not Referenced
Grayed out in IDE: param, variable, method
è Safe Delete using IDE = full-text search
⚠ sIll used by reflecIon, clients of your lib...
- Code only called from tests is NOT grayed out
Commented Code
è delete it 👍
è park it on a branch
è feature flag: if (bf) ...
Not Reachable
- Unused API endpoint è Monitor URLs
- Code branch impossible to reach
... you believe 😱😬
è Coverage/Profiler?
⚠ Monitor enough Ime (eg ≥ 13 months)
è If recent, ask author ASAP!!
6 month later: If it works, don't touch it!TM
- Shrink solu7on to minimum when it works
eg aker a SO rampage 🤠
if (<impossible>) {
lots of code
}
54. VictorRentea.ro
56
Overengineering
(SpeculaIve Generality)
è KISS Principle
Keep It Short & Simple
Ever wrote code an-cipa-ng
a future possible requirement,
or a broader use (eg a library)?
Code Smells
Simplicity is the
ul7mate sophis7ca7on
(Leonardo DaVinci)
Nothing is harder than
finding a simple solu?on
to a complex problem.
Yes!
- a bright developer
If that future didn't happen,
did you simplify that code?
😞
55. VictorRentea.ro
57
Java Language Weaknesses
OpTonal<> @Nullable
null
only throw runTme excepTons
Checked Excep@ons
ImmutableList(Guava)
CollecTons.unmodifiableList() List.of (Java 11) .toList() (Java 17)
Mutable Collec@ons
Code Smells Java got its name by people that had no idea if they were awake or asleep due to coffee abuse.
@Data (Lombok)
geZer, seZer, hash/equals, toString
Boilerplate Code
records (Java 17)
56. VictorRentea.ro
58
Code Smells
most common today (as of 2023)
Monster Method
God Class
Many Parameters
Heavy Lambda
Flags Data Classes
Data Clumps
PrimiFve Obsession
Feature Envy
Overengineering
Dead Code
ImperaFve FP
Complex Loop
Accumulators
Stream Wreck
Mutable Data
Confused Variable
58. VictorRentea.ro
60
Schedule right now in your team calendar
your first Ensemble Programming Session,
eg. next Fri, from 16:00 – 18:00
(first hour = office 1me = mandatory)
Ø on a ProducIon code snippet
Ø or a Coding Kata ✔, like:
eg hGps://github.com/victorrentea/kata-trivia-java
eg hGps://kata-log.rocks/refactoring
More about me è victorrentea.ro
The game to play:
• What could someone not like about this code? (idenIfy code smells)
• Ways to fix? (pracIce refactoring moves)
• Tradeoffs? (design principles)
• When NOT to fix? (team reality)
59. 61 VictorRentea.ro
a training by
Refactoring Moves
Extract
InliNe
Method 🌟
Local Variable 🌟
Parameter; also: Change Method Signature
Constant
Interface
Superclass; also: Pull Up to super class
Introduce Parameter Object (eg. Interval)
Move Method / Convert to Instance... (in a param or dependency)
Quick Fix for 300+ inspec'ons (Alt-ENTER)
Rename 👑 ShiR-F6
x
IntelliJ Shortcut:
Win/Lin: Ctrl + Alt + <KEY>
Mac: ⌘ + ⌥ + <KEY>
60. VictorRentea.ro
Stay into
The Light
Join me: victorrentea.ro/community
sourcemaking.com/refactoring
Chapter 1-11 + 17 Chapter 2+3 + links For Java beginners Coding Katas
kata-log.rocks/refactoring
cleancoders.com
videos that inspire 💗 for code
refactoring.guru
Summary arDcle: link
Clean Code - Reading Guide
A Ime-efficient way to get up to speed with Clean Code and Refactoring
61. VictorRentea.ro
63
Clean Code, Two Decades Later
victor.rentea@gmail.com ♦ ♦ @victorrentea ♦ VictorRentea.ro
Passionate about Clean Code?
Join our community: