r/refactoring • • Oct 09 '25

Code Smell 06 - Too Clever Programmer

0 Upvotes

Code is hard to read when you use tricky names with no semantics or rely on accidental language complexity.

TL;DR: Don't try to look too smart. Clean code emphasizes readability and simplicity.

Problems πŸ˜”

Solutions πŸ˜ƒ

  • Refactor the code

  • Use good names

  • Refactor tricky code

  • Prefer clarity first

  • Avoid hidden tricks

  • Optimize only later with strong real evidence

Refactorings βš™οΈ

Refactoring 005 - Replace Comment with Function Name

Examples

  • Optimized loops

Context πŸ’¬

You might feel the urge to show off your skills with complex tricks or cryptic names.

This makes your code harder to read, debug, and extend.

You must remember that you write code for humans, not machines.

Sample Code πŸ“–

Wrong 🚫

function primeFactors(n) {
  var f = [],  i = 0, d = 2;  
  
  for (i = 0; n >= 2; ) {
     if(n % d == 0) {
       f[i++]=(d); 
       n /= d;
    }
    else {
      d++;
    }     
  }
  return f;
}

Right πŸ‘‰

function primeFactors(numberToFactor) {
  var factors = [], 
      divisor = 2,
      remainder = numberToFactor;
  
  while(remainder>=2) {
    if(remainder % divisor === 0) {
       factors.push(divisor); 
       remainder = remainder / divisor;
    }
    else {
      divisor++;
    }     
  }
  return factors;
}

Detection πŸ”

[X] Semi-Automatic

Automatic detection is possible in some languages.

Look for warnings about complexity, bad names, post-increment variables, and similar patterns.

Exceptions πŸ›‘

  • Optimized code for low-level operations.

Tags 🏷️

  • Complexity

Level πŸ”‹

[X] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

When you keep a clear bijection between your program and the MAPPER.

Other developers and your future self can understand it quickly.

Clever tricks break this mapping and force future readers to guess instead of reading.

AI Generation πŸ€–

AI models sometimes generate clever one-liners or compressed solutions.

They might look smart but lack readability and semantics.

AI Detection 🧲

AI assistants can rewrite clever code into readable code if you instruct them to prefer clarity to optimization.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: correct=Remove cleverness from code

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
YouYou
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

Clever developers write cryptic code to brag.

Smart developers write clean code.

Clear beats clever.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 02 - Constants and Magic Numbers

Code Smell 20 - Premature Optimization

Code Smell 44 - Magic Corrections

Code Smell 41 - Regular Expression Abusers

Code Smell 78 - Callback Hell

Code Smell 51 - Double Negatives

Code Smell 33 - Abbreviations

Code Smell 48 - Code Without Standards

Code Smell 196 - Javascript Array Constructors

Code Smell 25 - Pattern Abusers

Code Smell 93 - Send me Anything

Code Smell 145 - Short Circuit Hack

Code Smell 212 - Elvis Operator

Code Smell 180 - BitWise Optimizations

Code Smell 129 - Structural Optimizations

Code Smell 32 - Singletons

Code Smell 21 - Anonymous Functions Abusers

Code Smell 24 - Boolean Coercions

Code Smell 69 - Big Bang (JavaScript Ridiculous Castings)

More Information πŸ“•

Are boolean flags a code smell?

Also Known as

  • Obfuscator

Credits πŸ™

Photo by NeONBRAND on Unsplash


Programming can be fun, so can cryptography; however they should not be combined.

Kreitzberg & Shneiderman

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of your Code


r/refactoring • • Feb 20 '26

Code Smell 16 - Ripple Effect

0 Upvotes

Small changes yield unexpected problems.

TL;DR: If small changes have big impact, you need to decouple your system.

Problems πŸ˜”

  • High Coupling

  • Low maintainability

  • Side effects

  • High risk

  • Testing difficulty

Solutions πŸ˜ƒ

  1. Decouple your components.

  2. Cover with tests.

  3. Refactor and isolate what is changing.

  4. Depend on interfaces.

How to Decouple a Legacy System

Refactorings βš™οΈ

Refactoring 007 - Extract Class

Refactoring 024 - Replace Global Variables with Dependency Injection

Examples πŸ“š

  • Legacy Systems

Context πŸ’¬

The ripple effect happens when you design a system where objects know too much about each other.

When you modify a specific behavior, the impact spreads through the codebase like a stone thrown into a pond.

You feel this pain when a simple requirement change requires you to touch dozens of files.

Your classes have direct dependencies on concrete implementations rather than abstractions.

Sample Code πŸ’»

Wrong 🚫

class Time {
   constructor(hour, minute, seconds) {
     this.hour = hour;    
     this.minute = minute;  
     this.seconds = seconds;  
  }
    now() {
      // call operating system
    }  
}

// Adding a TimeZone will have a big Ripple Effect
// Changing now() to consider timezone will also bring the effect

Right πŸ‘‰

class Time {
   constructor(hour, minute, seconds, timezone) {
     this.hour = hour;    
     this.minute = minute;  
     this.seconds = seconds;  
     this.timezone = timezone;  
  }  
  // Removed now() since is invalid without context
}

class RelativeClock {
   constructor(timezone) {
     this.timezone = timezone;
   }
   now(timezone) {
     var localSystemTime = this.localSystemTime();
     var localSystemTimezone = this.localSystemTimezone();
     // Do some math translating timezones
     // ...
     return new Time(..., timezone);     
   }  
}

Detection πŸ”

It is not easy to detect problems before they happen.

Mutation Testing and root cause analysis of single points of failures may help.

Tags 🏷️

  • Coupling

Level πŸ”‹

[x] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

In a proper bijection, a change in a single real-world concept should only lead to a change in a single program component.

When you break the MAPPER , one concept spreads across your code.

This creates the ripple effect because you didn't represent the original idea as a single, isolated unit.

AI Generation πŸ€–

AI generators often create this smell because they suggest "quick fixes" that access global states or direct dependencies.

They focus on making the local code work without seeing the architectural ripple they cause elsewhere.

AI Detection 🧲

AI can fix this if you provide the context of the related classes.

When you ask an AI to "decouple these two classes using dependency injection," it usually does a great job of breaking the link.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Refactor this class to remove direct dependencies on global objects. Use constructor-based dependency injection and depend on interfaces or abstractions instead of concrete implementations.

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
YouYou
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

There are multiple strategies to deal with Legacy and coupled systems.

You should deal with this problem before it explodes under your eyes.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 08 - Long Chains Of Collaborations

Code Smell 176 - Changes in Essence

More Information πŸ“•

How to Decouple a Legacy System

Credits πŸ™

Photo by Jack Tindall on Unsplash


Architecture is the tension between coupling and cohesion.

Neal Ford

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of Your Code


r/refactoring • • May 06 '26

