of coding 10 years of training & consul2ng at 130+ companies: ❤ Refactoring, Architecture, Unit Tes4ng 🛠 Spring, Hibernate, Reac4ve ⚡ Java Performance & Secure Coding Lead of European So8ware Cra8ers (6.900)👉 victorrentea.ro/community Mee4ng online every month from 1700 CET Channel: YouTube.com/vrentea Life += 👰 + 👧 + 🙇 + 🐈 @victorrentea 🇷🇴 past events 👉 victorrentea.ro/catalog
of code §Method depth < .... indenta3on levels §File length < ....... lines §Feeling about //comments in code: ....... §When we see duplicated code: ...... §To work with collec3ons, instead of a for loop, use: ....... §Feeling about seAers: .... §FormaBng: for ( var e : list ) 🥾Kick-off Quiz 20* 3 200 not required by good code extract a shared method .map / .filter avoid - prefer immutable objects! auto-format by IDE
- Increase complexity: godMethod(a, b, c, d, false, true, -1, (x,y) -> ...) - Increase coupling: method uses 10 dependencies - Abuse inheritance: class Green extends Color extends Base extends ... - Accidental Coupling☣ of unrelated flows with similar code Only unify code that changes together 👉 ask PO The rule of 3: a li<le repe00on is beKer than the wrong abstrac>ons
lines⛔ > 200 lines⛔ > 4 ⛔ Bloaters The more complex, the shorter depth > 3⛔ Complex Lambda -> { ... è Extract reusable Parameter Object Code Smells è Split in Flow A / Flow B è Split method by SRP è Split in High / Low level Extract if you can find a good name The magic number 7±2 How many things the human brain can keep in working memory (wiki) max 7 ideas/method max 7 methods/class Same Level of AbstracKon (SLAb) also: Generics<A,B,U,S,E>
classes to group data that s4cks together int, int è Interval Tuple4<String, Long, Long, Date> è PricedProduct Lightweight structures Java: @Data/@Value, record Kotlin: data class Scala: case class C#: record class TS: class, interface, type Code Smells aka: {"amount":10, "cur": "EUR"} è Money{amount,currency} js, ts, php, py.. Interval(start,end) ArrayGeddon What fields I receive?😱 IDE Refactoring = unsafe! Missing Type Missing Abstrac7on Tangled Tuples
Shrink En==es carModel.years:Interval Less Parameters in(x, Interval) Interval(start,end) Guard Constraints start<end Host Bits of Behavior interval.intersects(..) If Objects, not only a Data Structure
int place = player.getPlace() + roll; if (place !>= 12) { place -= 12; } player.setPlace(place); !!... Data Classes Anemic class with fields, but no behavior Feature Envy Code Smells Rich Objects with logic & constraints help simplify core logic Keep behavior next to state (OOP) player.advance(roll);
race bugs Immutable💚 Objects Code Smells in complex flows = 💀 hard to track changes Fragmented Immutable if designed too large: Memory Churn if cloned repeatedly: Cumbersome if mixed with an ORM: https://gist.github.com/victorrentea/67f923dca33c7e737c867f60c727c604 list5000.fold(listOf<Int>()) { list, e -> list + e } immutable
employees) { avg += e.getSalary(); } log.debug("Average:" + avg); avg = avg / employees.size(); 1 2 3 4 5 6 sum Code Smells ≠ 0 sum Here avg means "sum" This code lies to you! Don't reassign local variables Recycled variable Confused Variable sum Mind IDE warnings Define a new var final const val IntelliJ can automa6cally add final everywhere possible: SO Error Prone fails compila6on unless it's annotated with @Var
results.add(...) total += ... sideEffect(e); } var results = new ArrayList(); for (e : list) { results.add(...) } for (e : list) {total += ...} var total = list.stream()….sum()/reduce for (e : list) sideEffect(e); list.forEach(e -> sideEffect(e)); Split Loop var results = list.stream()….collect(… doing unrelated things Accumulator Loop gathering data via a loop Code Smells 🤔 ⚠ no performance hit for typical BE ⚠ order of side-effects can maker
new Dec(prev?.price || 0).gte(e.price || 0) ? prev : e, undefined); ✅ Prep the collec7on using .filter() .map() and keep reduce trivial const maxByPrice = (a, b) => new Dec(a.price).gte(b.price) ? a : b; return list.filter(({price}) => !!price).reduce(maxByPrice); Reduce Rodeo ✅ Use specialized collectors .sum() .toList() .max() .average() Use reduce for: a) unusual accumula7on b) compute mul7ple results eg max+min in a single pass c) performance (measured) d) JS/TS, kept simple⚠
-> 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)); } ☠ Reac%ve Programming ☠ Chaining is mandatory Reac=ve Programming is NOT clean!* Goodbye variables and excep4ons. And learn 100+ operators. * there are some safe coding best pracIces, that I teach in my ReacIve Programming workshop.
not FP Try<String> inputTry = process(data); if (inputTry.isFailure()) { return Try.failure(inputTry.getCause()); } log.info("X"); Try<Long> idTry = inputTry.flatMap(this::save); if (idTry.isSuccess()) { audit(data.input()); } return idTry; } Double-Edged Return Try<String> process(Data data) { if (data.name().isBlank()) return Try.failure(new AnException("…")); ... return Try.success(result); } Just like checked excep6ons(🤮) throws AnException Try<Long> tryingFP(Data data) { return process(data) .onSuccess(e -> log.info("X")) .flatMap(repo::save) .andThen(id -> audit(data)); } // feeling zmart? 😎🤓 Long boring(Data data) { String r = process(data); log.info("X"); saveToDb(r); audit(data); } Pollutes caller throw new ... Use it to: - Return a sealed type carrying the business outcome of an ac6on - Collect all failed items in a list Java: vavr lib Kotlin: Result Caught in transla4on ... return result; }
{ return process(data) // 😱 .onSuccess(e -> log.info("X")) .flatMap(repo::save) .andThen(id -> audit(data)); } Use it to: - Return a sealed type carrying the business outcome of an ac6on - Collect all failed items in a list Long boring(Data data) { String r = process(data); log.info("X"); saveToDb(r); audit(data); } String process(Data data) { if (data.name().isBlank()) throw new Ex("…"); ... return result; } Try<String> process(Data data) { if (data.name().isBlank()) return Try.failure(new Ex("…")); ... return Try.success(result); } throws Checked Caught in Transi7on nostalgic🥺 for Go,Scala,Kotlin,JS In FP don't throw pollutes caller
param, variable, method, unused in IDE è Delete [with IDE] 🥳 Unreachable Code How to find code never called in produc4on? èMonitor API calls in prod ègit blame: if recent🤞 > Ask author now! (6 month later: It works? Don't touch it! ™ ) è log.warn("🙏Delete after Sep'24 if this is in not in log of the last 6 months*") if (impossible) { lots of code* lots of code } ... you think 😬
Simple (KISS) Code Smells Yes! - a bright developer Ever wrote code an+cipa+ng a future requirement, or a hoping a wider use (eg. a shared library)? But it's fun!! Pet-project! Nothing is more difficult than finding a simple solu4on to a complex problem. Simplicity is the ul=mate sophis=ca=on. -- Leonardo DaVinci don't buildwalls in front of probable change,
Checked ExcepJons Code Smells Java got its name by people that had no idea if they were awake or asleep due to coffee abuse. records [17] Lombok Boilerplate Code geDer, seDer, hashCode/equals, toString No Extension FuncJons U6ls No Default / Named Params Overloading Builders ImmutableList (Guava) unmodifiableList(list) List.of() [11] .toList() [17] Mutable CollecJons list.add(e)
Switches Monster Method God Class Many Parameters Heavy Lambda Flags OOP Middle Man Data Classes Missing Abstrac7on Primi7ve Obsession Feature Envy Overengineering Dead Code Impera7ve FP Complex Loop Accumulators FP Chainsaw Mutable Data Temporary Field Confused Variable FP Bad Names Reduce Rodeo
17 Chapter 2+3 +links For newbies Coding Katas kata-log.rocks/refactoring cleancoders.com refactoring.guru Summary ar4cle: link ↑ Seed Passion for Clean Code Clean Code - Reading Guide A 4me-efficient way to get up to speed with Clean Code and Refactoring 2008 2018
on - craUing good quality code, - simple design - robust unit tests - con'nuous growth & teamwork Mee'ng every month a7er hours (1700 CET) online on Zoom + YouTube Live Free Forever! I was VictorRentea.ro