Table of Contents
KEY TAKEAWAYS
- DRY: every piece of logic should exist in exactly one place
- Use functions to eliminate repeated logic
- Use
#defineorconstto eliminate repeated constants - DRY reduces bugs: change one place, not ten
- But do not merge code that only looks similar — it must represent the same knowledge
What Is the DRY Principle?
DRY stands for Don’t Repeat Yourself. It means: every piece of knowledge or logic should have a single, unambiguous representation in the system.When the same logic exists in multiple places, changing it requires finding and updating every copy. Miss one, and you have a bug. DRY eliminates this risk by putting shared logic in one place.Example 1: Duplicated Validation Code
Bad: Same validation logic copy-pasted in three places
int set_motor_speed(int speed) {
/* Validation - copy 1 */
if (speed 100) speed = 100;
motor_reg = speed;
return speed;
}
int set_fan_speed(int speed) {
/* Validation - copy 2 (identical logic) */
if (speed 100) speed = 100;
fan_reg = speed;
return speed;
}
int set_pump_speed(int speed) {
/* Validation - copy 3 (identical logic) */
if (speed 100) speed = 100;
pump_reg = speed;
return speed;
}Problem: If the valid range changes to 0-255, you must update three places. Miss one and one actuator will behave differently.Good: Extract shared logic into one function
int clamp(int value, int min, int max) {
if (value max) return max;
return value;
}
int set_motor_speed(int speed) {
speed = clamp(speed, 0, 100);
motor_reg = speed;
return speed;
}
int set_fan_speed(int speed) {
speed = clamp(speed, 0, 100);
fan_reg = speed;
return speed;
}
int set_pump_speed(int speed) {
speed = clamp(speed, 0, 100);
pump_reg = speed;
return speed;
}Now the clamping logic exists in one place. Change the range once, it applies everywhere.Example 2: Repeated Struct Initialization
Bad: Copy-pasting initialization code
typedef struct {
char name[32];
int id;
int status;
int error_count;
int last_reading;
} sensor_t;
void init_sensors(void) {
/* Copy-paste for each sensor */
strcpy(sensors[0].name, "Temperature");
sensors[0].id = 1;
sensors[0].status = 0;
sensors[0].error_count = 0;
sensors[0].last_reading = 0;
strcpy(sensors[1].name, "Humidity");
sensors[1].id = 2;
sensors[1].status = 0;
sensors[1].error_count = 0;
sensors[1].last_reading = 0;
strcpy(sensors[2].name, "Pressure");
sensors[2].id = 3;
sensors[2].status = 0;
sensors[2].error_count = 0;
sensors[2].last_reading = 0;
}Good: Use a helper function
void sensor_init(sensor_t *s, const char *name, int id) {
strncpy(s->name, name, sizeof(s->name) - 1);
s->name[sizeof(s->name) - 1] = '';
s->id = id;
s->status = 0;
s->error_count = 0;
s->last_reading = 0;
}
void init_sensors(void) {
sensor_init(&sensors[0], "Temperature", 1);
sensor_init(&sensors[1], "Humidity", 2);
sensor_init(&sensors[2], "Pressure", 3);
}Adding a new field (like calibration_offset) requires changing only sensor_init, not every initialization site.DRY Also Applies To Constants
/* Bad: Same magic number in multiple places */ char buffer1[256]; char buffer2[256]; if (len > 256) return -1; /* Good: Define once, use everywhere */ #define BUFFER_SIZE 256 char buffer1[BUFFER_SIZE]; char buffer2[BUFFER_SIZE]; if (len > BUFFER_SIZE) return -1;
When NOT to Over-Apply DRY
- If two pieces of code look similar but serve different purposes, they may evolve independently — forcing them into one function creates a confusing abstraction
- Do not create a shared function for two lines of code that happen to look alike but have different meanings
- DRY is about knowledge duplication, not code that looks similar
Key Takeaways
- DRY: every piece of logic should exist in exactly one place
- Use functions to eliminate repeated logic
- Use
#defineorconstto eliminate repeated constants - DRY reduces bugs: change one place, not ten
- But do not merge code that only looks similar — it must represent the same knowledge
Example 3: Repeated Struct Initialization
In many C projects, you see the same struct initialization logic scattered across multiple functions. This is a subtle form of duplication that becomes painful when the struct changes.Bad — Copy-Pasted Initialization
void create_user_report(void) {
Report r;
r.type = REPORT_USER;
r.timestamp = time(NULL);
r.version = 2;
r.flags = FLAG_ACTIVE | FLAG_VERBOSE;
r.data = NULL;
r.size = 0;
// ... generate user report
}
void create_system_report(void) {
Report r;
r.type = REPORT_SYSTEM;
r.timestamp = time(NULL);
r.version = 2;
r.flags = FLAG_ACTIVE | FLAG_VERBOSE;
r.data = NULL;
r.size = 0;
// ... generate system report
}
void create_error_report(void) {
Report r;
r.type = REPORT_ERROR;
r.timestamp = time(NULL);
r.version = 2;
r.flags = FLAG_ACTIVE | FLAG_VERBOSE;
r.data = NULL;
r.size = 0;
// ... generate error report
}If version changes to 3 or a new required field is added, you must update every function. Miss one, and you have a bug.Good — Factory Function
Report report_create(ReportType type) {
Report r;
r.type = type;
r.timestamp = time(NULL);
r.version = 2;
r.flags = FLAG_ACTIVE | FLAG_VERBOSE;
r.data = NULL;
r.size = 0;
return r;
}
void create_user_report(void) {
Report r = report_create(REPORT_USER);
// ... generate user report
}
void create_system_report(void) {
Report r = report_create(REPORT_SYSTEM);
// ... generate system report
}One place to change when the struct evolves. Every caller gets the update automatically.When NOT to Apply DRY
DRY is powerful, but over-applying it can hurt more than help:- Coincidental similarity — Two blocks of code look the same today but serve different purposes. Merging them creates a false dependency. If they change for different reasons, keep them separate.
- Premature abstraction — Don’t extract a shared function the first time you see duplication. Wait until you see the pattern three times (the “Rule of Three”). The third occurrence confirms it’s a real pattern.
- Cross-module coupling — Sometimes two modules have similar code, but extracting a shared utility creates an unwanted dependency between them. A little duplication is better than tight coupling.

Vivek Bhageria — Lead Firmware R&D Engineer, 12+ years. Ex-Bosch (automotive powertrain), MusicTribe (real-time audio), medical devices. M.Tech BITS Pilani. I write at NerdyElectronics — practical, register-level embedded systems for engineers who want to understand what’s actually happening under the hood.