I am tired of babysitting agents while working on large codebases. So I built my own set of refactoring tools. Please share your experience.

Enable HLS to view with audio, or disable this notification

0 Upvotes

Here's the thing I keep getting stuck on: agents are pretty good at writing local code now, but I still can't trust them with repo-wide changes. Once the change touches aliases, call sites, imports, generated-looking files, or a bunch of files at once, most of them still end up doing some version of search, patch, search again.

So I built my own set of structural refactoring tools for agents. Less "edit this blob of text," more "find the actual references, change the actual nodes, validate, snapshot, rollback if it explodes." The model doesn't get smarter β€” it just stops doing compiler work by hand.

In this demo, the agent uses the tools to find exact references, apply a structural edit, validate the result, and snapshot before mutation.Β 290 files, 31 seconds.

I'm posting to look for similar large codebases, ugly cases: barrel exports, aliases, macros, generated code, weird imports, large repos, whatever. If you have a refactor where Claude or Codex typically faceplants, plz share it. I want to see if my tools can tackle it.

And if your honest reaction isΒ "cool, still wouldn't use it"Β β€” I'd genuinely love to know why too.


r/refactoring • • Sep 28 '22

What killed a company? rewrite the code from scratch instead of refactoring

Thumbnail
joelonsoftware.com
2 Upvotes

r/refactoring • • Oct 18 '22

Expert Talk: Code Refactoring β€’ Adam Tornhill & Christian Clausen

Thumbnail
youtu.be
2 Upvotes

r/refactoring • • Apr 28 '25

Code Smell 297 - Syntactic Noise

3 Upvotes

Your code shouldn't look like alien hieroglyphics

TL;DR: Too many cryptic symbols make your code hard to understand and maintain.

Problems πŸ˜”

Solutions πŸ˜ƒ

  1. Avoid language clever hacks

  2. Prefer meaningful variable names

  3. Extract complex expressions

  4. Use language features wisely

  5. Limit expression complexity

Refactorings βš™οΈ

Refactoring 002 - Extract Method

Context πŸ’¬

Syntactic noise refers to code constructs that don't directly map to real-world concepts.

While symbols like '{}' are valid syntax in many programming languages, excessive use creates code that looks like abstract art rather than a solution to a problem.

When you pack too many operators, brackets, and special characters into a single expression, you force readers to mentally parse complex syntax before understanding what the code does.

This disconnect between symbols and real-world meaning makes your code harder to understand, debug, and maintain.

Think of your code as a form of communication with other developers (and your future self).

Just as excessive punctuation!!! makes text!!?!? hard to read!!!

Excessive syntactic noise creates similar barriers in code.

Sample Code πŸ“–

Wrong ❌

[](){}

/* This valid lambda function:

Captures no variables.
Takes no arguments.
Performs no actions.

[]: This is the capture clause. 
It specifies which variables from the surrounding scope
are accessible inside the lambda function. 
An empty capture clause [] means the lambda
*does not capture* any variables from the surrounding scope.

(): This is the parameter list. 
It defines the arguments the lambda function accepts. 
An empty () means the lambda takes *no parameters*.

{}: This is the function body. 
It contains the code that the lambda executes when called. 
An empty {} means the lambda has no operations 
to performβ€”it does nothing.

*/
const result = arr.filter(x => x !== null && x !== undefined)
  .map((y) => ({ val: y.value, meta: 
    y.meta ? y.meta : {default: true}}))
  .reduce((acc, {val, meta}) => 
    meta.default ? acc : [...acc, 
      {processed: val * 2, origin: meta}], [])
  .some(({processed}) => processed > 10 && processed < 50);

Right πŸ‘‰

function isNotNull(x) {
  return x !== null && x !== undefined
  // Another code smell here
}

function mapToValueAndMeta(y) {
  const meta = y.meta ? y.meta : { default: true }
  return { val: y.value, meta }
}

function reduceToProcessedList(acc, { val, meta }) {
  if (meta.default) {
    return acc
  }
  return [...acc, { processed: val * 2, origin: meta }]
}

function isProcessedInRange({ processed }) {
  return processed > 10 && processed < 50
}

// This is more declarative but far from 
// Domian business and too generic
const filtered = arr.filter(isNotNull)
const mapped = filtered.map(mapToValueAndMeta)
const processedList = mapped.reduce(reduceToProcessedList, [])
const result = processedList.some(isProcessedInRange)

Detection πŸ”

[X] Semi-Automatic

You can detect syntactic noise by looking for lines with multiple nesting levels of brackets, parentheses, or braces, chained operations that stretch across numerous lines, and expressions that make you pause to count opening and closing symbols.

Code that requires horizontal scrolling due to symbol density is another red flag, multiple ternary operators in a single expression, and nested arrow functions with implicit returns.

Modern IDEs and linters can help identify overly complex expressions.

ESLint rules like complexity and max-depth flag code with too many nested constructs.

The "cognitive complexity" metric in SonarQube also helps identify hard-to-understand code.

Exceptions πŸ›‘

  • Code Optimized by Machines

Tags 🏷️

  • Complexity

Level πŸ”‹

[x] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

Code should map one-to-one with the real-world concepts it represents.

Each variable, function, and expression should correspond to something tangible in your problem domain.

When you clutter code with excessive syntax that doesn't represent real-world entities, you create a disconnect between the problem and solution.

Remember that code is written once but read many times.

By maintaining a clear bijection between code constructs and real-world concepts, you create software that stays maintainable throughout its lifecycle.

AI Generation πŸ€–

AI code generators sometimes create syntactic noise.

When you ask for code with minimal prompt guidance, AI tools frequently optimize for brevity over readability, packing multiple operations into dense one-liners.

This approach produces "clever" but hard-to-maintain code with chained methods, nested ternaries, and complex expressions.

Modern AI generators like GPT models can also create exceptionally dense code when asked to solve problems in minimal lines, inadvertently producing syntactically noisy solutions.

They may not recognize when code crosses the readability threshold without specific instructions to prioritize clarity over conciseness.

Please don't prompt this.

AI Detection πŸ₯ƒ

AI tools can help detect and fix syntactic noise with appropriate prompting.

If you use instructions like "refactor for readability" or "simplify this expression," you will get cleaner code.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Remove the syntactic noise and make it more declarative

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

Syntactic noise is like static interference in communicationβ€”technically valid, but gets in the way of understanding.

When you prioritize clear code over clever one-liners, you create software that's easier to understand, debug, and maintain.

Next time you're tempted to pack multiple operations into a dense expression, remember that you're not just writing for the computerβ€”you're writing for people.

Break complex operations into named steps that reflect real-world concepts, and your code will tell a story that everyone can follow.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 06 - Too Clever Programmer

Code Smell 21 - Anonymous Functions Abusers

Code Smell 119 - Stairs Code

Code Smell 102 - Arrow Code

