Skip to content
Home » Software Design » Code Smells in C: Dispensables (Dead Code, Deep Nesting, Global Abuse)

Code Smells in C: Dispensables (Dead Code, Deep Nesting, Global Abuse)

Code Smells Dispensables featured image with dark purple background, Code Smells badge, exclamation icon, Dispensables in C subtitle in the Software Design in C series
Software Design Principles in C
Part 24 of 31View Full Path →

KEY TAKEAWAYS

  • Dispensables are unnecessary code that adds complexity without value: dead code, deep nesting, global abuse
  • Dead code (unreachable functions, unused variables) should be deleted, not commented out
  • Excessive global variables create hidden dependencies and make code harder to test and reason about
  • Reduce deep nesting with early returns, guard clauses, and function extraction

What Are Dispensable Code Smells?

Dispensables are code smells where something exists in the code that should not be there. Removing it makes the code cleaner, shorter, and easier to understand.

Smell 1: Dead Code

What it is: Code that is never executed: unreachable branches, unused functions, commented-out blocks, or variables that are written but never read.

Smelly Code

int process_value(int value) {
    int result = value * 2;

    /* Old implementation - kept "just in case" */
    // int old_result = value + 10;
    // if (old_result > 100) old_result = 100;
    // log_value(old_result);

    if (result > 0) {
        return result;
    }

    /* This code is unreachable if result is always value*2 and value > 0 */
    reset_system();  /* Never called */
    return -1;
}

/* Unused function - no one calls it */
void legacy_calibrate(void) {
    /* ... 30 lines of code no one uses ... */
}

Fix: Delete it

int process_value(int value) {
    return value * 2;
}

/* legacy_calibrate() deleted entirely */
/* Commented-out code deleted - it lives in version control if needed */
Rule: If code is not being used, delete it. Version control (Git) keeps the history. Commented-out code is not a backup strategy — it is clutter.

Smell 2: Speculative Generality

What it is: Abstractions, parameters, or hooks added “in case we need them someday” that are never actually used. This is the code equivalent of YAGNI violations.

Smelly Code

/* "Generic" message system - but only one message type is ever used */
typedef struct {
    int type;
    int priority;           /* Always 0 - never used */
    int source_id;          /* Always 1 - never used */
    int destination_id;     /* Always 0 - never used */
    uint32_t timestamp;     /* Never read */
    uint8_t flags;          /* Always 0 - never used */
    int payload;            /* The only field that matters */
} message_t;

void send_message(message_t *msg) {
    /* Ignores everything except payload */
    uart_send_int(msg->payload);
}

Fix: Keep only what is used

void send_value(int payload) {
    uart_send_int(payload);
}

/* When you actually need message routing, add the struct then */

Smell 3: Comments as Deodorant

What it is: Using comments to explain bad code instead of making the code self-explanatory. Good code rarely needs comments to explain what it does.

Smelly Code

/* Check if temperature is above maximum allowed temperature
   and if the system is not in maintenance mode and if the
   alarm has not been silenced by the user */
if (t > 80 && m == 0 && s != 1) {
    a = 1;  /* Set alarm flag to active */
    gpio_set(4);  /* Turn on the buzzer on pin 4 */
}

Fix: Make the code self-documenting

#define MAX_TEMPERATURE       80
#define PIN_BUZZER            4

int is_alarm_condition(int temp, int maintenance_mode, int alarm_silenced) {
    return (temp > MAX_TEMPERATURE)
        && (!maintenance_mode)
        && (!alarm_silenced);
}

if (is_alarm_condition(temperature, maintenance_mode, alarm_silenced)) {
    alarm_active = 1;
    gpio_set(PIN_BUZZER);
}
When comments ARE useful: explaining why (business rules, workarounds for hardware bugs, references to datasheets), not what.
/* Datasheet Section 4.2: ADC needs 12us settling time after channel switch */
delay_us(12);

Smell 4: Deep Nesting

What it is: Code with 4+ levels of indentation, making it hard to follow the logic and easy to miss edge cases.

Smelly Code

void process_packet(uint8_t *buf, int len) {
    if (buf != NULL) {
        if (len >= 4) {
            if (buf[0] == HEADER_BYTE) {
                if (validate_checksum(buf, len)) {
                    if (buf[1] < MAX_CMD) {
                        if (handlers[buf[1]] != NULL) {
                            handlers[buf[1]](&buf[2], len - 3);
                        } else {
                            log_error("No handler");
                        }
                    } else {
                        log_error("Invalid cmd");
                    }
                } else {
                    log_error("Bad checksum");
                }
            }
        }
    }
}

Fix: Use early returns (guard clauses)

void process_packet(uint8_t *buf, int len) {
    if (buf == NULL || len = MAX_CMD) {
        log_error("Invalid cmd");
        return;
    }
    if (handlers[buf[1]] == NULL) {
        log_error("No handler");
        return;
    }

    handlers[buf[1]](&buf[2], len - 3);
}
Same logic, maximum 1 level of nesting. Each guard clause eliminates one error case, and the happy path flows straight down.

Smell 5: Global Variable Abuse

What it is: Excessive use of global variables, making it impossible to track where values are changed and creating hidden dependencies between modules.

Smelly Code

/* globals.h - included by every file */
extern int temperature;
extern int humidity;
extern int motor_speed;
extern int alarm_active;
extern int system_mode;
extern int error_count;
extern int uptime_seconds;

/* Any file can read or write any of these at any time */
/* sensor.c writes temperature */
/* display.c reads temperature */
/* alarm.c reads AND writes temperature (to clear alarms) */
/* logger.c reads everything */
/* Who changed motor_speed? Good luck finding out. */

Fix: Encapsulate state in modules with accessor functions

/* sensor.c */
static int temperature = 0;  /* Private to this file */

void sensor_update(void) {
    temperature = adc_read_temperature();
}

int sensor_get_temperature(void) {
    return temperature;
}

/* motor.c */
static int speed = 0;  /* Private to this file */

void motor_set_speed(int new_speed) {
    speed = clamp(new_speed, 0, 100);
    pwm_set(speed);
}

int motor_get_speed(void) {
    return speed;
}
Now each variable has a clear owner. You can grep for motor_set_speed to find every place the speed is changed.

Summary

SmellSignFix
Dead CodeCommented-out blocks, unreachable branches, unused functionsDelete it; trust version control
Speculative GeneralityUnused parameters, hooks, or abstractions “for the future”Remove until actually needed
Comments as DeodorantComments explaining what bad code doesRename variables and extract functions
Deep Nesting4+ levels of indentationUse guard clauses (early returns)
Global Variable Abuseextern variables everywhereEncapsulate with static + getter/setter functions

Leave a Reply

Your email address will not be published. Required fields are marked *