Recognizing and Eliminating Code Smells: A Practical Guide

Mohasin Hossain
4 min readJan 20, 2024

Introduction:

Code smell refers to certain characteristics or patterns in code that indicate the presence of potential issues or inefficiencies. Identifying and addressing code smells is crucial for maintaining a clean, maintainable, and scalable codebase. In this article, we will explore some common code smells in PHP and provide examples along with solutions.

Long Methods:

Code smell: When a method becomes excessively long, it often indicates that the method is trying to do too many things at once.

// Example of a long method
class OrderProcessor {
public function processOrder($order) {
// ... many lines of code ...
}
}

Solution:

Break down the method into smaller, more focused functions with meaningful names.

class OrderProcessor {
public function processOrder($order) {
$this->validateOrder($order);
$this->calculateTotal($order);
$this->applyDiscount($order);
// ... other specific tasks ...
}

private function validateOrder($order) {
// validation logic
}

private function calculateTotal($order) {
// calculation logic
}

private function applyDiscount($order) {
// discount logic
}
}

Large Classes:

Code smell: A class with too many responsibilities and methods can become difficult to understand and maintain.

// Example of a large class
class Order {
public function process() {
// ... many lines of code ...
}

public function calculateTotal() {
// ... many lines of code ...
}

// ... other methods ...
}

Solution:

Divide the class into smaller, more focused classes, each responsible for a specific aspect of functionality.

class Order {
private $processor;
private $calculator;

public function __construct(OrderProcessor $processor, OrderCalculator $calculator) {
$this->processor = $processor;
$this->calculator = $calculator;
}

public function process() {
$this->processor->processOrder($this);
}

public function calculateTotal() {
$this->calculator->calculateTotal($this);
}
}

Duplicate Code:

Code smell: Repeated blocks of code throughout the codebase may lead to maintenance issues and bugs.

// Example of duplicate code
class UserController {
public function createUser($data) {
// ... validation logic ...
$user = new User();
$user->name = $data['name'];
$user->email = $data['email'];
$user->save();
}

public function updateProfile($data) {
// ... validation logic ...
$user = Auth::user();
$user->name = $data['name'];
$user->email = $data['email'];
$user->save();
}
}

Solution:

Extract the common functionality into separate methods or classes.

class UserController {
public function createUser($data) {
$this->validateAndSaveUser($data);
}

public function updateProfile($data) {
$this->validateAndSaveUser($data, Auth::user());
}

private function validateAndSaveUser($data, $user = null) {
// ... validation logic ...
if (!$user) {
$user = new User();
}
$user->name = $data['name'];
$user->email = $data['email'];
$user->save();
}
}

Comments Overuse:

Code smell: Excessive comments may indicate that the code is not self-explanatory.

// Example of overuse of comments
class Calculator {
// Function to add two numbers
public function add($a, $b) {
return $a + $b;
}

// Function to subtract two numbers
public function subtract($a, $b) {
return $a - $b;
}
}

Solution:

Write clean and self-explanatory code, eliminating the need for excessive comments.

class Calculator {
public function add($a, $b) {
return $a + $b;
}

public function subtract($a, $b) {
return $a - $b;
}
}

Complex Conditionals:

Code smell: When conditionals become too complex, with multiple nested conditions, it becomes challenging to understand and maintain the code.

// Example of complex conditionals
class PaymentProcessor {
public function processPayment($order) {
if ($order->isConfirmed()) {
if ($order->hasItems()) {
if ($order->isPaymentDue()) {
// Process payment
} else {
// Payment already made
}
} else {
// No items in the order
}
} else {
// Order not confirmed
}
}
}

Solution:

Simplify complex conditionals by breaking them down into smaller, more manageable pieces.

class PaymentProcessor {
public function processPayment($order) {
if (!$this->isValidOrder($order)) {
// Handle invalid order
return;
}

// Process payment
}

private function isValidOrder($order) {
return $order->isConfirmed() && $order->hasItems() && $order->isPaymentDue();
}
}

Infinite Loops:

Code smell: Infinite loops can be a serious issue, leading to application hangs or crashes.

// Example of a potential infinite loop
class TaskScheduler {
public function runTasks() {
while ($this->hasPendingTasks()) {
// Perform tasks
// ...
}
}

private function hasPendingTasks() {
// Check if there are pending tasks
// This method might not be updating the state correctly
}
}

Solution:

Ensure that loops have proper termination conditions to prevent infinite loops.

class TaskScheduler {
public function runTasks() {
$maxIterations = 1000; // Set a maximum number of iterations to prevent potential infinite loop

for ($i = 0; $i < $maxIterations && $this->hasPendingTasks(); $i++) {
// Perform tasks
// ...
}
}

private function hasPendingTasks() {
// Check if there are pending tasks
// ...
}
}

Conclusion:

By recognizing and addressing code smells in PHP, developers can enhance the readability, maintainability, and overall quality of their code. Adopting best practices and design principles, such as SOLID, can help create more robust and efficient codebases. Regular code reviews and refactoring are essential for keeping code smells at bay and ensuring the long-term success of a PHP project.

Hope you found this helpful!

--

--

Mohasin Hossain

Senior Software Engineer | Mentor @ADPList | Backend focused | PHP, JavaScript, Laravel, Vue.js, Nuxt.js, MySQL, TDD, CI/CD, Docker, Linux