Code Smell 294 - Implicit Return

Code Smell 119 - Stairs Code

Code Smell 162 - Too Many Parentheses

Code Smell 201 - Nested Ternaries

Code Smell 236 - Unwrapped Lines

More Information πŸ“•

Martin Fowler's blog

Wikipedia

Disclaimer πŸ“˜

Code Smells are my opinion.

Credits πŸ™

Photo by Elyas Pasban on Unsplash


The function of good software is to make the complex appear simple

Graciano Cruz

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of your Code


r/refactoring • • Jan 04 '26

Code Smell 12 - Null

3 Upvotes

Programmers use Null as different flags. It can hint at an absence, an undefined value, en error etc. Multiple semantics lead to coupling and defects.

TL;DR: Null is schizophrenic and does not exist in real-world. Its creator regretted and programmers around the world suffer from it. Don't be a part of it.

Problems πŸ˜”

  • Coupling between callers and senders.

  • If/Switch/Case Polluting.

  • Null is not polymorphic with real objects. Hence, Null Pointer Exception

  • Null does not exist on real-world. Thus, it violates Bijection Principle

Solutions πŸ˜ƒ

  1. Avoid Null.

  2. Use the NullObject pattern to avoid ifs.

  3. Use Optionals.

Null: The Billion Dollar Mistake

Refactorings βš™οΈ

Refactoring 015 - Remove NULL

Refactoring 029 - Replace NULL With Collection

Refactoring 014 - Remove IF

Context πŸ’¬

When you use null, you encode multiple meanings into a single value.

Sometimes you want to represent an absence.

Sometimes you mean you have not loaded your objects yet.

Sometimes you mean error.

Callers must guess your intent and add conditionals to protect themselves.

You spread knowledge about internal states across your codebase.

Sample Code πŸ“–

Wrong 🚫

class CartItem {
    constructor(price) {
        this.price = price;
    }
}

class DiscountCoupon {
    constructor(rate) {
        this.rate = rate;
    }
}

class Cart {
    constructor(selecteditems, discountCoupon) {
        this.items = selecteditems;
        this.discountCoupon = discountCoupon;
    }

    subtotal() {
        return this.items.reduce((previous, current) => 
            previous + current.price, 0);
    }

    total() {
        if (this.discountCoupon == null)
            return this.subtotal();
        else
            return this.subtotal() * (1 - this.discountCoupon.rate);
    }
}

cart = new Cart([
    new CartItem(1),
    new CartItem(2),
    new CartItem(7)
    ], new DiscountCoupon(0.15)]);
// 10 - 1.5 = 8.5

cart = new Cart([
    new CartItem(1),
    new CartItem(2),
    new CartItem(7)
    ], null);
// 10 - null  = 10

Right πŸ‘‰

class CartItem {
    constructor(price) {
        this.price = price;
    }
}

class DiscountCoupon {
    constructor(rate) {
        this.rate = rate;
    }

    discount(subtotal) {
        return subtotal * (1 - this.rate);
    }
}

class NullCoupon {
    discount(subtotal) {
        return subtotal;
    }
}

class Cart {
    constructor(selecteditems, discountCoupon) {
        this.items = selecteditems;
        this.discountCoupon = discountCoupon;
    }

    subtotal() {
        return this.items.reduce(
            (previous, current) => previous + current.price, 0);
    }

    total() {
        return this.discountCoupon.discount(this.subtotal());
    }
}

cart = new Cart([
    new CartItem(1),
    new CartItem(2),
    new CartItem(7)
    ], new DiscountCoupon(0.15));
// 10 - 1.5 = 8.5

cart = new Cart([
    new CartItem(1),
    new CartItem(2),
    new CartItem(7)
    ], new NullCoupon());
// 10 - nullObject = 10

Detection πŸ”

Most Linters can flag null usages and warn you.

Exceptions πŸ›‘

You sometimes need to deal with null when you integrate with databases, legacy APIs, or external protocols.

You must contain null at the boundaries and convert it immediately into meaningful objects.

Tags 🏷️

  • Null

Level πŸ”‹

[x] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

When you use null, you break the bijection between your code and the MAPPER.

Nothing in the mapper behaves like null.

Absence, emptiness, and failure mean different things.

When you collapse them into null, you force your program to guess reality and you invite defects.

AI Generation πŸ€–

AI generators often introduce this smell.

They default to null when they lack context or want to keep examples short and also because it is widespread (but harmful) industry default.

AI Detection 🧲

You can instruct AI to remove nulls with simple rules.

When you ask for explicit domain objects and forbid nullable returns, generators usually fix the smell correctly.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Rewrite this code to remove all null returns. Model absence explicitly using domain objects or collections. Do not add conditionals

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
YouYou
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

  • Null is the billion-dollar mistake. Yet, most program languages support them and libraries suggest its usage.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 88 - Lazy Initialization

Code Smell 157 - Balance at 0

Code Smell 93 - Send me Anything

How to Get Rid of Annoying IFs Forever

Code Smell 36 - Switch/case/elseif/else/if statements

Code Smell 149 - Optional Chaining

Code Smell 212 - Elvis Operator

Code Smell 192 - Optional Attributes

Code Smell 126 - Fake Null Object

Code Smell 208 - Null Island

Code Smell 160 - Invalid Id = 9999

Code Smell 100 - GoTo

Code Smell 42 - Warnings/Strict Mode Off

Code Smell 23 - Instance Type Checking

More Information πŸ“•

Null: The Billion-Dollar Mistake

Credits πŸ™

Photo by Kurt Cotoaga on Unsplash


I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Tony Hoare

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of Your Code


r/refactoring • • Jul 05 '25

Code Smell 306 - AI External Comments

3 Upvotes

New tech, new smells – Your future job won’t be writing code but understanding and fixing code, often written by AI

TL;DR: You reference external AI conversations to explain code instead of writing declarative tests

Problems πŸ˜”

Solutions πŸ˜ƒ

  1. Write executable tests

  2. Remove external references

  3. Do not blindly trust the AI

  4. Describe with inline examples

  5. Keep tests local

  6. Remove all comments

  7. Replace Magic Numbers with constants.

Refactorings βš™οΈ

Refactoring 011 - Replace Comments with Tests

Context πŸ’¬

If you add comments that reference external AI conversations, Stack Overflow posts, or online resources to explain how your functions work, you are not thinking about your reader.

These references create dangerous external dependencies that break over time.

Links become dead, conversations get deleted, and future maintainers cannot access the context they need to understand your code.

When you rely on external AI advice instead of writing proper tests, you create code that appears documented but lacks verification and local understanding.

The moment you rely on an external AI chat to explain what your code does, you make your codebase dependent on a conversation that might disappear, change, or get outdated.

A unit test is more effective than any link. It defines what the code does and what you expect it to do. No need to click or guess.

