### Excessive Namespace Referrals in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Shows an example of a Clojure namespace explicitly referring to a large number of Vars from another namespace, or using `:refer :all`. This practice pollutes the namespace, increases the risk of name collisions, and obscures the origin of function calls. The example is in Clojure. ```clojure (ns my-app.core (:require [my-lib.utils :refer [this that the other more moar]])) ; <--- Long list of referred symbols ``` -------------------------------- ### Thread Ignorance in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Demonstrates 'Thread Ignorance' by showing a scenario where Clojure's threading macros (`->`, `->>`) could be used for clearer data flow but are avoided. The example uses sequential `let` bindings instead. ```clojure (defn transform [xs] (let [step1 (map inc xs) step2 (filter even? step1) step3 (reduce + step2)] step3)) (transform [1 2 3 4]) ``` -------------------------------- ### Clojure - Premature Optimization with Retry Macro Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Demonstrates the 'Premature Optimization' code smell in Clojure using a `with-retry` macro. This example shows unnecessary retries without addressing potential root causes of failure, leading to complexity. ```clojure (defmacro with-retry [[attempts timeout] & body] `(loop [n# ~attempts] (let [[e# result#] (try [nil (do ~@body)] (catch Throwable e# [e# nil]))] (cond (nil? e#) result# (> n# 0) (do (Thread/sleep ~timeout) (recur (dec n#))) :else (throw (new Exception "all attempts exhausted" e#)))))) (with-retry [3 2000] (get-file-from-network "/path/to/file.txt")) ``` -------------------------------- ### Trivial Lambda Usage in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Highlights the excessive use of anonymous functions (lambdas) instead of named functions, reducing clarity and reusability. This example demonstrates a simple `map` operation with a lambda. ```clojure ;; Example from source (map #(f %) xs) ``` ```clojure (defn square [x] (* x x)) (def numbers [1 2 3 4]) (println (map #(square %) numbers)) ``` -------------------------------- ### Overengineering Asynchronous Tasks with core.async in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the misuse of clojure.core.async for simple asynchronous tasks that only involve a single value or one-time response. Using channels for such cases introduces unnecessary complexity and overhead compared to Promises/Deferreds or callbacks. This example is in Clojure. ```clojure (ns my-app.async-smell (:require [clojure.core.async :as a])) (defn fetch-single-result-smelly [url] (let [ch (a/chan)] (a/go (a/>! ch (http/get url))) ch)) ``` -------------------------------- ### Overabstracted Composition in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates excessive use of function composition and partial application leading to overly abstract and less readable code. This example shows how deep nesting of composed functions can obscure data flow. ```clojure ;; Example from source (def m {:one {:two {:three 3 :four 4 :five 5}}} (->> ((comp :two :one) m) ((juxt :three :five)))) ``` ```clojure (defn get-user [data] (:user data)) (defn get-email [user] (:email user)) (defn trim [s] (str/trim s)) (defn lower [s] (str/lower-case s)) (def domain (comp second (partial split-at "@"))) ;; Compose everything into a pipeline (def process-email (comp domain lower trim get-email get-user)) (defn describe-user [data] (str "Domain: " (process-email data))) (comment (describe-user {:user {:email " Bob@Example.org "}}) ) ``` -------------------------------- ### Identify Inappropriate Intimacy in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md Demonstrates the 'Inappropriate Intimacy' code smell in Clojure, where components have overly tight coupling. This example uses `proxy` to define a `session` that directly exposes its internal structure, violating encapsulation. ```clojure (def session (proxy [clojure.lang.IDeref] [] (deref [] {:user-id 42 :role "admin"}) (store [] {:type ::memory-store}))) (println @session) ``` -------------------------------- ### Manage Test Fixtures Carefully in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the 'Global Test Fixture Cache' smell in Clojure using `clojure.test/use-fixtures`. This anti-pattern involves using fixtures, especially `:once`, to manage mutable global state, breaking test isolation. The example shows a misuse with a global atom for database connections. ```clojure ;; Not from source (defonce *database-conn* (atom nil)) (use-fixtures :once (fn [f] (reset! *database-conn* (setup-db-connection)) (f) (close-db-connection))) (deftest test-user-creation ;; This test relies on the mutable state left by prior tests (is (not (nil? @*database-conn*))) (db/insert @*database-conn* {:user "Alice"})) ``` -------------------------------- ### Splitting Monolithic Namespaces with load/in-ns in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This practice splits a single logical namespace across multiple files using legacy `load` and `in-ns` macros. It hinders dependency resolution and makes code harder to manage. The recommended approach is to use separate, distinct namespaces with explicit `require` statements. The example shows the problematic usage. ```clojure (ns slamhound-test.core) (load "core_extra.clj") (defn -main [& args] (pprint args) (io/copy (ByteArrayInputStream. (.getBytes "hello")) (first args))) (in-ns 'slamhound-test.core) (defn temp []) ``` -------------------------------- ### Clojure - Deeply-nested Call Stacks Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Demonstrates deeply nested function calls in Clojure, which can lead to debugging difficulties, stack overflow risks, and reduced readability. The example shows a chain of string manipulation functions. ```clojure (defn sanitize [s] (clojure.string/trim (clojure.string/lower-case (clojure.string/replace s #"[^a-zA-Z0-9]" "")))) (defn process-user [user] (assoc user :username (sanitize (:name (first (sort-by :created-at (:accounts user))))))) (def users [{:name "Alice" :accounts [{:created-at "2020-01-01" :name "Main"}]} {:name "Bob" :accounts [{:created-at "2019-05-01" :name "Legacy"}]}]) (println (map process-user users)) ``` -------------------------------- ### Detect Shotgun Surgery in Clojure Functions Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md Illustrates the 'Shotgun Surgery' code smell in Clojure, where a single change requires edits across multiple functions. This example shows `create-user` calling `save-user`, `send-welcome-email`, and `track-user`, making modifications to user creation spread out. ```clojure (defn save-user [user] (println "Saving to DB:" (:name user) (:email user) (:age user))) (defn send-welcome-email [user] (println "Sending email to:" (:email user))) (defn track-user [user] (println "Tracking new user:" (:name user) (:age user))) (defn create-user [name email age] (let [user {:name name :email email :age age}] (save-user user) (send-welcome-email user) (track-user user))) (comment (create-user "Alice" "alice@example.com" 30)) ``` -------------------------------- ### Clojure - Inappropriate Collection Usage Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the 'Inappropriate Collection' code smell in Clojure, where a sequential collection is scanned for elements by key instead of using an associative structure. This example checks for a person's name in a list of maps. ```clojure ;; Example from source (def people [{:person/name "Fred"} {:person/name "Ethel"} {:person/name "Lucy"}]) (defn person-in-people? [person people] (some #(= person (:person/name %)) people)) (person-in-people? "Fred" people);; => true (person-in-people? "Bob" people) ;; => nil ``` ```clojure (ns examples.smells.inappropriate-collection) (def people [{:person/name "Fred"} {:person/name "Ethel"} {:person/name "Lucy"}]) (defn person-in-people? [person people] (some #(= person (:person/name %)) people)) (println (boolean (person-in-people? "Fred" people))) (println (boolean (person-in-people? "Alice" people))) ``` -------------------------------- ### Use Namespaced Keys in Clojure to Prevent Collisions Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell occurs when unqualified keywords are used instead of namespaced keywords, leading to ambiguity and potential key collisions, especially in larger projects. The example shows how unqualified `:id` and `:name` can be ambiguous when used with different data structures. ```clojure (def user {:id 1 :name "Alice"}) (def order {:id 101 :name "Order-101"}) (println (:id user)) ;; 1 (println (:id order)) ;; 101 ``` -------------------------------- ### Case with Non-Literal Test Values in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md An example of using the `case` macro with test expressions that rely on runtime values instead of compile-time literals. The `case` macro is optimized for literal identity checks and can lead to logic errors with dynamic values. Use `cond` or `condp` for runtime comparisons. ```clojure ;; Not from source (defn MyCameraView [current-orientation] (case result "portrait" (do-portrait-logic) "landscape" (do-landscape-logic) :else (do-default-logic))) ``` -------------------------------- ### Unmanaged Resource I/O in Clojure (Missing with-open) Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell arises from not using the `with-open` macro for resources implementing `java.io.Closeable`, such as readers, writers, or sockets. This omission leads to resource leaks and potential system instability by failing to release resources. The example shows a function that opens a reader without ensuring its closure. ```clojure ;; Not from source (defn read-file-smelly [filename] (let [reader (io/reader filename)] (line-seq reader))) ``` -------------------------------- ### Identify Divergent Change in Clojure Functions Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md Demonstrates the 'Divergent Change' code smell in Clojure, where a single function needs modification for multiple unrelated reasons. This indicates poor separation of concerns. The example shows a `process-user` function that handles both validation and notification logic. ```clojure (defn process-user [user] (let [full-name (str (:first-name user) " " (:last-name user)) valid-age? (>= (:age user) 18)] (if valid-age? (do (println (str "User " full-name " is valid. Sending notification...")) {:full-name full-name :status "Valid"}) (do (println (str "User " full-name " is not valid. No notification sent.")) {:full-name full-name :status "Invalid"})))) (println (process-user {:first-name "Alice" :last-name "Smith" :age 22})) ``` -------------------------------- ### Unnecessary Laziness in Clojure Sequence Operations Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Demonstrates the use of lazy sequence functions like `map` and `filter` when eager alternatives like `mapv` or `into []` would be more efficient and clearer. Unnecessary laziness adds complexity, risks realization bugs, and makes performance unpredictable. This example is in Clojure. ```clojure (defn process-smelly [coll] (let [doubled (map #(* 2 %) coll)] (vec doubled))) ; Forces realization later ``` -------------------------------- ### Clojure - Lazy Side Effects in Sequences Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the 'Lazy Side Effects' code smell in Clojure, where side effects occur within lazy sequences. The example shows a `notify` function used with `map` and `filter`, where the side effect (printing) may not happen as expected due to laziness. ```clojure (defn notify [x] (println "Notifying value:" x) x) (def data (range 3)) (->> data (map notify) (filter even?)) ;; No output printed ``` -------------------------------- ### Avoid Lazy Sequence Accumulation in Eager Contexts in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Demonstrates the 'Lazy Sequence Accumulation' smell in Clojure, where lazy sequence functions are used in an eager evaluation context. This can lead to high memory consumption and performance issues. The example shows an incorrect usage with `apply merge-with` and `map`. ```clojure ;; Example from source (first (:a (apply merge-with concat (map (fn [n] {:a (range 1 n)}) (range 1 4000))))) ``` -------------------------------- ### Detect Long Parameter List in Clojure Functions Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md Illustrates the 'Long Parameter List' code smell in Clojure, where a function accepts an excessive number of parameters. This makes functions harder to understand and error-prone. The example shows a function with many parameters for user registration. ```clojure (defn register-new-user [username password email phone age gender location interests newsletter-opt-in referred-by] {:username username :password password :email email :phone phone :age age :gender gender :location location :interests interests :newsletter newsletter-opt-in :referral referred-by}) ``` -------------------------------- ### Manage Resource Eager Realization with Lazy Processing Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This code smell involves functions that eagerly buffer entire data sources (like files or streams) into memory instead of processing them lazily. This can lead to excessive resource consumption and `OutOfMemoryError` exceptions, hindering scalability. The example demonstrates a function that buffers a stream into memory, which should be avoided. ```clojure ;; Example from source (defn ?->InputStream [bindata] (cond (satisfies? Media bindata) (.getInputStream bindata) (satisfies? ChunkedStream bindata) (.getInputStream (ChunkedStream->Media bindata)))) ``` -------------------------------- ### Clojure: Avoid Unnecessary Macros Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell identifies the misuse of macros in Clojure where simpler constructs like functions would suffice. Overusing macros can lead to unnecessary abstraction, complexity, and reduced maintainability. The example shows a macro defined for logging and execution, which could potentially be replaced by a simpler approach. ```clojure (defmacro log-and-exec [expr] `(do (println "Running...") ~expr)) ``` -------------------------------- ### Misuse of Channel Closing Semantics in core.async (Clojure) Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell occurs when custom sentinel values are used to signal channel termination instead of the standard `core.async` contract (`a/close!`). Closing a channel causes reads (`a/MyRecord val1 val2)`) for `defrecord` instantiation is non-idiomatic. It's brittle to changes in field order or additions. The idiomatic Clojure way is to use `map->RecordName` with keyword arguments. ```clojure (defrecord Foo [a b]) (Foo. 1 2) ;;=> #user.Foo{:a 1, :b 2} ``` -------------------------------- ### Improve Emptiness Checks in Clojure Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Recommends using the idiomatic `seq` function for checking emptiness in Clojure instead of verbose constructs like `(not (empty? x))`. This leads to more concise and readable code. The anti-pattern uses `(not (empty? x))` and `when-not (empty? x)`, while the better version uses `when (seq x)`. ```clojure ;; Anti-pattern: Verbose emptiness checks (when (not (empty? x)) (process x)) (when-not (empty? x) (process x)) ;; Better: Use seq for idiomatic emptiness check (when (seq x) (process x)) ``` -------------------------------- ### Long Function Smell in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md Demonstrates the 'Long Function' code smell in Clojure, where a function becomes excessively large and performs too many operations. This can lead to difficulties in understanding, maintenance, and testing. This example shows a user validation function with numerous checks. ```clojure (defn validate-user [user] (let [errors (atom [])] (if (string? (:name user)) (do (when (< (count (:name user)) 2) (swap! errors conj "Name is too short.")) (when (> (count (:name user)) 100) (swap! errors conj "Name is too long.")) (when (str/blank? (:name user)) (swap! errors conj "Name cannot be blank.")) (when (re-find #"\d" (:name user)) (swap! errors conj "Name must not contain numbers."))) (swap! errors conj "Invalid name.")) (let [email (:email user)] (if (string? email) (do (if (not (re-matches #".+@.+\..+" email)) (swap! errors conj "Invalid email format.")) (if (or (str/includes? email "spam") (str/includes? email "fake")) (swap! errors conj "Email contains suspicious terms.")) (when (str/ends-with? email ".xyz") (swap! errors conj "Emails ending in .xyz are not allowed."))) (swap! errors conj "Email must be a string."))) (let [pwd (:password user)] (if (string? pwd) (do (when (not-any? #(Character/isUpperCase %) pwd) (swap! errors conj "Password must contain an uppercase letter.")) (when (not-any? #(Character/isDigit %) pwd) (swap! errors conj "Password must contain a number.")) (when (not-any? #(contains? #{\! \@ \# \$ \% \&} %) pwd) (swap! errors conj "Password must contain a special character.")) (when (str/includes? pwd " ") (swap! errors conj "Password must not contain spaces."))) (swap! errors conj "Invalid password."))) (let [age (:age user)] (cond (nil? age) (swap! errors conj "Age is required.") (not (number? age)) (swap! errors conj "Age must be a number.") (> age 120) (swap! errors conj "Invalid age."))) (let [prefs (:preferences user)] (if (sequential? prefs) (do (when (empty? prefs) (swap! errors conj "Preferences list is empty.")) (when (> (count prefs) 20) (swap! errors conj "Too many preferences."))) (swap! errors conj "Preferences must be a list."))) (if (empty? @errors) {:valid true} {:valid false :errors @errors}))) ``` -------------------------------- ### Proper Channel Termination in Clojure Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Illustrates the correct way to signal channel termination in Clojure by closing the channel, rather than relying on sentinel values like `:done`. Closing the channel allows consumers to receive `nil` when the channel is exhausted, which is a more robust approach. ```clojure ;; Anti-pattern: Sentinel value for termination (def my-chan (a/chan 1)) (a/put! my-chan :done) (a/go (loop [] (when-let [event (a/>`) for improved readability and clearer data flow. This avoids deeply nested code and makes the sequence of operations more apparent. The anti-pattern uses nested `let`, while the better version employs `->>`. ```clojure ;; Anti-pattern: Nested let bindings (defn transform [xs] (let [step1 (map inc xs) step2 (filter even? step1) step3 (reduce + step2)] step3)) (transform [1 2 3 4]) ;; => 12 ;; Better: Use threading macros for clear data flow (defn transform [xs] (->> xs (map inc) (filter even?) (reduce +))) (transform [1 2 3 4]) ;; => 12 ``` -------------------------------- ### Reinventing the Wheel: Clojure - Use idiomatic functions like mapcat Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Shows how to avoid reinventing the wheel by using built-in Clojure functions. The anti-pattern manually implements functionality similar to `mapcat`, while the better solution utilizes `mapcat` for efficiency and readability. ```clojure ;; Anti-pattern: Manually reimplementing mapcat (defn process-data [data] (let [filtered (filter (fn [x] (not (nil? (get x :active)))) data) names (map (fn [x] (get x :name)) filtered) seconds (map (fn [x] (nth x 1)) (map vec (map :tags filtered))) flat-tags (apply concat (map (fn [x] (:tags x)) filtered))] {:names names :seconds seconds :flat-tags flat-tags})) (process-data [{:name "Alice" :active true :tags ["admin" "editor"]} {:name "Bob" :active false :tags ["viewer" "editor"]} {:name "Carol" :active true :tags ["editor" "reviewer"]}]) ;; Better: Use idiomatic functions (defn process-data [data] (let [filtered (filter :active data) names (map :name filtered) seconds (map #(second (:tags %)) filtered) flat-tags (mapcat :tags filtered)] ;; Use mapcat! {:names names :seconds seconds :flat-tags flat-tags})) ``` -------------------------------- ### Production `doall` in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Shows the 'Production `doall`' code smell, where `doall` is used to force realization of lazy sequences in production. This practice undermines Clojure's laziness and can lead to performance issues. ```clojure (defn print-evens [] (doall (map #(println %) (filter even? (range 1000))))) (print-evens) ``` -------------------------------- ### Relying on Load-Time Side Effects in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This anti-pattern involves relying on a function's output being static because it was evaluated at namespace load time. It breaks static analysis and causes non-deterministic behavior due to reliance on unmanaged side effects and mutable state. The example demonstrates a configuration loaded at top-level. ```clojure (ns my-app.fragile-config) ;; Relies on this complex function running *only once* at load time (def CONFIG (calculate-heavy-config (some/global-atom))) ``` -------------------------------- ### Use `seq` for Non-Empty Checks in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell involves using verbose or non-idiomatic constructs like `(not (empty? x))` to check if a collection is non-empty. The idiomatic Clojure approach is to use `seq x`, which is more concise and handles Clojure's nuanced concept of emptiness, including `nil` and lazy sequences. ```clojure ;; Example from source (when (not (empty? x)) ...) (when-not (empty? x) ...) ``` -------------------------------- ### Inline Complex Operation Smell in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This smell represents complex, multi-step business logic directly within an inline code block rather than delegated to a named function. It hinders readability, testability, and compiler optimizations. The example demonstrates a rule definition with inline calculations for discount and new price. ```clojure (defrule calculate-discount [:user :id ?user-id :status "VIP"] => (let [base-price (get-in @db/store [:items ?item-id :price]) discount (* base-price 0.15) new-price (- base-price discount)] (insert! (->Purchase ?user-id ?item-id new-price)))) ``` -------------------------------- ### Avoid Unnecessary Macros in Clojure Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Demonstrates how to replace unnecessary macros with regular functions or existing Clojure constructs. This improves code clarity by avoiding over-abstraction and complexity. The anti-pattern shows a custom `unless` macro, while the better version uses the idiomatic `when-not`. ```clojure (ns examples.clojure-specific.smells.unnecessary-macros) (defmacro unless [test & body] `(if (not ~test) (do ~@body))) (unless false (println "This runs because test is false")) ;; Better: Use a regular function or existing constructs (when-not false (println "This runs because test is false")) ``` -------------------------------- ### Use `cond` or `condp` for Runtime Comparisons in Clojure Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Highlights the anti-pattern of using `case` with runtime values in Clojure, which can lead to logic errors because `case` expects compile-time literals. The better approach uses `cond` or `condp` for runtime value comparisons. ```clojure ;; Anti-pattern: case with runtime values (defn MyCameraView [current-orientation] (let [portrait-mode "portrait" landscape-mode "landscape"] (case current-orientation portrait-mode (do-portrait-logic) ;; Won't match runtime value! landscape-mode (do-landscape-logic) (do-default-logic)))) ;; Better: Use cond or condp for runtime comparison (defn MyCameraView [current-orientation] (cond (= current-orientation "portrait") (do-portrait-logic) (= current-orientation "landscape") (do-landscape-logic) :else (do-default-logic))) ``` -------------------------------- ### Avoid Reimplementing Standard Clojure Functions Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This code smell involves reimplementing existing idiomatic Clojure functions like map, filter, or concat. Instead of using these built-in, optimized functions, developers might use verbose anonymous functions or manual iteration. This reduces readability and increases the chance of bugs. The example shows a common pattern that could be replaced by `mapcat`. ```clojure ;; Example from source (apply concat (map f xs)) ``` ```clojure (defn process-data [data] (let [filtered (filter (fn [x] (not (nil? (get x :active)))) data) names (map (fn [x] (get x :name)) filtered) seconds (map (fn [x] (nth x 1)) (map vec (map :tags filtered))) flat-tags (apply concat (map (fn [x] (:tags x)) filtered))] {:names names :seconds seconds :flat-tags flat-tags})) (process-data [{:name "Alice" :active true :tags ["admin" "editor"]} {:name "Bob" :active false :tags ["viewer" "editor"]} {:name "Carol" :active true :tags ["editor" "reviewer"]}]) ``` -------------------------------- ### Refs in Dependency Vector (React Hooks) in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Placing mutable state references (atoms, state objects) directly in a hook's dependency array (`[]`) is an anti-pattern. This can cause hooks to either never re-run or re-run unexpectedly due to reference stability or framework updates. The idiomatic solution is to use the dereferenced value (`@ref`) in the dependency array. The example demonstrates this incorrect pattern. ```clojure ;; Not from source (def my-atom-ref (r/atom 0)) (defn MyComponent [] (r/use-effect (fn [] (println "Value is now:" @my-atom-ref)) [my-atom-ref])) ``` -------------------------------- ### Marker Protocol Definition in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the use of `defprotocol` solely to define a type identifier (a "marker") for use with type checking, rather than defining a contract for polymorphic behavior. This approach unnecessarily introduces the complexity and overhead of the protocol machinery. ```clojure clojure.lang.IEquiv (-equiv [_ other] (and (satisfies? IUUID other) (identical? uuid (.-uuid other)))) ``` -------------------------------- ### Clojure Function Validation with Comments Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/traditional/README.md This Clojure code snippet demonstrates a function that performs validation and data transformation. It uses comments to explain different stages of the process, such as simulating database saving, validating input, and transforming data. While comments can aid readability, over-reliance might indicate a smell. ```clojure (ns examples.smells.comments (:require [clojure.string :as str])) (defn save-user [user]) ;; simulate saving to database (println "Saving user:" user)) (defn process-user [user]) ;; validate input (when-not (:email user) (throw (Exception. "Missing email"))) (when-not (:id user) (throw (Exception. "Missing ID"))) ;; transform data (let [username (str/lower-case (:email user)) uid (str "user-" (:id user))]) ;; store in database (save-user {:username username :uid uid :email (:email user)}))) (process-user {:id 1 :email "Exemplo@Email.com"}) ``` -------------------------------- ### Use Hierarchical Namespaces in Clojure Projects Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Using single-segment namespaces (e.g., `digest`) instead of hierarchical ones (e.g., `my-app.digest`) is a structural anti-pattern. This violates Clojure's naming conventions, increases the risk of global naming collisions, reduces code organization clarity, and can cause tooling errors. ```clojure ;; Not from source (ns digest) (defn md5 [s] ...) ``` -------------------------------- ### Avoid `nil` Values in Clojure Maps Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This code smell involves inserting `nil` values into Clojure maps. Since accessing a missing key or a key mapped to `nil` both return `nil`, this practice creates ambiguity and can lead to subtle bugs. It's recommended to omit the key or use a sentinel value instead. ```clojure (defn welcome-message [user] (str "Welcome, " (:name user))) (welcome-message {:id 42}) (welcome-message {:id 43 :name nil}) ``` -------------------------------- ### Refactor Long Parameter List in Clojure using Maps Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Illustrates refactoring functions with excessive parameters into ones that accept a map. This improves readability and maintainability by grouping related parameters and allowing for optional parameters. ```clojure ;; Anti-pattern: Too many parameters (defn register-new-user [username password email phone age gender location interests newsletter-opt-in referred-by] {:username username :password password :email email :phone phone :age age :gender gender :location location :interests interests :newsletter newsletter-opt-in :referral referred-by}) ;; Better: Use a map parameter (defn register-new-user [{:keys [username password email phone age gender location interests newsletter-opt-in referred-by]}] {:username username :password password :email email :phone phone :age age :gender gender :location location :interests interests :newsletter newsletter-opt-in :referral referred-by}) ;; Even better: Pass entire map through (defn register-new-user [user-data] (select-keys user-data [:username :password :email :phone :age :gender :location :interests :newsletter-opt-in :referred-by])) ``` -------------------------------- ### Managing Side Effects in Clojure Lazy Sequences Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Addresses the anti-pattern of performing side effects within lazy sequences in Clojure, leading to unpredictable execution. The better solutions involve forcing realization with `doall` or using eager operations like `run!` to ensure side effects are executed predictably. ```clojure ;; Anti-pattern: Side effects in lazy sequence (defn notify [x] (println "Notifying value:" x) x) (def data (range 3)) (->> data (map notify) (filter even?)) ;; No output printed! Side effects not executed until realization ;; Better: Force realization or use eager operations (def result (->> data (map notify) (filter even?) doall)) ;; Forces realization ;; Or better: Separate side effects from data transformation (->> data (filter even?) (run! notify)) ;; Eager side effect iteration ``` -------------------------------- ### Verbose Checks in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Illustrates the 'Verbose Checks' code smell, where custom logic is used for common number checks instead of idiomatic Clojure functions. This results in less readable and more verbose code. ```clojure (defn number-type [n] (cond (= n 0) :zero (< 0 n) :positive (> 0 n) :negative)) (number-type 0) ;; => :zero (number-type 5) ;; => :positive (number-type -3) ;; => :negative ``` -------------------------------- ### Avoid Namespace Load Side Effects in Clojure Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Performing operations like `require` or `requiring-resolve` outside the main `ns` macro is an anti-pattern. It introduces hidden, dynamic dependencies that bypass the build tool's static graph, breaking load ordering and leading to non-deterministic behavior. Ensure all namespace requirements are within the `ns` macro. ```clojure ;; Not from source (ns my-app.core) (def some-var (requiring-resolve 'my-app.config/get-setting)) ``` -------------------------------- ### Clojure: Manual state/action dispatch with cond/case (Reinventing Dispatch) Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md This code smell involves manually implementing state and action dispatch using complex nested `cond` or `case` structures instead of more idiomatic Clojure abstractions like Multimethods, Protocols, or Function Maps. This makes the code harder to read, extend, and maintain as new states or types are added. ```clojure ;; Not from source (defn run-pda [[state clicked]] (cond (and (= state :start) (= clicked :coin)) :running (and (= state :running) (= clicked :stop)) :stopped (and (= state :running) (not= clicked :stop)) :running :else (throw (ex-info "Invalid transition")))) ``` -------------------------------- ### Direct Function References Over Lambdas in Clojure Source: https://context7.com/nufuturo-ufcg/clj-smells-catalog/llms.txt Demonstrates the anti-pattern of using unnecessary lambda wrappers (`#(...)`) when a direct function reference is clearer in Clojure. The improved version passes the function directly to `map`, making the code more concise and readable. ```clojure ;; Anti-pattern: Unnecessary lambda wrapper (ns examples.functional.smells.trivial-lambda) (defn square [x] (* x x)) (def numbers [1 2 3 4]) (println (map #(square %) numbers)) ;; => (1 4 9 16) ;; Better: Pass function directly (println (map square numbers)) ;; => (1 4 9 16) ``` -------------------------------- ### Avoid Blocking Operations Inside Clojure Go Blocks Source: https://github.com/nufuturo-ufcg/clj-smells-catalog/blob/main/README.md Calling blocking operations (e.g., `a/!!`, or general blocking I/O) within a `go` block is a code smell. `go` blocks are intended for non-blocking concurrency and run on a limited thread pool. Blocking can lead to thread starvation, deadlocks, and system-wide performance issues. ```clojure ;; Not from source (ns my-app.async-fail (:require [clojure.core.async :as a])) (def my-chan (a/chan)) (a/go (println "Blocked thread consuming:" (a/` or `->>` for a clear, step-by-step data transformation flow. ```clojure ;; Anti-pattern: Over-composed pipeline (defn get-user [data] (:user data)) (defn get-email [user] (:email user)) (defn trim [s] (str/trim s)) (defn lower [s] (str/lower-case s)) (def domain (comp second (partial re-seq #"@(.+)"))) (def process-email (comp domain lower trim get-email get-user)) (defn describe-user [data] (str "Domain: " (process-email data))) (describe-user {:user {:email " Bob@Example.org "}}) ;; => "Domain: Example.org" ;; Better: Clear threading pipeline (defn describe-user [data] (let [domain (-> data :user :email str/trim str/lower-case (str/split #"@") second)] (str "Domain: " domain))) ```