Code Style Conventions
Convenciones de Estilo de Codigo — Guia Normativa
Sección titulada «Convenciones de Estilo de Codigo — Guia Normativa»AI Software Factory OS v7.7 — Guia Normativa
Sección titulada «AI Software Factory OS v7.7 — Guia Normativa»Version: 1.0.0 | Fecha: Marzo 2026 Aplica a: Todo codigo generado por humanos y por agentes AI Enforcement:
compliance-linter.py, reglas en.claude/rules/code_quality.md
1. PRINCIPIOS GENERALES
Sección titulada «1. PRINCIPIOS GENERALES»- Legibilidad sobre cleverness — El codigo se lee 10x mas de lo que se escribe. Preferir claridad sobre concision ingeniosa.
- Consistencia dentro del proyecto — Una convencion mediocre aplicada consistentemente es mejor que dos buenas aplicadas parcialmente.
- Codigo AI = mismo estandar — El codigo generado por agentes DEBE cumplir las mismas reglas. No hay excepcion por origen.
- MUST vs SHOULD — Las reglas marcadas MUST son obligatorias y bloquean CI. Las marcadas SHOULD son recomendadas y generan warnings.
| Nivel | Significado | Efecto en CI |
|---|---|---|
| MUST | Obligatorio | Bloquea merge si no se cumple |
| SHOULD | Recomendado | Warning en linter, no bloquea |
| MAY | Opcional | Informativo |
2. TYPESCRIPT / JAVASCRIPT
Sección titulada «2. TYPESCRIPT / JAVASCRIPT»2.1 Naming
Sección titulada «2.1 Naming»| Elemento | Convencion | Ejemplo |
|---|---|---|
| Variables / funciones | camelCase | getUserById, isActive |
| Clases / interfaces / types | PascalCase | UserService, ButtonProps |
| Constantes | UPPER_SNAKE_CASE | MAX_RETRIES, API_BASE_URL |
| Archivos de modulo | kebab-case.ts | user-service.ts, auth-middleware.ts |
| Componentes React | PascalCase.tsx | UserProfile.tsx, DataTable.tsx |
| Hooks | useNombreHook | useAuth.ts, usePagination.ts |
| Enums | PascalCase, miembros UPPER_SNAKE_CASE | enum Status { ACTIVE, INACTIVE } |
CORRECTO:
const MAX_PAGE_SIZE = 100;
interface UserFilterOptions { status: UserStatus; createdAfter?: Date;}
function findUsersByFilter(options: UserFilterOptions): Promise<User[]> { const { status, createdAfter } = options; // ...}INCORRECTO:
// Archivo: UserRepository.ts ← nombre de archivo PascalCaseconst max_page_size = 100; // ← constante en snake_case
interface userfilter { // ← interface en minusculas, sin sufijo descriptivo Status: string; // ← propiedad PascalCase, tipo string generico}
function Find_Users(s: any, d: any): any { // ← naming mixto, parametros any // ...}2.2 Funciones
Sección titulada «2.2 Funciones»| Regla | Nivel |
|---|---|
| Max 30 lineas por funcion | SHOULD |
| Max 50 lineas por funcion | MUST |
| Max 4 parametros | SHOULD (usar objeto config si mas) |
| Tipo de retorno explicito en funciones publicas | MUST |
| JSDoc en funciones exportadas | SHOULD |
| Arrow functions para callbacks | SHOULD |
| Function declarations para exports | SHOULD |
CORRECTO:
/** Calcula el precio total con impuestos y descuentos aplicados. */export function calculateTotalPrice(items: CartItem[], taxRate: number): Money { const subtotal = items.reduce((sum, item) => sum + item.price * item.quantity, 0); const tax = subtotal * taxRate; return { amount: subtotal + tax, currency: "USD" };}
// Callback con arrow functionconst activeUsers = users.filter((user) => user.isActive);INCORRECTO:
// Sin tipo de retorno, sin documentacion, demasiados parametros sueltosexport function calc(items, tax, discount, shipping, coupon, giftCard, membership) { // 60 lineas de logica mezclada... // ...imposible de entender sin leer todo}
// Function declaration para callback inlineconst activeUsers = users.filter(function filterActive(user) { return user.isActive;});2.3 Archivos
Sección titulada «2.3 Archivos»| Regla | Nivel |
|---|---|
| Max 300 lineas por archivo | SHOULD |
| Max 500 lineas por archivo | MUST |
| 1 export principal por archivo | SHOULD |
| Imports agrupados y ordenados | MUST |
Orden de imports:
// 1. Externos (node_modules)import { useState, useEffect } from "react";import { z } from "zod";
// 2. Internos (paths del proyecto)import { UserService } from "@/services/user-service";import { formatDate } from "@/utils/date-helpers";
// 3. Tiposimport type { User, UserRole } from "@/types/user";
// 4. Estilosimport styles from "./user-profile.module.css";2.4 React
Sección titulada «2.4 React»| Regla | Nivel |
|---|---|
| Function components (nunca class) | MUST |
| Props con interface + sufijo Props | MUST |
| Hooks custom en archivo separado | SHOULD |
| Memoization solo con medicion previa | SHOULD |
CORRECTO:
interface UserCardProps { user: User; onSelect: (userId: string) => void;}
export function UserCard({ user, onSelect }: UserCardProps): JSX.Element { const handleClick = () => onSelect(user.id);
return ( <div className="user-card" onClick={handleClick}> <h3>{user.name}</h3> <span>{user.email}</span> </div> );}INCORRECTO:
// Class component, props sin tipar, logica mezcladaclass UserCard extends React.Component { render() { return ( <div onClick={() => this.props.onSelect(this.props.user.id)}> <h3>{this.props.user.name}</h3> </div> ); }}2.5 Tipos
Sección titulada «2.5 Tipos»| Regla | Nivel |
|---|---|
Preferir interface sobre type (excepto unions) | SHOULD |
No usar any | MUST |
readonly para immutability | SHOULD |
| Enums sobre string literals cuando hay 4+ opciones | SHOULD |
2.6 Errores y tests
Sección titulada «2.6 Errores y tests»- Custom error classes que extiendan
Errorconnamedescriptivo. try/catchcon tipos especificos — nuncacatchgenerico sin manejo.- Logging estructurado en produccion — nunca
console.logsuelto. - Tests:
describe/it/expect, patron Arrange/Act/Assert. - Naming de tests:
"should [comportamiento] when [condicion]".
3. PYTHON
Sección titulada «3. PYTHON»3.1 Naming
Sección titulada «3.1 Naming»| Elemento | Convencion | Ejemplo |
|---|---|---|
| Variables / funciones | snake_case | get_user_by_id, is_valid |
| Clases | PascalCase | UserService, HttpClient |
| Constantes | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Archivos / modulos | snake_case.py | user_service.py, db_utils.py |
| Protected | _prefijo | _internal_cache |
| Private | __prefijo (rara vez) | __secret_key |
3.2 Funciones
Sección titulada «3.2 Funciones»| Regla | Nivel |
|---|---|
| Type hints en parametros y retorno | MUST |
| Docstrings Google style en funciones publicas | MUST |
| Max 30 lineas por funcion | SHOULD |
| Max 50 lineas por funcion | MUST |
| Max 4 parametros (usar dataclass si mas) | SHOULD |
if __name__ == "__main__": en scripts | MUST |
CORRECTO:
from dataclasses import dataclassfrom datetime import datetime
@dataclassclass UserFilter: """Filtros para busqueda de usuarios.""" status: str created_after: datetime | None = None role: str | None = None
def find_users_by_filter( db: Database, filters: UserFilter,) -> list[User]: """Busca usuarios que coincidan con los filtros dados.
Args: db: Conexion activa a la base de datos. filters: Criterios de busqueda.
Returns: Lista de usuarios que cumplen los filtros.
Raises: DatabaseError: Si la conexion falla. """ query = db.query(User).filter(User.status == filters.status) if filters.created_after: query = query.filter(User.created_at >= filters.created_after) return query.all()INCORRECTO:
# Sin type hints, sin docstring, parametros sueltos, nombre no descriptivodef find(db, s, d=None, r=None, limit=10, offset=0, sort="asc"): # 80 lineas de logica sin estructura... results = db.query("SELECT * FROM users WHERE status = '" + s + "'") print(f"Found {len(results)} users") # print en vez de logging return results3.3 Archivos
Sección titulada «3.3 Archivos»| Regla | Nivel |
|---|---|
| Max 500 lineas por archivo | SHOULD |
| Imports agrupados: stdlib, third-party, local | MUST |
No import * | MUST |
CORRECTO:
# 1. Standard libraryimport loggingfrom pathlib import Path
# 2. Third-partyimport yamlfrom pydantic import BaseModel
# 3. Localfrom .models import Userfrom .exceptions import ValidationErrorINCORRECTO:
from os import * # import wildcardimport yamlfrom .models import Userimport logging # stdlib mezclado con third-partyfrom pydantic import BaseModel3.4 Errores y tests
Sección titulada «3.4 Errores y tests»- Custom exceptions que hereden de
Exceptioncon nombres descriptivos. - Modulo
logging— nuncaprinten produccion. try/exceptcon excepciones especificas — nuncaexcept Exceptionsin re-raise.- Tests con
pytest(nounittesten proyectos nuevos). - Naming:
test_should_[comportamiento]_when_[condicion]. - Fixtures con
conftest.py.
CORRECTO:
class InsufficientFundsError(Exception): """El usuario no tiene saldo suficiente para la operacion."""
def withdraw(account: Account, amount: Decimal) -> Transaction: if account.balance < amount: raise InsufficientFundsError( f"Balance {account.balance} insuficiente para retiro de {amount}" ) return account.create_transaction(-amount)INCORRECTO:
def withdraw(account, amount): try: if account.balance < amount: raise Exception("error") # mensaje generico, clase generica return account.create_transaction(-amount) except: # bare except — atrapa TODO incluyendo KeyboardInterrupt print("algo fallo") return None # retorna None silenciosamente4. BASH / SHELL
Sección titulada «4. BASH / SHELL»4.1 Convenciones
Sección titulada «4.1 Convenciones»| Regla | Nivel |
|---|---|
set -euo pipefail despues del shebang | MUST |
Shebang: #!/usr/bin/env bash | MUST |
| Variables constantes: UPPER_SNAKE_CASE | MUST |
Variables locales: lower_snake_case con local | SHOULD |
| Funciones: snake_case | MUST |
| Archivos: kebab-case.sh | SHOULD |
| Colores definidos como constantes al inicio | SHOULD |
4.2 Estructura
Sección titulada «4.2 Estructura»Todo script DEBE seguir este orden:
#!/usr/bin/env bashset -euo pipefail
# ─── Constantes y colores ───readonly RED='\033[0;31m'readonly GREEN='\033[0;32m'readonly YELLOW='\033[1;33m'readonly NC='\033[0m'readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# ─── Funciones auxiliares ───log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" >&2; }log_error() { echo -e "${RED}[ERROR]${NC} $1" >&2; }
cleanup() { # Limpieza de archivos temporales rm -f "${tmp_file:-}"}trap cleanup EXIT
validate_input() { local input="$1" [[ -z "$input" ]] && { log_error "Input requerido"; exit 2; }}
# ─── Main ───main() { local target="${1:-}" validate_input "$target" log_info "Procesando: $target" # ...logica principal}
main "$@"INCORRECTO:
# Sin shebang, sin set -euo pipefail, sin estructuraecho "starting..."TARGET=$1if [ "$TARGET" = "" ]; then echo "need target" # no va a stderr, sin colores, sin exit codefi# logica mezclada sin funciones, variables globales sin readonlyresult=$(curl $TARGET) # variable sin comillasecho $result4.3 Errores
Sección titulada «4.3 Errores»| Exit code | Significado |
|---|---|
| 0 | Exito |
| 1 | Warning / error recuperable |
| 2 | Error fatal / input invalido |
- Mensajes de error SIEMPRE a
stderr(>&2). trappara cleanup de archivos temporales.- Comillas dobles en TODAS las expansiones de variables:
"$var", no$var.
5.1 Naming
Sección titulada «5.1 Naming»| Elemento | Convencion | Ejemplo |
|---|---|---|
| Tablas | plural, snake_case | users, ticket_events |
| Columnas | snake_case | created_at, user_id |
| Foreign keys | tabla_singular_id | user_id, order_id |
| Indices | idx_tabla_columna | idx_users_email |
| Primary keys | pk_tabla | pk_users |
| Unique constraints | uq_tabla_columna | uq_users_email |
| Foreign key constraints | fk_tabla_referencia | fk_orders_user_id |
5.2 Estructura
Sección titulada «5.2 Estructura»| Regla | Nivel |
|---|---|
| Keywords SQL en UPPERCASE | MUST |
| 1 columna por linea en CREATE TABLE | SHOULD |
| Timestamps: siempre TIMESTAMPTZ | MUST |
created_at + updated_at en tablas mutables | MUST |
| UUID sobre SERIAL para sistemas distribuidos | SHOULD |
CORRECTO:
CREATE TABLE orders ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id), status VARCHAR(20) NOT NULL DEFAULT 'pending', total_cents INTEGER NOT NULL CHECK (total_cents >= 0), notes TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now());
CREATE INDEX idx_orders_user_id ON orders(user_id);CREATE INDEX idx_orders_status ON orders(status);
SELECT o.id, o.status, u.emailFROM orders oJOIN users u ON u.id = o.user_idWHERE o.status = 'pending' AND o.created_at >= now() - INTERVAL '7 days'ORDER BY o.created_at DESC;INCORRECTO:
-- Tabla singular, sin timestamps, TIMESTAMP sin timezone, naming inconsistentecreate table order ( ID serial primary key, userId int references users(ID), -- camelCase en columna Status varchar(20), -- PascalCase total float, -- float para dinero = errores de precision created timestamp -- TIMESTAMP sin TZ, sin default, sin updated_at);
select * from order where Status = 'pending'; -- SELECT * en produccion, keywords lowercase5.3 Migraciones
Sección titulada «5.3 Migraciones»| Regla | Nivel |
|---|---|
Archivo: NNN_description.sql (up) | MUST |
Rollback: NNN_description.down.sql | MUST |
| Siempre reversibles | MUST |
| Zero-downtime: aditivo primero | SHOULD |
Estrategia zero-downtime para cambios destructivos:
- Migracion 1: Agregar columna nueva (aditivo).
- Deploy: Codigo escribe a ambas columnas.
- Migracion 2: Backfill datos.
- Deploy: Codigo lee de columna nueva.
- Migracion 3: Eliminar columna vieja.
6. YAML / OPENAPI
Sección titulada «6. YAML / OPENAPI»6.1 Convenciones YAML
Sección titulada «6.1 Convenciones YAML»| Regla | Nivel |
|---|---|
| Indentacion: 2 espacios | MUST |
| Nunca tabs | MUST |
| Strings sin comillas (excepto caracteres especiales) | SHOULD |
| Comentarios en espanol (framework) | MUST |
Separadores de seccion: # --- Seccion --- | SHOULD |
CORRECTO:
# --- Configuracion del proyecto ---project: name: mi-proyecto version: 1.0.0 # Activar monitoreo para ambientes de produccion monitoring: enabled: true interval_seconds: 30 endpoints: - /health - /metricsINCORRECTO:
# tabs, comillas innecesarias, sin estructuraproject: name: "mi-proyecto" # 4 espacios (debe ser 2) version: "1.0.0" monitoring: enabled: "true" # string en vez de boolean interval_seconds: "30" # string en vez de integer6.2 OpenAPI
Sección titulada «6.2 OpenAPI»| Regla | Nivel |
|---|---|
| Version 3.1.0 | SHOULD |
operationId en cada endpoint | MUST |
Schemas reutilizables en components/schemas/ | SHOULD |
| Ejemplos en cada schema | SHOULD |
7. TABLA RESUMEN: MUST vs SHOULD
Sección titulada «7. TABLA RESUMEN: MUST vs SHOULD»TypeScript / JavaScript
Sección titulada «TypeScript / JavaScript»| Regla | Nivel |
|---|---|
| Max 50 lineas por funcion | MUST |
| Max 500 lineas por archivo | MUST |
No any | MUST |
| Tipo de retorno explicito en exports | MUST |
| Interface con sufijo Props en React | MUST |
| Function components (no class) | MUST |
| Imports agrupados y ordenados | MUST |
| Max 30 lineas por funcion | SHOULD |
| Max 300 lineas por archivo | SHOULD |
| JSDoc en funciones exportadas | SHOULD |
readonly para immutability | SHOULD |
| 1 export principal por archivo | SHOULD |
| Regla | Nivel |
|---|---|
| Type hints en parametros y retorno | MUST |
| Docstrings Google style en funciones publicas | MUST |
| Max 50 lineas por funcion | MUST |
if __name__ == "__main__": en scripts | MUST |
No import * | MUST |
| Imports agrupados | MUST |
| Max 30 lineas por funcion | SHOULD |
| Max 500 lineas por archivo | SHOULD |
pytest sobre unittest | SHOULD |
| Regla | Nivel |
|---|---|
set -euo pipefail | MUST |
#!/usr/bin/env bash | MUST |
| Constantes UPPER_SNAKE_CASE | MUST |
| Funciones snake_case | MUST |
| Errores a stderr | MUST |
Variables locales con local | SHOULD |
| Colores como constantes | SHOULD |
| Regla | Nivel |
|---|---|
| Keywords UPPERCASE | MUST |
| TIMESTAMPTZ (no TIMESTAMP) | MUST |
created_at + updated_at en tablas mutables | MUST |
| Migraciones con rollback | MUST |
| 1 columna por linea en CREATE TABLE | SHOULD |
| UUID sobre SERIAL en distribuidos | SHOULD |
| Regla | Nivel |
|---|---|
| Indentacion 2 espacios | MUST |
| Nunca tabs | MUST |
| Comentarios en espanol (framework) | MUST |
| Strings sin comillas innecesarias | SHOULD |
8. INTEGRACION CON LINTERS Y FORMATTERS
Sección titulada «8. INTEGRACION CON LINTERS Y FORMATTERS»Tooling recomendado por lenguaje
Sección titulada «Tooling recomendado por lenguaje»| Lenguaje | Linter | Formatter | Config |
|---|---|---|---|
| TypeScript | ESLint | Prettier | .eslintrc.json + .prettierrc |
| Python | ruff | ruff format | pyproject.toml seccion [tool.ruff] |
| Bash | shellcheck | shfmt | .shellcheckrc |
| SQL | sqlfluff | sqlfluff fix | .sqlfluff |
| YAML | yamllint | yamllint | .yamllint.yaml |
Configuracion minima recomendada
Sección titulada «Configuracion minima recomendada»ESLint (TypeScript):
{ "rules": { "@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/explicit-function-return-type": "warn", "max-lines-per-function": ["warn", { "max": 50 }], "max-lines": ["warn", { "max": 500 }], "max-params": ["warn", { "max": 4 }] }}ruff (Python):
[tool.ruff]line-length = 100target-version = "py312"
[tool.ruff.lint]select = ["E", "F", "I", "N", "UP", "ANN", "S", "B"]# E=pycodestyle, F=pyflakes, I=isort, N=pep8-naming# UP=pyupgrade, ANN=annotations, S=bandit, B=bugbearshellcheck:
# En CI:shellcheck -e SC1091 scripts/*.shIntegracion con CI
Sección titulada «Integracion con CI»Todos los linters DEBEN correr en el pipeline de CI. Las reglas MUST bloquean merge; las reglas SHOULD generan warnings visibles en el PR.
# Ejemplo GitHub Actions- name: Lint TypeScript run: npx eslint . --max-warnings=0
- name: Lint Python run: ruff check . && ruff format --check .
- name: Lint Bash run: shellcheck scripts/*.sh
- name: Lint YAML run: yamllint -s .9. NOTAS PARA AGENTES AI
Sección titulada «9. NOTAS PARA AGENTES AI»Los agentes autonomos (FABs) DEBEN:
- Generar codigo que cumpla estas convenciones sin necesidad de correccion humana.
- Ejecutar linters antes de commit — el pre-commit hook valida automaticamente.
- No introducir
any,print(),console.log()en codigo de produccion. - Respetar limites de lineas — si una funcion crece mas de 30 lineas, refactorizar.
- Incluir type hints y docstrings en toda funcion publica desde la primera iteracion.
El compliance-linter.py verifica estas convenciones automaticamente en gates B (Architecture) y C (Implementation).