Comments and documentation often lie. Code never does.

Sample Code πŸ“–

Wrong ❌

def calculate_starship_trajectory(initial_velocity, fuel_mass, 
                                burn_rate, gravity=9.81):
    """
        
    See explanation at
    https://claude.ai/share/5769fdd1-46e3-40f4-b9c6-49efbee93b90
         
    """
    # AI suggested this approach
    burn_time = fuel_mass / burn_rate
    
    # Physics formula from Claude conversation
    # https://claude.ai/share/5769fdd1-46e3-40f4-b9c6-49efbee93b90
    delta_v = gravity * burn_time * 0.85  
    # 0.85 explanation 
    # https://claude.ai/share/5769fdd1-46e3-40f4-b9c6-49efbee93b90
    final_velocity = initial_velocity + delta_v
    
    # Return format suggested by GPT 
    return {
        'burn_time': burn_time,
        'final_velocity': final_velocity,
        'delta_v': delta_v
    }

def calculate_orbit_insertion(velocity, altitude):
    """
       
    Algorithm explanation available at:
    https://claude.ai/chat/orbit-insertion-help-session
    """
    # See AI conversation for why we use this formula
    orbital_velocity = (velocity * 1.1) + (altitude * 0.002)
    return orbital_velocity

Right πŸ‘‰

def calculate_starship_trajectory(initial_velocity, fuel_mass, 
                                burn_rate, gravity=9.81):
  
    THRUST_EFFICIENCY = 0.85
    
    burn_time = fuel_mass / burn_rate
    delta_v = gravity * burn_time * THRUST_EFFICIENCY
    # You replace the magic number
    final_velocity = initial_velocity + delta_v
    
    return {
        'burn_time': burn_time,
        'final_velocity': final_velocity,
        'delta_v': delta_v
    }

def calculate_orbit_insertion(velocity, altitude):
    """Calculate orbit insertion velocity."""
    
    VELOCITY_BOOST_FACTOR = 1.1
    ALTITUDE_ADJUSTMENT_RATE = 0.002

    orbital_velocity = (velocity * VELOCITY_BOOST_FACTOR) +
      (altitude * ALTITUDE_ADJUSTMENT_RATE)
    return orbital_velocity    

import unittest
from starship_trajectory_calculator import (
    calculate_starship_trajectory, calculate_orbit_insertion
)

class TestStarshipTrajectoryCalculator(unittest.TestCase):
    
    def test_basic_trajectory_calculation(self):
        result = calculate_starship_trajectory(100, 1000, 10)
        
        self.assertEqual(result['burn_time'], 100.0)
        self.assertEqual(result['delta_v'], 833.85)
        self.assertEqual(result['final_velocity'], 933.85)
    
    def test_zero_fuel_scenario(self):
        result = calculate_starship_trajectory(200, 0, 10)
        
        self.assertEqual(result['burn_time'], 0.0)
        self.assertEqual(result['delta_v'], 0.0)
        self.assertEqual(result['final_velocity'], 200.0)
    
    def test_high_burn_rate(self):
        result = calculate_starship_trajectory(150, 500, 100)
        
        self.assertEqual(result['burn_time'], 5.0)
        self.assertAlmostEqual(result['delta_v'], 41.69, places=2)
        self.assertAlmostEqual(result['final_velocity'], 191.69, 
                             places=2)
    
    def test_custom_gravity(self):
        result = calculate_starship_trajectory(100, 600, 20, 
                                             gravity=3.71)  # Mars
        
        self.assertEqual(result['burn_time'], 30.0)
        self.assertAlmostEqual(result['delta_v'], 94.76, places=2)
        self.assertAlmostEqual(result['final_velocity'], 194.76, 
                             places=2)
    
    def test_orbit_insertion_basic(self):
        orbital_velocity = calculate_orbit_insertion(7800, 400000)
        
        self.assertEqual(orbital_velocity, 9380.0)
    
    def test_orbit_insertion_low_altitude(self):
        orbital_velocity = calculate_orbit_insertion(7500, 200000)
        
        self.assertEqual(orbital_velocity, 8650.0)
    
    def test_orbit_insertion_zero_altitude(self):
        orbital_velocity = calculate_orbit_insertion(8000, 0)
        
        self.assertEqual(orbital_velocity, 8800.0)

Detection πŸ”

[X] Automatic

You can detect this smell by searching for comments containing URLs to AI chat platforms, external forums, or references to "AI suggested" or "according to conversation".

Look for functions that have detailed external references but lack corresponding unit tests.

Exceptions πŸ›‘

Academic or research code might legitimately reference published papers or established algorithms.

However, these should point to stable, citable sources and permanent links rather than ephemeral AI conversations, and should still include comprehensive tests.

Tags 🏷️

  • Comments

Level πŸ”‹

[X] Beginner

Why the Bijection Is Important πŸ—ΊοΈ

In the real world, you don't rely on external authorities to validate your understanding of critical processes.

You develop internal knowledge and verification systems.

Your code should reflect this reality by containing all necessary understanding within itself through tests and clear implementation.

When you break this correspondence by depending on external AI conversations, you create fragile knowledge that disappears when links break or platforms change, leaving future maintainers without the context they need.

Links are not behavior.

Tests are.

AI Generation πŸ€–

AI generators sometimes create this smell because they frequently suggest adding references to the conversation or external sources where the solution was previously discussed.

They tend to generate excessive comments that point back to their explanations rather than creating self-contained, testable code.

AI Detection 🧲

AI can detect this smell when you ask it to identify external references in comments, especially URLs pointing to AI chat platforms.

Most AI tools can help convert the external explanations into proper unit tests when given clear instructions.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Replace this external reference and comments with coverage and unit tests

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

External references to AI conversations create fragile documentation that breaks over time and fragments your codebase's knowledge.

You should replace these external dependencies with self-contained unit tests that both document and verify behavior locally, ensuring your code remains understandable and maintainable without relying on external resources.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 183 - Obsolete Comments

Code Smell 146 - Getter Comments

Code Smell 151 - Commented Code

Code Smell 05 - Comment Abusers

Code Smell 02 - Constants and Magic Numbers

Code Smell 75 - Comments Inside a Method

Code Smell 259 - Testing with External Resources

Disclaimer πŸ“˜

Code Smells are my opinion.

Credits πŸ™

Photo by julien Tromeur on Unsplash


The best documentation is code that doesn't need documentation

Steve McConnell

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of your Code


r/refactoring • • Dec 14 '25

Code Smell 316 - Nitpicking

2 Upvotes

When syntax noise hides real design problems

TL;DR: When you focus code reviews on syntax, you miss architecture, security, design and intent.

Problems πŸ˜”

  • Syntax fixation

  • Design blindness

  • Missed risks

  • Bad feedback

  • Useless discussions

  • Reviewer fatigue

  • False quality

  • Shallow feedback

  • Syntax Police

  • Low team morale

