Mejora de Nombres y Comunicación de Intenciones
Técnicas para elegir nombres que revelen intenciones y mejoren la comprensión
Introducción¶
Los nombres son la forma más básica y fundamental de documentación en el código. Un buen nombre comunica propósito, tipo, alcance y restricciones sin necesidad de comentarios adicionales. Por el contrario, nombres pobres oscurecen la intención, aumentan la carga cognitiva y conducen a errores.
Este apunte presenta técnicas sistemáticas para mejorar los nombres en el código, desde variables y funciones hasta tipos y constantes, con el objetivo de hacer el código auto-explicativo y mantener la coherencia en todo el proyecto.
Reglas de estilo que resuelve¶
Esta refactorización no depende del gusto personal: responde a los problemas concretos que modelan las reglas de nomenclatura de la cátedra. Cuando un nombre es críptico, inconsistente, engañoso o está mal formado, hay una regla que explica por qué y una técnica de esta guía que lo corrige. La tabla siguiente mapea cada regla con el problema que modela y con la técnica que lo resuelve.
| Regla | Problema que modela | Cómo lo resuelve esta refactorización |
|---|---|---|
| 0x0101h: Los identificadores deben ser descriptivos | Identificadores sin propósito claro | Reemplaza nombres crípticos por otros que revelan intención. |
| 0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas | Variables y parámetros en otras convenciones | Unifica variables locales y argumentos en snake_case. |
| 0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE | Constantes en minúsculas o camelCase | Pasa los const a MAYUSCULAS_SNAKE_CASE. |
| 0x0104h: Los nombres de los archivos deben usar snake_case en minúsculas (sin espacios) | Archivos con mayúsculas, espacios o guiones | Renombra los archivos a snake_case en minúsculas. |
| 0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas | Funciones en PascalCase, camelCase o abreviaturas | Estandariza los nombres de función en snake_case estricto. |
| 0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_ | Globales sin static ni prefijo | Marca las globales como static o las prefija con g_. |
| 0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE | Macros #define en minúsculas o camelCase | Renombra las macros a MAYUSCULAS_SNAKE_CASE. |
| 0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ) | Identificadores con acentos o ñ | Elimina todo carácter no ASCII de los nombres. |
| 0x0109h: Prohibición de identificadores que colisionen con palabras clave o tipos estándar | Nombres que chocan con palabras clave o tipos estándar | Renombra para evitar colisiones con el lenguaje y la biblioteca. |
| 0x010Ah: Prohibición de identificadores con prefijos reservados para el compilador (__ o _[A-Z]) | Prefijos _ y __ reservados al compilador | Elimina los prefijos reservados del identificador. |
| 0x010Bh: Proporcionalidad en longitud de identificadores según su alcance | Nombres de una letra en alcance amplio o kilométricos | Ajusta la longitud del nombre a su alcance real. |
| 0x010Ch: Auditor de identificadores reservados con doble guion bajo o guion bajo inicial | __ o guion bajo inicial detectados sin auditoría | Aplica el auditor de identificadores reservados. |
| 0x010Dh: Auditor de consistencia en nombres de constantes simbólicas | Constantes simbólicas con convenciones mezcladas | Homogeneiza el estilo de todas las constantes. |
| 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n) | Sufijos numéricos (datos1, datos2) o afijos de tipo | Reemplaza los genéricos numerados por nombres con contexto. |
| 0x0110h: Los booleanos se nombran con prefijo interrogativo | Booleanos llamados flag, activo o estado | Renombra con prefijo interrogativo (es_, tiene_, puede_). |
| 0x0111h: Nombrá en positivo y evitá las dobles negaciones | Nombres negados que se combinan con ! | Formula los nombres en positivo y evita dobles negaciones. |
| 0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t | struct sin typedef ni sufijo _t | Define los tipos de estructura con typedef y sufijo _t. |
Problemas Comunes con Nombres¶
1. Nombres Crípticos o Abreviados¶
Los nombres de una o dos letras solo son tolerables en alcances muy locales; fuera de ahí violan la proporcionalidad que pide 0x010Bh: Proporcionalidad en longitud de identificadores según su alcance y sacrifican la intención que exige 0x0101h: Los identificadores deben ser descriptivos.
1 2 3 4 5 6 7 8 9// Problemático: nombres sin significado claro int d; // ¿días? ¿dinero? ¿distancia? int tmp; // temporal... ¿para qué? int x, y; // ¿coordenadas? ¿variables matemáticas? char buf[256]; // ¿buffer de qué? void proc(int n) { // ¿procesar qué? // ... }
2. Nombres Engañosos¶
Un booleano que en realidad verifica una sola condición es un nombre engañoso;
para los booleanos, 0x0110h: Los booleanos se nombran con prefijo interrogativo pide un prefijo interrogativo (es_, tiene_,
puede_).
1 2 3 4 5 6// Problemático: nombres que no reflejan el contenido real int dias_del_mes; // Contiene el número del mes, no los días char *get_usuario(); // Retorna NULL si no existe, no "obtiene" bool validar_email(char *e); // Solo verifica formato, no valida existencia // Array que NO contiene elementos de ese tipo usuario_t usuarios_activos[100]; // Puede tener usuarios inactivos
3. Inconsistencia en Nomenclatura¶
Mezclar obtener_edad, getAltura y fetch_peso viola a la vez
0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas y 0x010Dh: Auditor de consistencia en nombres de constantes simbólicas: el mismo concepto debería nombrarse con una
única convención.
1 2 3 4 5 6 7 8 9 10 11// Problemático: diferentes convenciones mezcladas int obtener_edad(); int getAltura(); int fetch_peso(); int edad_recuperar(); typedef struct { int ID; // Mayúsculas char *nombre; // minúsculas int EdadPersona; // PascalCase } usuario_t;
4. Nombres Genéricos Sin Contexto¶
calcular(int a, int b) no dice nada ni sobre la operación ni sobre los
argumentos; renombrarlos es también un deber de 0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas, y los afijos
numerados como datos1/datos2 caen bajo 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n).
1 2 3 4 5 6 7 8 9 10 11// Problemático: nombres demasiado genéricos void procesar(void *datos); int calcular(int a, int b); char *obtener_valor(); bool es_valido(); typedef struct { int valor; char *dato; void *info; } elemento_t;
5. Información de Tipo en el Nombre¶
La notación húngara y los sufijos de tipo (nombre_string, edad_entero) son
afijos redundantes que 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n) desaconseja.
1 2 3 4 5 6 7 8// Problemático: notación húngara o prefijos de tipo int iContador; char *strNombre; bool bActivo; float fPrecio; // Redundante char nombre_string[50]; int edad_entero;
6. Colisiones y Prefijos Reservados¶
1 2 3 4 5 6 7 8// Problemático: choca con palabras clave y tipos estándar int free; // función de <stdlib.h> int malloc; // función de <stdlib.h> struct open {}; // nombre de función estándar // Problemático: prefijos reservados al compilador int _contador; int __interno; double __atributo_x;
Estos nombres colisionan con lo que prohíbe 0x0109h: Prohibición de identificadores que colisionen con palabras clave o tipos estándar y usan los prefijos que 0x010Ah: Prohibición de identificadores con prefijos reservados para el compilador (__ o _[A-Z]) y 0x010Ch: Auditor de identificadores reservados con doble guion bajo o guion bajo inicial reservan a la implementación. El guion bajo inicial y el doble guion bajo son territorio del compilador o del sistema: evitalos siempre.
7. Constantes y Macros Inconsistentes¶
1 2 3 4 5// Problemático: las constantes no siguen una única convención #define maxItems 100 #define TIMEOUT 30 const double tasa_iva = 0.21; const int MAX_CONEXIONES = 5;
Los #define deben ir en MAYUSCULAS_SNAKE_CASE (0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE) y las
constantes de cualquier tipo deben ser consistentes entre sí
(0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE, 0x010Dh: Auditor de consistencia en nombres de constantes simbólicas).
8. Archivos y Variables Globales¶
Un archivo GestionUsuarios.C viola 0x0104h: Los nombres de los archivos deben usar snake_case en minúsculas (sin espacios): los nombres de archivo van
en snake_case y minúsculas (gestion_usuarios.c). Una global como
int contador; definida sin static queda expuesta a todo el programa;
0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_ exige marcarla static o prefijarla con g_
(static int contador; o int g_contador;). Además, nada justifica un
identificador con acentos o ñ (año, dirección): 0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ) los
prohíbe aunque parezcan descriptivos.
Principios para Buenos Nombres¶
1. Revelar Intención¶
El nombre debe responder: ¿qué es? ¿qué hace? ¿por qué existe?
Antes:
int d; // días transcurridos
int elapsed; // mejor, pero ¿elapsed desde cuándo?Después:
int dias_desde_ultima_modificacion;
int tiempo_transcurrido_en_milisegundos;2. Evitar Desinformación¶
No usar nombres que contradigan el contenido o comportamiento.
Antes:
1 2 3 4 5 6// Engañoso: no es una lista usuario_t lista_usuarios[100]; // Engañoso: puede retornar NULL usuario_t *get_usuario_by_id(int id); // Confuso: ¿incluye o excluye el IVA? double calcular_precio(double base);
Después:
1 2 3 4 5 6 7usuario_t usuarios[100]; int cantidad_usuarios; // Retorna NULL si no encuentra usuario_t *buscar_usuario_por_id(int id); // Explícito sobre lo que incluye double calcular_precio_sin_iva(double base); double calcular_precio_con_iva(double base);
3. Hacer Distinciones Significativas¶
Evitar variaciones arbitrarias que no comunican diferencias reales.
Antes:
1 2 3 4 5 6 7 8 9 10void copiar_cadena(char *a1, char *a2); void procesar_datos1(int *datos); void procesar_datos2(int *datos); void procesar_datos_final(int *datos); typedef struct { char *info; char *data; char *contenido; } registro_t;
Después:
1 2 3 4 5 6 7 8 9 10void copiar_cadena(const char *origen, char *destino); void validar_datos(int *datos); void transformar_datos(int *datos); void guardar_datos(int *datos); typedef struct { char *titulo; char *descripcion; char *cuerpo; } documento_t;
4. Usar Nombres Pronunciables¶
Facilita la comunicación verbal sobre el código; para eso hay que evitar caracteres no ASCII (0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ)) y elegir una longitud acorde al alcance (0x010Bh: Proporcionalidad en longitud de identificadores según su alcance).
Antes:
1 2 3 4 5 6 7struct usrmgr { int nusrs; char *usrnm[100]; int usrsts; }; int gnymdhms(void); // generation year, month, day, hour, minute, second
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16typedef struct { int cantidad_usuarios; char *nombres_usuario[100]; int estado_usuarios; } gestor_usuarios_t; typedef struct { int anio; int mes; int dia; int hora; int minuto; int segundo; } marca_temporal_t; marca_temporal_t obtener_marca_temporal_actual(void);
5. Usar Nombres Buscables¶
Evitar nombres de una sola letra excepto en contextos muy locales (0x010Bh: Proporcionalidad en longitud de identificadores según su alcance); además, los valores fijos deben extraerse a constantes con nombre en MAYUSCULAS_SNAKE_CASE (0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE).
Antes:
1 2 3 4 5 6 7 8 9 10// Difícil de buscar for (int i = 0; i < 7; i++) { double s = 0; for (int j = 0; j < n; j++) { s += a[j] * b[i]; } r[i] = s; }
Después:
1 2 3 4 5 6 7 8 9 10 11const int DIAS_SEMANA = 7; for (int dia = 0; dia < DIAS_SEMANA; dia++) { double suma_diaria = 0; for (int transaccion = 0; transaccion < cantidad_transacciones; transaccion++) { suma_diaria += montos[transaccion] * factores[dia]; } resultados[dia] = suma_diaria; }
6. Evitar Codificación de Tipo¶
No usar notación húngara ni prefijos de tipo: son afijos redundantes que rechaza
0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n). La estructura debe declararse con typedef y sufijo _t según
0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t.
Antes:
1 2 3 4 5 6 7 8int iEdad; char *strNombre; bool bActivo; float fPrecio; struct user_struct { int iId; };
Después:
1 2 3 4 5 6 7 8int edad; char *nombre; bool activo; float precio; typedef struct { int id; } usuario_t;
7. Nombres de Clases y Estructuras¶
Usar sustantivos o frases nominales y, en C, definir cada tipo con typedef y
sufijo _t (0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t).
Antes:
1 2 3 4 5 6 7 8 9 10 11 12typedef struct { // ... } procesar_t; typedef struct { // ... } manager_t; typedef struct { // ... } data_t;
Después:
1 2 3 4 5 6 7 8 9 10 11 12typedef struct { // ... } procesador_pedidos_t; typedef struct { // ... } gestor_usuarios_t; typedef struct { // ... } configuracion_sistema_t;
8. Nombres de Funciones¶
Usar verbos o frases verbales, en snake_case estricto (0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas) y con prefijo interrogativo cuando retornan un booleano (0x0110h: Los booleanos se nombran con prefijo interrogativo).
Antes:
int cuenta();
void nombre(char *n);
bool estado();Después:
int contar_elementos();
void establecer_nombre(const char *nombre);
bool esta_activo();Técnicas de Refactorización de Nombres¶
1. Renombrado de Variables¶
Las locales y los parámetros van en snake_case (0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas); los valores fijos que aparecen en el cuerpo se elevan a constantes en MAYUSCULAS_SNAKE_CASE (0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE) y las variables de poco uso no deben quedar en una sola letra (0x010Bh: Proporcionalidad en longitud de identificadores según su alcance).
Antes:
1 2 3 4 5 6 7 8void calcular() { int n = obtener_cantidad(); double p = obtener_precio(); double d = 0.15; double r = n * p * (1 - d); printf("Total: %.2f\n", r); }
Después:
1 2 3 4 5 6 7 8 9void calcular_total_con_descuento() { int cantidad_items = obtener_cantidad(); double precio_unitario = obtener_precio(); const double DESCUENTO_MAYORISTA = 0.15; double total = cantidad_items * precio_unitario * (1 - DESCUENTO_MAYORISTA); printf("Total: %.2f\n", total); }
2. Renombrado de Funciones por Intención¶
Los nombres de función siguen snake_case estricto (0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas); los predicados booleanos usan prefijo interrogativo (0x0110h: Los booleanos se nombran con prefijo interrogativo) y se formulan en positivo para no arrastrar dobles negaciones (0x0111h: Nombrá en positivo y evitá las dobles negaciones).
Antes:
1 2 3 4 5 6 7 8 9 10 11 12bool chequear(usuario_t *u) { return u->edad >= 18 && !u->bloqueado; } void hacer(pedido_t *p) { // procesar pago y envío } int get(int id) { // busca y retorna }
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13bool puede_realizar_compra(const usuario_t *usuario) { return usuario->edad >= 18 && !usuario->bloqueado; } void procesar_pedido_completo(pedido_t *pedido) { procesar_pago(pedido); iniciar_envio(pedido); } int buscar_indice_por_id(int id) { // busca y retorna índice, -1 si no encuentra }
3. Contexto en Nombres de Estructuras¶
El tipo se define con typedef y sufijo _t (0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t), y sus campos
evitan repetir el contexto del tipo o llevar prefijos innecesarios
(0x010Bh: Proporcionalidad en longitud de identificadores según su alcance).
Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16typedef struct { char *calle; char *numero; char *ciudad; char *provincia; char *codigo_postal; } direccion_t; typedef struct { char *nombre; direccion_t direccion; // Redundante con el contexto char *direccion_calle; char *direccion_numero; } cliente_t;
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15typedef struct { char *calle; char *numero; char *ciudad; char *provincia; char *codigo_postal; } direccion_t; typedef struct { char *nombre; direccion_t domicilio; // Evita redundancia } cliente_t; // Uso printf("Calle: %s\n", cliente.domicilio.calle); // Claro por contexto
4. Nombres Consistentes para Conceptos Similares¶
Un mismo concepto debe usar un mismo verbo y una misma convención (0x010Dh: Auditor de consistencia en nombres de constantes simbólicas, 0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas).
Antes:
usuario_t *obtener_usuario(int id);
producto_t *fetch_producto(int id);
pedido_t *recuperar_pedido(int id);
factura_t *get_factura(int id);Después:
1 2 3 4 5 6 7 8 9usuario_t *buscar_usuario(int id); producto_t *buscar_producto(int id); pedido_t *buscar_pedido(int id); factura_t *buscar_factura(int id); // O usando un patrón diferente pero consistente: usuario_t *obtener_usuario_por_id(int id); producto_t *obtener_producto_por_id(int id); pedido_t *obtener_pedido_por_id(int id); factura_t *obtener_factura_por_id(int id);
5. Agregar Contexto Significativo¶
Agrupar los datos en un tipo con typedef y sufijo _t (0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t) evita
variables sueltas con nombres ambiguos y da contexto a cada campo
(0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas).
Antes:
1 2 3 4 5 6 7 8 9 10 11 12void imprimir_direccion() { char *calle; char *numero; char *ciudad; // ... código printf("%s %s, %s\n", calle, numero, ciudad); } // En otro lugar char *calle; // ¿De qué? char *numero; // ¿De qué? char *ciudad; // ¿De qué?
Después:
1 2 3 4 5 6 7 8 9 10 11 12 13typedef struct { char *calle; char *numero; char *ciudad; char *provincia; char *codigo_postal; } direccion_t; void imprimir_direccion(const direccion_t *direccion) { printf("%s %s, %s\n", direccion->calle, direccion->numero, direccion->ciudad); }
Casos Prácticos Completos¶
Cada caso muestra un módulo completo antes y después. En todos ellos se resuelven
la intención de los nombres (0x0101h: Los identificadores deben ser descriptivos), la convención snake_case de
variables y funciones (0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas, 0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas), las constantes
simbólicas (0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE, 0x010Dh: Auditor de consistencia en nombres de constantes simbólicas) y la definición de tipos con sufijo
_t (0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t).
Caso 1: Sistema de Gestión de Inventario¶
Código Original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30typedef struct { int id; char *n; int q; float p; int min; int max; char *cat; } item_t; int chk(item_t *i) { return i->q >= i->min; } void upd(item_t *i, int cant) { i->q += cant; } int proc(item_t *items, int n) { int c = 0; for (int i = 0; i < n; i++) { if (!chk(&items[i])) { c++; } } return c; }
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31typedef struct { int codigo_producto; char *nombre; int cantidad_en_stock; float precio_unitario; int stock_minimo; int stock_maximo; char *categoria; } producto_t; bool tiene_stock_suficiente(const producto_t *producto) { return producto->cantidad_en_stock >= producto->stock_minimo; } void actualizar_stock(producto_t *producto, int cantidad_a_agregar) { producto->cantidad_en_stock += cantidad_a_agregar; } int contar_productos_con_stock_bajo(const producto_t *productos, int cantidad_productos) { int productos_con_stock_bajo = 0; for (int i = 0; i < cantidad_productos; i++) { if (!tiene_stock_suficiente(&productos[i])) { productos_con_stock_bajo++; } } return productos_con_stock_bajo; }
Caso 2: Procesamiento de Datos de Sensores¶
Código Original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24typedef struct { double v; long t; int s; } reading_t; double calc(reading_t *r, int n) { double sum = 0; int cnt = 0; for (int i = 0; i < n; i++) { if (r[i].s == 1) { sum += r[i].v; cnt++; } } return cnt > 0 ? sum / cnt : 0; } bool val(reading_t *r) { return r->v >= 0 && r->v <= 100 && r->s == 1; }
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35typedef enum { SENSOR_ERROR = 0, SENSOR_OK = 1, SENSOR_ADVERTENCIA = 2 } estado_sensor_t; typedef struct { double temperatura_celsius; long marca_temporal_unix; estado_sensor_t estado; } lectura_sensor_t; double calcular_temperatura_promedio(const lectura_sensor_t *lecturas, int cantidad_lecturas) { double suma_temperaturas = 0; int lecturas_validas = 0; for (int i = 0; i < cantidad_lecturas; i++) { if (lecturas[i].estado == SENSOR_OK) { suma_temperaturas += lecturas[i].temperatura_celsius; lecturas_validas++; } } return (lecturas_validas > 0) ? suma_temperaturas / lecturas_validas : 0; } bool es_lectura_valida(const lectura_sensor_t *lectura) { const double TEMPERATURA_MINIMA = 0.0; const double TEMPERATURA_MAXIMA = 100.0; return lectura->temperatura_celsius >= TEMPERATURA_MINIMA && lectura->temperatura_celsius <= TEMPERATURA_MAXIMA && lectura->estado == SENSOR_OK; }
Caso 3: Sistema de Autenticación¶
Código Original:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28typedef struct { char *u; char *p; int a; long lt; int fc; } usr_t; int auth(usr_t *u, char *p) { if (u->fc >= 3) { return -2; } if (strcmp(u->p, p) == 0) { u->fc = 0; u->lt = time(NULL); return 0; } u->fc++; return -1; } bool chk_t(usr_t *u) { long now = time(NULL); return (now - u->lt) < 3600; }
Código Refactorizado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43typedef struct { char *nombre_usuario; char *hash_password; bool activo; time_t ultimo_acceso; int intentos_fallidos; } usuario_t; typedef enum { AUTH_EXITO = 0, AUTH_PASSWORD_INCORRECTO = -1, AUTH_CUENTA_BLOQUEADA = -2, AUTH_USUARIO_INACTIVO = -3 } resultado_autenticacion_t; resultado_autenticacion_t autenticar_usuario(usuario_t *usuario, const char *password) { const int MAX_INTENTOS_FALLIDOS = 3; if (!usuario->activo) { return AUTH_USUARIO_INACTIVO; } if (usuario->intentos_fallidos >= MAX_INTENTOS_FALLIDOS) { return AUTH_CUENTA_BLOQUEADA; } if (strcmp(usuario->hash_password, password) == 0) { usuario->intentos_fallidos = 0; usuario->ultimo_acceso = time(NULL); return AUTH_EXITO; } usuario->intentos_fallidos++; return AUTH_PASSWORD_INCORRECTO; } bool sesion_esta_vigente(const usuario_t *usuario) { const long DURACION_SESION_SEGUNDOS = 3600; // 1 hora time_t tiempo_actual = time(NULL); time_t tiempo_transcurrido = tiempo_actual - usuario->ultimo_acceso; return tiempo_transcurrido < DURACION_SESION_SEGUNDOS; }
Convenciones de Nomenclatura¶
Funciones¶
Los nombres de función van en snake_case estricto (0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas) y los predicados booleanos llevan prefijo interrogativo (0x0110h: Los booleanos se nombran con prefijo interrogativo).
Verbos que indican acción:
1 2 3 4 5 6 7 8 9 10 11 12 13 14// Acciones void crear_usuario(); void eliminar_producto(); void actualizar_stock(); void guardar_en_archivo(); // Consultas int obtener_cantidad(); char *leer_linea(); double calcular_total(); // Predicados (retornan bool) bool es_valido(); bool tiene_permiso(); bool esta_vacio(); bool puede_procesar();
Variables¶
Variables locales y parámetros en snake_case (0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas), con nombres que revelan su intención (0x0101h: Los identificadores deben ser descriptivos).
Sustantivos o frases nominales:
1 2 3 4 5 6 7 8 9 10 11 12// Buenos nombres de variables int edad_usuario; double precio_total; char *nombre_completo; bool esta_activo; // Contadores y acumuladores int cantidad_elementos; int total_procesados; double suma_acumulada; // Índices y posiciones int indice_actual; int posicion_cursor;
Constantes¶
Las constantes usan MAYUSCULAS_SNAKE_CASE (0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE) de forma consistente
(0x010Dh: Auditor de consistencia en nombres de constantes simbólicas), y las macros #define siguen la misma convención
(0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE).
Mayúsculas con guiones bajos:
const int MAX_USUARIOS = 100;
const double TASA_IVA = 0.21;
const char *RUTA_CONFIGURACION = "/etc/app/config.ini";
const int TIMEOUT_CONEXION_SEGUNDOS = 30;Tipos (Estructuras y Enumeraciones)¶
Cada estructura, enumeración o puntero a función se define con typedef y sufijo
_t (0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t).
Sufijo _t para tipos:
1 2 3 4 5 6 7 8 9 10 11typedef struct { // ... } usuario_t; typedef enum { ESTADO_ACTIVO, ESTADO_INACTIVO, ESTADO_BLOQUEADO } estado_usuario_t; typedef int (*funcion_comparacion_t)(const void *, const void *);
Patrones de Nombres Específicos¶
Pares Get/Set¶
int obtener_edad(const usuario_t *usuario);
void establecer_edad(usuario_t *usuario, int edad);
char *obtener_nombre(const usuario_t *usuario);
void establecer_nombre(usuario_t *usuario, const char *nombre);Pares Create/Destroy¶
usuario_t *crear_usuario(const char *nombre);
void destruir_usuario(usuario_t *usuario);
lista_t *crear_lista(void);
void destruir_lista(lista_t *lista);Pares Open/Close¶
archivo_t *abrir_archivo(const char *ruta);
void cerrar_archivo(archivo_t *archivo);
conexion_t *abrir_conexion(const char *host);
void cerrar_conexion(conexion_t *conexion);Pares Begin/End¶
void iniciar_transaccion(void);
void finalizar_transaccion(void);
void comenzar_procesamiento(void);
void terminar_procesamiento(void);Antipatrones a Evitar¶
1. Ruido en Nombres¶
Palabras como “the”, “do” o afijos de tipo no agregan información; son los afijos genéricos que 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n) manda eliminar.
1 2 3 4 5 6 7 8 9// Problemático: palabras que no agregan información int the_numero; char *a_nombre; void do_proceso(); typedef struct { int dato_numero; char *info_cadena; } objeto_data_t;
2. Diferencias Sutiles¶
Variantes que solo cambian por un sufijo numérico o una letra son exactamente lo que 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n) y 0x010Bh: Proporcionalidad en longitud de identificadores según su alcance desaconsejan: no comunican diferencias reales.
// Problemático: demasiado similar, fácil confundir
void procesar_cliente_activo();
void procesar_clientes_activos();
usuario_t usuario;
usuario_t usuarios; // Plural solo difiere en 's'3. Nombres Dependientes del Contexto¶
Las globales con nombres genéricos exponen su significado al archivo donde se
leen; esto lo atacan 0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_ (marcarlas static o prefijarlas) y
0x010Bh: Proporcionalidad en longitud de identificadores según su alcance (darles alcance y longitud coherentes).
1 2 3 4 5 6 7 8// Problemático en diferentes archivos // archivo1.c int contador; // Cuenta usuarios // archivo2.c int contador; // Cuenta productos // Mejor: específico int contador_usuarios; int contador_productos;
Ejemplo Integrador¶
Un mismo fragmento puede violar varias reglas a la vez. El módulo siguiente comete errores de intención, convención, prefijos reservados, codificación de tipo, booleanos y tipos. La versión refactorizada los resuelve en conjunto.
❌ Antes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31#define maxUsers 50 int userCount; // global sin static ni prefijo g_ typedef struct { int idUsuario; // camelCase en campo char *nombreCompleto; int __estado; // prefijo reservado y nombre opaco int añoAlta; // carácter no ASCII } User; // sin typedef ni sufijo _t int CheckUser(User u, int idx) // PascalCase y parámetros pobres { if (!u.__estado) // doble negación { return 0; } return 1; } void ProcessAll(User users[], int n) { int i; for (i = 0; i < n; i++) { if (CheckUser(users[i], i)) { printf("%d\n", users[i].idUsuario); } } }
✅ Después:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27#define MAX_USUARIOS 50 static int g_usuarios_activos; typedef struct { int id_usuario; char *nombre_completo; bool esta_activo; int anio_alta; } usuario_t; bool usuario_esta_activo(const usuario_t *usuario) { return usuario->esta_activo; } void procesar_usuarios(const usuario_t *usuarios, int cantidad_usuarios) { for (int indice = 0; indice < cantidad_usuarios; indice++) { if (usuario_esta_activo(&usuarios[indice])) { printf("%d\n", usuarios[indice].id_usuario); } } }
Qué resolvió cada cambio:
La macro pasó de
maxUsersaMAX_USUARIOS(0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE) y la globaluserCountse reemplazó porg_usuarios_activosconstatic(0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_).El tipo
Userse convirtió enusuario_tcontypedefy sufijo_t(0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t); los camposidUsuario,nombreCompletoyañoAltaahora sonid_usuario,nombre_completoyanio_alta, sin caracteres no ASCII (0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas, 0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ)).El campo reservado
__estadodesapareció y se reemplazó por el booleano positivoesta_activo(0x010Ah: Prohibición de identificadores con prefijos reservados para el compilador (__ o _[A-Z]), 0x010Ch: Auditor de identificadores reservados con doble guion bajo o guion bajo inicial, 0x0110h: Los booleanos se nombran con prefijo interrogativo, 0x0111h: Nombrá en positivo y evitá las dobles negaciones).Las funciones
CheckUseryProcessAllse volvieronusuario_esta_activoyprocesar_usuarios(0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas), y el índice genéricoidxse convirtió enindice(0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n)).
Diagnóstico y refactorización¶
Usá esta tabla para ir del síntoma observado en el código hasta la técnica de esta guía que lo corrige.
| Regla | Síntoma en el código | Técnica de esta guía |
|---|---|---|
| 0x0101h: Los identificadores deben ser descriptivos | int d;, void proc(int n) sin significado | Renombrar revelando intención (secciones 1 a 8 de principios). |
| 0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas | Campos o parámetros en camelCase (idUsuario) | Unificar variables locales y parámetros en snake_case. |
| 0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE | const double tasa_iva = 0.21; en minúsculas | Elevar a TASA_IVA en MAYUSCULAS_SNAKE_CASE. |
| 0x0104h: Los nombres de los archivos deben usar snake_case en minúsculas (sin espacios) | GestionUsuarios.C con mayúsculas | Renombrar el archivo a gestion_usuarios.c. |
| 0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas | CheckUser, getAltura, ProcessAll | Unificar en snake_case estricto (usuario_esta_activo). |
| 0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_ | int userCount; global visible en todo el programa | Marcar static o prefijar con g_. |
| 0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE | #define maxItems 100 | Renombrar a MAX_ITEMS. |
| 0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ) | int añoAlta; con ñ | Sustituir por anio_alta. |
| 0x0109h: Prohibición de identificadores que colisionen con palabras clave o tipos estándar | int free;, struct open {}; | Renombrar para evitar palabras clave y funciones estándar. |
| 0x010Ah: Prohibición de identificadores con prefijos reservados para el compilador (__ o _[A-Z]) | int __interno;, int _contador; | Quitar los prefijos reservados al compilador. |
| 0x010Bh: Proporcionalidad en longitud de identificadores según su alcance | int i en función larga o nombre kilométrico | Ajustar la longitud del nombre a su alcance. |
| 0x010Ch: Auditor de identificadores reservados con doble guion bajo o guion bajo inicial | Auditoría de __ o guion bajo inicial | Pasar el auditor de identificadores reservados. |
| 0x010Dh: Auditor de consistencia en nombres de constantes simbólicas | tasa_iva junto a MAX_CONEXIONES | Homogeneizar la convención de constantes. |
| 0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n) | datos1, datos2, strNombre, idx | Eliminar sufijos numéricos y afijos de tipo. |
| 0x0110h: Los booleanos se nombran con prefijo interrogativo | bool activo; o bool flag; | Renombrar a esta_activo con prefijo interrogativo. |
| 0x0111h: Nombrá en positivo y evitá las dobles negaciones | if (!no_hay_error) | Formular en positivo y evitar dobles negaciones. |
| 0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t | struct user_struct { ... }; sin typedef | Definir usuario_t con typedef y sufijo _t. |
Resumen¶
Principios para buenos nombres:
Revelar Intención: El nombre debe ser auto-explicativo
Evitar Desinformación: No usar nombres engañosos
Distinciones Significativas: Diferencias claras y útiles
Pronunciables: Facilita la comunicación
Buscables: Evitar nombres de una letra en alcance amplio
Sin Codificación: No notación húngara
Sustantivos para Datos: Clases, estructuras, variables
Verbos para Acciones: Funciones
Consistencia: Mismo concepto, mismo nombre
Contexto Apropiado: Ni muy poco ni demasiado
Beneficios:
Código auto-documentado
Menor necesidad de comentarios
Más fácil de entender y mantener
Reduce errores por confusión
Facilita colaboración
Los buenos nombres son inversión, no gasto. El tiempo dedicado a elegir nombres apropiados se recupera ampliamente en mantenimiento y comprensión.
Checklist de verificación¶
¿Cada identificador revela su intención (0x0101h: Los identificadores deben ser descriptivos) y tiene una longitud proporcional a su alcance (0x010Bh: Proporcionalidad en longitud de identificadores según su alcance)?
¿Variables locales y parámetros están en snake_case (0x0102h: Los argumentos de función y las variables locales deben usar snake_case en minúsculas) y las funciones en snake_case estricto (0x0105h: Los nombres de funciones deben usar snake_case estricto en minúsculas)?
¿Las constantes
const(0x0103h: Las constantes (const o #define) deben nombrarse en MAYUSCULAS_SNAKE_CASE) y las macros#define(0x0107h: Las macros #define deben nombrarse en MAYUSCULAS_SNAKE_CASE) usan MAYUSCULAS_SNAKE_CASE de forma consistente (0x010Dh: Auditor de consistencia en nombres de constantes simbólicas)?¿Los nombres de archivo están en snake_case y minúsculas (0x0104h: Los nombres de los archivos deben usar snake_case en minúsculas (sin espacios))?
¿Toda variable global es
statico lleva prefijog_(0x0106h: Las variables globales deben ser declaradas como static o usar prefijo g_)?¿Evitaste acentos y
ñen identificadores (0x0108h: Prohibición de identificadores con caracteres no ASCII (acentos, ñ)) y colisiones con palabras clave o tipos estándar (0x0109h: Prohibición de identificadores que colisionen con palabras clave o tipos estándar)?¿Ningún nombre usa prefijos reservados con
_o__(0x010Ah: Prohibición de identificadores con prefijos reservados para el compilador (__ o _[A-Z]), 0x010Ch: Auditor de identificadores reservados con doble guion bajo o guion bajo inicial)?¿Evitaste identificadores genéricos con sufijos numéricos o afijos de tipo (0x010Eh: Prescindí de identificadores genéricos con sufijo numérico o afijos (numero1, num_1, n_a, a_n))?
¿Los booleanos llevan prefijo interrogativo (0x0110h: Los booleanos se nombran con prefijo interrogativo) y los nombres están en positivo, sin dobles negaciones (0x0111h: Nombrá en positivo y evitá las dobles negaciones)?
¿Los tipos de estructura usan
typedefy sufijo_t(0x3004h: Utilizá typedef para definir tipos de estructuras con el sufijo _t)?