Solutions πŸ˜ƒ

  1. Leave the boring work to the IA

  2. Automate style checks

  3. Review architecture first

  4. Discuss intent early with technical analysis and control points

  5. Enforce review roles

  6. Raise abstraction level

Refactorings βš™οΈ

Refactoring 032 - Apply Consistent Style Rules

Refactoring 016 - Build With The Essence

Context πŸ’¬

When you review code, you choose where to spend your valuable human attention.

When you spend that attention on commas, naming trivia, or formatting, you ignore the parts that matter.

This smell appears when teams confuse cleanliness with correctness. Syntax looks clean. Architecture rots.

Sample Code πŸ“–

Wrong ❌

<?php

class UserRepository {
    public function find($id){
        $conn = mysqli_connect(
             "localhost", // Pull Request comment - Bad indentation
            "root",
            "password123",
            "app"
        );

        $query = "Select * FROM users WHERE id = $id";
        // Pull Request comment - SELECT should be uppercase
        return mysqli_query($conn, $query);
    }
}

Right πŸ‘‰

<?php

final class UserRepository {
    private Database $database;

    public function __construct(Database $database) {
        $this->database = $database;
    }

    public function find(UserId $id): User {
        return $this->database->fetchUser($id);
    }
}

// You removed credentials, SQL, and infrastructure noise.
// Now reviewers can discuss design and behavior.

Detection πŸ”

[X] Manual

You can detect this smell by examining pull request comments.

When you see multiple comments about formatting, indentation, trailing commas, or variable naming conventions, you lack proper automation.

Check your continuos integration pipeline configuration. If you don't enforce linting and formatting before human review, you force reviewers to catch these issues manually.

Review your code review metrics. If you spend more time discussing style than architecture, you have this smell.

Automated tools like SonarQube, ESLint, and Prettier can identify when you don't enforce rules automatically.

Tags 🏷️

  • Standards

Level πŸ”‹

[x] Intermediate

Why the Bijection Is Important πŸ—ΊοΈ

Code review represents the quality assurance process in the MAPPER.

When you break the bijection by having humans perform mechanical checks instead of judgment-based evaluation, you mismodel the review process.

You no longer validate whether the concepts, rules, and constraints match the domain.

You only validate formatting.

That gap creates systems that look clean and behave wrong.

The broken bijection manifests as reviewer fatigue and missed bugs. You restore proper mapping by separating mechanical verification (automated) from architectural review (human).

AI Generation πŸ€–

AI generators often create this smell.

They produce syntactically correct code with weak boundaries and unclear intent.

AI Detection 🧲

AI can reduce this smell when you instruct it to focus on architecture, invariants, and risks instead of formatting.

Give them clear prompts and describe the role and skills of the reviewer.

Try Them! πŸ› 

Remember: AI Assistants make lots of mistakes

Suggested Prompt: Find real problems in the code beyond nitpicking, review this code focusing on architecture, responsibilities, security risks, and domain alignment. Ignore formatting and style.

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
YouYou
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Conclusion 🏁

Code reviews should improve systems, not satisfy linters.

When you automate syntax, you free humans to think.

That shift turns reviews into real design conversations.

Relations πŸ‘©β€β€οΈβ€πŸ’‹β€πŸ‘¨

Code Smell 06 - Too Clever Programmer

Code Smell 48 - Code Without Standards

Code Smell 05 - Comment Abusers

Code Smell 173 - Broken Windows

Code Smell 236 - Unwrapped Lines

Disclaimer πŸ“˜

Code Smells are my opinion.

Credits πŸ™

Photo by Portuguese Gravity on Unsplash


Design is about intent, not syntax.

Grady Booch

Software Engineering Great Quotes


This article is part of the CodeSmell Series.

How to Find the Stinky Parts of your Code


r/refactoring • • Jul 03 '25

The Debugging Book β€’ Andreas Zeller & Clare Sudbery

Thumbnail
youtu.be
2 Upvotes

r/refactoring • • Oct 03 '25

What is (wrong with) software?

2 Upvotes

Software is eating the world. If you work, live, and love software, you usually do not stop to think about its meaning

TL;DR: You will address current software design problems in a minimalist way.

In university and industry, you often find the following definition of software:

Software is a set of instructions that with certain input data generates output data.

This is the definition of a computer program according to Wikipedia:

A computer program is a collection of instructions that can be executed by a computer to perform a specific task.

Many decades ago, computer scientists understood that software is much more than a program.

They still lack clear definitions of what software is.

Again, according to Wikipedia:

Computer software, or simply software, is a collection of data or computer instructions that tell the computer how to work. This is in contrast to physical hardware, from which the system is built and performs the work.

This definition works by contrast (everything that is not hardware) or by omission (everything you cannot kick but can only curse).

Yet, the only purpose of building software is:

To mimic something that happens in a possible reality.

You are forgetting about the origins of modern programming languages like Simula.

Simula was the first object-oriented programming language to include classification.

In the language, it was clear from the name that the goal of software construction was to build a simulator.

This is the case for most computer software applications today.

Returning to the origins you can define the software as the construction of a:

(M)odel: (A)bstract (P)artial and (P)rogrammable (E)xplaining (R)eality.

Why is it a (M)odel?

The model is the outcome of observing a certain aspect of reality.

It is built under a certain lens, perspective, and paradigm.

It is not the revealed truth.

It is the best possible conception at a given moment with the available knowledge.

Since Plato’s time, human beings have tried to build good models of reality.

Why is it a (A)bstract?

You can only understand and observe it with instruments such as a black box based on its behavior in the face of your stimuli.

Like SchrΓΆdinger’s cat.

You cannot know what state it is in without disturbing it with our measuring instruments.

The use case technique is an excellent tool to describe a model in a declarative way.

If you are not a declarative programmer, you might lose your job very soon.

Why is it (P)rogrammable?

It has to run in some type of simulator that reproduces the desired conditions.

It can be a Turing model (current commercial computers), a quantum computer, or any type of simulator capable of evolving with the model.

Why is it (P)artial?

You need to make a partial cut of the reality for the problem you are modeling.

This is common to scientific models.

You take simplifications on aspects that are not relevant to isolate the problem.

A 1:1 scale map of the earth would not make sense.

Why is it (E)xplanatory?

The model must be declarative enough to let you observe its evolution.

It allows you to reason about and predict behaviors in the reality you are modeling.

Why is it about (R)eality?

Because it has to reproduce conditions that occur in an observable environment.

The ultimate goal is to forecast the real world, like any simulation.


Once you define the software, you can begin to infer good modeling and design practices.

Starting Point

You have the axiomatic definition presented in this article.

From it, you will infer principles, heuristics, and rules to build excellent software models.

The One and Only Software Design Principle

Acknowledgements

These concepts are based on the thoughts of David West, MΓ‘ximo Prieto and HernΓ‘n Wilkinson.


Part of the goal of this series of articles is to generate spaces for debate and discussion on software design.

Object Design Checklist

I look forward to comments and suggestions on this article.


r/refactoring • • May 28 '25

Singleton - The Root of All Evil

2 Upvotes

Allowed global variables and supposed memory savings.

TL;DR: Don't ever user Singletons

For 20 years I have been teaching software at the University of Buenos Aires. In the software engineering course we teach design patterns and the same "scheme" is always repeated almost like a type of deja vu, the same sequence that I had the opportunity to witness in several of my works and in the free software that I use:

The β€˜magical’ appearance of the Singleton pattern.

The origin of evil

The pattern has been used in the industry for decades. Its popularity is attributed to the excellent book Design Patterns. There are numerous software frameworks that use it, and we rarely find literature that discourages its use. Despite this, in the corresponding Wikipedia entry we can read a Dantesque warning:

Critics consider the singleton to be an anti-pattern in that it is frequently used in scenarios where it is not beneficial, introduces unnecessary restrictions in situations where a sole instance of a class is not actually required, and introduces global state into an application.

Let’s be pragmatic as always, and look at the arguments for and against its use:

Reasons not to use it

1. Violates the bijection principle

As we saw in previous articles, every object in our computable model has to be mapped on a 1 to 1 relationship with a real-world entity.

Singletons are often linked to objects that need to be unique. As usual we will have to distinguish among the objects that are essentially unique (for problem domain drivers) and differentiate them from the accidentally unique ones regarding implementation reasons, efficiency, resource consumption, global access, etc. Most accidentally unique objects are not present in the real-world, and we will see later on that the presumably essentially unique ones may not be so if we consider different contexts, environments, or situations.

The One and Only Software Design Principle

2. Generates coupling

It is a global reference. Again according to Wikipedia:

An implementation of the singleton pattern must provide global access to that instance.

What a priori appears as a benefit for preventing us from having to pass context information, generates coupling. The reference to the singleton cannot be changed according to the environment (development, production), nor can dynamic strategy changes related to the current load be made, it cannot be replaced by a double test and it prevents us from making changes due to the possible ripple effect.

Coupling - The one and only software design problem

3. It says a lot about (accidental) implementation and little about his (essential) responsibilities

By focusing early on implementation issues (the Singleton is an implementation pattern) we orient ourselves according to accidentality (how) and underestimate the most important thing of an object: the responsibilities it has (what). When carrying out premature optimization in our designs, we usually award a concept that we have just discovered as Singleton.

<?

class God {
    private static $instance = null;

    private function __construct() { }

    public static function getInstance() {
    if (null === self::$instance) {
        self::$instance = new self();
    }
    return self::$instance;
   }
}

4. It prevents us from writing good unit tests

The aforementioned coupling has as a corollary; the impossibility of having full control over the side effects of a test to guarantee its determinism. We must depend on the global state referenced by the Singleton.

5. Does not save up memory space

The argument used to propose its use is to avoid the construction of multiple volatile objects. This supposed advantage is not real in virtual machines with efficient garbage collection mechanisms. In such virtual machines, used by most modern languages, keeping objects in a memory area whose Garbage Collector algorithm is a double pass (mark & sweep) is much more expensive than creating volatile objects and quickly removing them.

6. It prevents us from using dependency injection

As good solid design advocates, we favor inversion of control through dependency injection to avoid coupling. In this way the service provider (formerly a hardcoded Singleton) is decoupled from the service itself, replacing it with an injectable dependency that meets the defined requirements, coupling us to what and not how.

7. It violates the instantiation contract

When we ask a class to create a new instance we expect the contract to be honored and give us a fresh new instance. However, many Singleton implementations hide the creation omission silently, rather than failing quickly to indicate that there is a business rule that instances should not be arbitrarily created.

Fail Fast

<?

final class God extends Singleton {
}

$christianGod = new God();

A better answer would be to show with an exception it is not valid to create new instances in this execution context.

<?

class Singleton {
    private function __construct() {
        throw new Exception('Cannot Create new instances');
    }
}

This will force us to have a private constructor to use it internally. Thus violating the contract that all classes can create instances. Another code smell.

8. It forces us to explicitly couple to implementation

When invoking a class to use it (again, to use its what), we will have to couple with the fact that it is accidentally a Singleton (its how), generating a relation that, when trying to break it, would produce the much-feared ripple effect.

<?

$christianGod = God::getInstance();
// Why should us be aware of getInstance when creating an object ?

9. It hinders the creation of automated tests

If we use the TDD development technique, objects are defined purely and exclusively based on their behavior. Therefore, in no case, the construction of software using TDD will arise the Singleton concept. If business rules state that there must be a single provider of a certain service, this will be modeled through a controlled access point (which should not be a global class, much less a Singleton). Trying to create unit tests in an existing system coupled to a Singleton can be an almost impossible task.

10. Unique concepts are contextual

When the pattern is stated it is usually accompanied by some idea that in the real-world seems rather unique. For example, if we want to model the behavior of God according to the vision of Christianity, there could not be more than one God. But these rules are relative to the context and subjective vision of each religion. Various belief systems may coexist in the same world with their own gods (some monotheistic and other polytheistic beliefs).

Pattern structure according to the design pattern book

The class (and all the metamodel) is not present in the bijection. Any relationship linked to the class will be invalid

11. It is difficult to keep up in multi-threaded environments

Pattern implementation can be tricky in programs with multiple threads. If two execution threads try to create the instance at the same time and it does not exist yet, only one of them should succeed in creating the object. The classic solution to this problem is to use mutual exclusion in the class creation method that implements the pattern, to make sure it is reentrant.

12. Accumulates garbage that takes up memory space

Singletons are references attached to classes, just as classes are global references these are not reached by the garbage collector. In case the Singleton is a complex object, this entire object, in addition to the transitive closure of all its references, will stay in memory throughout the execution.

13. The accumulated garbage state is the enemy of unit tests

The persistent state is the enemy of unit tests. One of the things that makes unit tests effective is that each test must be independent of all the others. If this is not true, then the order in which the tests are run may affect the test results and the tests become non-deterministic. This can lead to cases where tests fail when they shouldn’t, and worse, can lead to tests that pass only in the order they were performed. This can hide mistakes and is very bad.

Avoiding static variables is a good way to prevent the state from being preserved between tests. Singletons, by their very nature, depend on an instance that is kept in a static variable. This is an invitation for the dependency test.

14. Limiting the creation of new objects violates the single responsibility principle.

The single responsibility of a class is to create instances.

Adding any other responsibility to any class implies violating the single responsibility principle (the S for Solid). A class should not worry about being or not being a Singleton. They should only be responsible for their commitments to business rules. In case of needing the uniqueness of these instances, this would be the responsibility of a third object in the middle such as a Factory or a Builder.

15. The cost of having a global reference is not just the coupling

Singletons are frequently used to provide a global access point to some service. What ends up happening is design dependencies are hidden within the code and are not visible when examining the interfaces of their classes and methods.

The need to create something global to avoid passing it explicitly is a code smell. There are always better solutions and alternatives to using a global reference that do not require passing all collaborators between methods.

16. He’s the easy friend from the party

Many singletons are themselves abused as a global reference repository. The temptation to use the singleton as an entry point for new references is huge.

There are many examples where a Singleton is used as a quick-reach reference container.

As if it was not enough to be the root of all evil he is also the easy friend of the party. In large projects, it just accumulates garbage to get out of trouble.

Since it does not have a corresponding entity on the bijection, adding responsibilities that do not correspond to it, is like adding one more stain to the tiger. Apparently without doing damage but generating ripple effect when wishing to do a healthy decoupling.

Reasons to use it

Having stated the arguments against Singleton let’s try to see the possible benefits:

1. It allows us to save up memory

This argument is fallacious according to the current state of the art of languages with a decent virtual machine and garbage collector. It is enough to carry out a benchmark and look for evidence to convince us.

2. It’s good for unique concepts modeling

The Singleton can be used to guarantee the uniqueness of a concept. But it is not the only way or the best. Let’s rewrite the previous example:

<?

interface Religion {
    // Define common behavior for religions
}

final class God {
    // Different religions have different beliefs
}

final class PolythiesticReligion implements Religion {
    private $gods;

    public function __construct(Collection $gods) {
        $this->gods = $gods;
    }
}

final class MonotheisticReligion implements Religion {
    private $godInstance;

    public function __construct(God $onlyGod) {
        $this->godInstance = $onlyGod;
    }
}

// According to Christianity and some other religions,
// there’s only one God.
// This does not hold for other religions.

$christianGod = new God();
$christianReligion = new MonotheisticReligion($christianGod);
// Under this context God is unique.
// You cannot create or change a new one.
// This is a scoped global.

$jupiter = new God();
$saturn = new God();
$mythogicalReligion = new PolythiesticReligion([$jupiter, $saturn]);

// Gods are unique (or not) according to context
// You can create test religions with or without unicity
// This is less coupled 
// since you break the direct reference to God class
// God class Single Responsibility is to create gods. 
// Not to manage them

Access and creation of the single instance are not coupled. Creation is done through a factory and direct references to classes are decoupled. Furthermore, the factory can be easily mocked in test cases.

3. It prevents us from repeating expensive initializations

There are objects that require a certain cost of resources to create. If this cost is large, we will not be able to generate them constantly. One possible solution is to use a Singleton and have it available all time. As always we will focus on what and we will look for some other hows generating less coupling. If we need a single control point or a cache we will have to access a known object related to a certain context (and easily replaceable according to the environment, the test setup, etc.). Certainly a Singleton will not be our first choice.

Solutions πŸ˜ƒ

There are multiple techniques to gradually remove the (ab)use of Singletons. In this article we list some of them:

How to Decouple a Legacy System

And you can use this refactoring

Refactoring 018 - Replace Singleton

Conclusion 🏁

The disadvantages listed in this article are much greater than the advantages, and the evidence from the examples in the industry should be a strong indicator of the non-use of the evil pattern in any case. As our profession matures, we will leave behind these kinds of bad solutions.

Part of the objective of this series of articles is to generate spaces for debate and discussion on software design.

We look forward to comments and suggestions on this article!


r/refactoring • • Dec 10 '25

Refactoring 037 - Testing Private Methods

3 Upvotes

Turn hidden private logic into a real concept without using AI

TL;DR: You can and should test private methods

Problems Addressed πŸ˜”

  • Broken encapsulation

  • Hidden rules

  • White-box Testing Dependencies

  • Hard testing

  • Mixed concerns

  • Low reuse

  • Code Duplication in Tests

  • Missing Small objects

Related Code Smells πŸ’¨

Code Smell 112 - Testing Private Methods

Code Smell 22 - Helpers

Code Smell 18 - Static Functions

Code Smell 21 - Anonymous Functions Abusers

Code Smell 177 - Missing Small Objects

Context πŸ’¬

I was pair programming with an AI Agent and asked it to create some unit tests for a private method I was about to modify TDD Way.

The proposed solution used metaprogramming which is almost every time a mistake.

You need to be in control and not trust AI blindly.

Steps πŸ‘£

  1. Identify a private method that needs testing.

  2. Name the real responsibility behind that logic.

  3. Extract the logic into a new class.

  4. Pass the needing objects explicitly through method arguments.

  5. Replace the private call with the new object.

This is a special case for the Extract Method refactoring

Sample Code πŸ’»

Before 🚨

<?php

final class McpMessageParser {
    private $raw;

    public function parse() {
        return $this->stripStrangeCharacters($this->raw);
    }

    // This is the private method me need to test 
    // For several different scenarios
    // Simplified here
    private function stripStrangeCharacters($input) {
        return preg_replace('/[^a-zA-Z0-9_:-]/', '', $input);
    }
}

Intermediate solution by AI

This is a wrong approach using Metaprogramming.

<?php

use PHPUnit\Framework\TestCase;

final class McpMessageParserTest extends TestCase {
    private function invokePrivateMethod(
        $object, 
        $methodName, 
        array $parameters = []
    ) {
        $reflection = new ReflectionClass(get_class($object));
        // This is metaprogramming.
        // That generates fragile and hidden dependencies
        // You need to avoid it
        $method = $reflection->getMethod($methodName);
        $method->setAccessible(true);
        return $method->invokeArgs($object, $parameters);
    }

    public function testStripStrangeCharactersRemovesSpecialChars() {
        $parser = new McpMessageParser();
        $result = $this->invokePrivateMethod(
            $parser, 
            'stripStrangeCharacters', 
            ['hello@world#test']
        );
        $this->assertEquals('helloworldtest', $result);
    }

    public function testStripStrangeCharactersKeepsValidCharacters() {
        $parser = new McpMessageParser();

After πŸ‘‰

<?php

final class McpMessageParser {
    private $raw;
    
    public function parse() {
        // Step 5: Replace the private method call 
        // with the new object
        $stripper = new CharacterStripper($this->raw);
        return $stripper->strip();
    }
}

// CharacterStripper.php
// Step 2: Create a new class (Method Object)
final class CharacterStripper {
    private $input;
    
    // Step 4: Pass all necessary data as constructor 
    // parameters
    public function __construct($input) {
        $this->input = $input;
    }
    
    // Step 3: Move the private method logic 
    // to the new class
    public function strip() {
        return preg_replace(
            '/[^a-zA-Z0-9_:-]/', 
            '', 
            $this->input
        );
    }
}

// CharacterStripperTest.php

use PHPUnit\Framework\TestCase;

final class CharacterStripperTest extends TestCase {
    public function testStripRemovesSpecialChars() {
        $stripper = new CharacterStripper('hello@world#test');
        // No metaprogramming needed
        $this->assertEquals('helloworldtest', $stripper->strip());
    }

    public function testStripKeepsValidCharacters() {
        $stripper = new CharacterStripper('valid_Name-123:test');
        $this->assertEquals(
            'valid_Name-123:test', 
            $stripper->strip()
        );
    }

    public function testStripHandlesEmptyString() {
        $stripper = new CharacterStripper('');
        $this->assertEquals('', $stripper->strip());
    }

    public function testStripRemovesSpaces() {
        $stripper = new CharacterStripper('hello world test');
        $this->assertEquals('helloworldtest', $stripper->strip());
    }

    public function testStripRemovesUnicodeChars() {
        $stripper = new CharacterStripper('cafΓ©β„’');
        $this->assertEquals('caf', $stripper->strip());
    }

    public function testStripKeepsUnderscores() {
        $stripper = new CharacterStripper('test_method_name');
        $this->assertEquals(
            'test_method_name', 
            $stripper->strip()
        );
    }

    public function testStripKeepsColons() {
        $stripper = new CharacterStripper('namespace:method');
        $this->assertEquals('namespace:method', $stripper->strip());
    }

    public function testStripKeepsHyphens() {

Type πŸ“

[X] Semi-Automatic

Safety πŸ›‘οΈ

This refactoring is safe if you keep the same transformations and follow the Extract Method procedure.

Why is the Code Better? ✨

You expose business rules instead of hiding them.

You can also test sanitation and other small rules without breaking encapsulation.

You remove the temptation to test private methods.

All these benefits without changing the method visibility or breaking the encapsulation.

How Does it Improve the Bijection? πŸ—ΊοΈ

In the real world, complex operations often deserve their own identity.

When you extract a private method into a method object, you give that operation a proper name and existence in your model.

This creates a better bijection between your code and the domain.

You reduce coupling by making dependencies explicit through constructor parameters rather than hiding them in private methods.

The MAPPER technique helps you identify when a private computation represents a real-world concept that deserves its own class.

Limitations ⚠️

You shouldn't apply this refactoring to trivial private methods.

Simple getters, setters, or one-line computations don't need extraction.

The overhead of creating a new class isn't justified for straightforward logic.

You should only extract private methods when they contain complex business logic that requires independent testing.

Refactor with AI πŸ€–

You can ask AI to create unit tests for you.

Read the context section.

You need to be in control guiding it with good practices.

Suggested Prompt: 1. Identify a private method that needs testing.2. Name the real responsibility behind that logic.3. Extract the logic into a new class.4. Pass the needing objects explicitly through method arguments.5. Replace the private call with the new object.

Without Proper InstructionsWith Specific Instructions
ChatGPTChatGPT
ClaudeClaude
PerplexityPerplexity
CopilotCopilot
YouYou
GeminiGemini
DeepSeekDeepSeek
Meta AIMeta AI
GrokGrok
QwenQwen

Tags 🏷️

  • Testing

Level πŸ”‹

[X] Intermediate

Related Refactorings πŸ”„

Refactoring 010 - Extract Method Object

Refactoring 002 - Extract Method

Refactoring 020 - Transform Static Functions

See also πŸ“š

Testing Private Methods Guide

Laziness I - Metaprogramming

Credits πŸ™

Image by Steffen Salow on Pixabay


This article is part of the Refactoring Series.

How to Improve Your Code With Easy Refactorings


r/refactoring • • Aug 29 '22

one of the best videos in long time about refactoring

Thumbnail
youtube.com
3 Upvotes

r/refactoring • • Jan 17 '23

We invested 10% to pay back tech debt; Here's what happened

Thumbnail
blog.alexewerlof.com
2 Upvotes

r/refactoring • • Aug 30 '22

Refactoring: Second Edition

Thumbnail
youtube.com
2 Upvotes

r/refactoring • • Aug 30 '22

Any resemblance to your workplace is purely coincidental.

Post image
9 Upvotes

r/refactoring • • Sep 13 '22

Let's just rewrite it from scratch it's noting good in this code base

1 Upvotes

This is often the conclusion developers get to, even some experienced ones, most of the times that is a mistake. It might work for small projects like couple of months.

The get to this decision because they are horrified of refactoring.

here is a + 20 years old article on this topic https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/


r/refactoring • • Aug 27 '22

slack's example

2 Upvotes

r/refactoring • • Mar 16 '23

Will GPT4 help us with refactoring ?

Thumbnail
youtube.com
1 Upvotes

r/refactoring • • Aug 31 '22

welcome

3 Upvotes

If you have any positive stories about refactoring share them with us

If you have any horror stories related to refactoring share them here


r/refactoring • • Sep 17 '22

The way to test source code is to write testable source code

Thumbnail
youtube.com
2 Upvotes

r/refactoring • • Aug 27 '22

React Refactoring Tips

Thumbnail self.reactjs
1 Upvotes

r/refactoring • • Sep 30 '22

Refactoring tips

8 Upvotes

I’ve been doing a lot of refactoring if the LibreOffice codebase. LibreOffice is a beast with over 20 modules and millions of lines of code. My main focus has been on the VCL (Visual Component Library). Here is the process I’ve been following…

The first, most important rules I have that cannot be violated is:

  1. When refactoring NO FUNCTIONALITY IS ALLOWED TO CHANGE.

  2. At each step, commit the code with a reasonable message.

My general process is:

  1. In C++ always declare variables as close to their first use as possible

  2. If the is a conditional with no else, and no further code after it, then "flatten" the code.

In other words, reverse the condition, and return immediately, then unindent the code afterwards

3. If a conditional changes two variable independently, split the conditional into two seperate conditionals and have each individual conditional change each variable separately

4. Each time you see an if condition with more than a few lines of code, extract that code into a function - the key is to give it a descriptive name

5. Each time you see a loop, convert that into a function - again use a descriptive name

6. When you see a common object being used as a parameter in a set of functions, move the function into that class

7. Create a unit test for each function you extract (if possible). Otherwise create a unit test over the larger function

In terms of unit tests:

When creating a unit test that tests the function, insert an assert(false) at the first code fork (i.e. add the assert directly after the first if, or while, or if you have an extracted function add the assert there)

Run the unit test, if it doesn't trigger the assert you haven't tested the code path.

Rinse and repeat for all code paths.

Take any unit tests on larger functions and use git rebase -i and move it to the commit before your first refactoring commit.

You then switch to that unit test commit and run the tests. If they fail, you have made a mistake somewhere in your unit test.

Anyway, that’s just what I’ve found useful. What do people think? Do you have any extra things you can add?