Skip to content

✍️ THE ART OF COMMIT MESSAGES

El Mensaje como Literatura Técnica del Futuro

"Un commit message no es solo una descripción técnica, es una carta de amor al futuro developer que intentará entender por qué esta línea de código existe. Ese developer podrías ser tú."


📖 TABLA DE CONTENIDOS

  1. La Arqueología del Código
  2. Geometría del Mensaje Perfecto
  3. Narrativa del Body
  4. Breaking Changes
  5. Ejemplos Exemplary
  6. Anti-Patterns Visualizados
  7. Psicología del Mensaje
  8. El Arte de la Concisión

🎭 PARTE 1: LA ARQUEOLOGÍA DEL CÓDIGO

El Viaje en el Tiempo

Cada commit message es una cápsula del tiempo. Es documentación arqueológica que preserva no solo qué cambió, sino por qué existió ese cambio.

🔮 ESCENARIO FUTURO (6 meses después)

DEVELOPER (tú u otra persona):
     │ "WTF, ¿por qué esta línea?"
📜 git blame suspicious_line.cpp
     │ Muestra: commit abc123f
📖 git show abc123f
     │ Esperando: explicación clara

RAMA A: Commit message malo
─────────────────────────────
"fixed bug"

Developer response: 😤 😭 🤬
├─ ¿Qué bug?
├─ ¿Por qué este fix?
├─ ¿Hay side effects?
└─ ¿Puedo cambiar esto?

   → FRUSTRACIÓN TOTAL

RAMA B: Commit message excelente
─────────────────────────────────
"fix(dsp): prevent division by zero in filter Q calc

When Q parameter approaches zero, coefficient
calculation divided by Q causing NaN propagation
through entire audio buffer. Now clamp Q to
minimum 0.001 before calculation.

Trade-off: Slightly different filter response at
extreme low Q, but prevents audio corruption.

Fixes #789"

Developer response: 💡 😊 🎉
├─ Entiendo el problema
├─ Entiendo la solución
├─ Entiendo las consecuencias
└─ Puedo trabajar con esto

   → CLARIDAD TOTAL

Las Tres Preguntas Eternas

Cada vez que alguien lee tu commit, busca respuestas a tres preguntas fundamentales:

╔═══════════════════════════════════════════════════════════╗
║                   LAS TRES PREGUNTAS                      ║
╚═══════════════════════════════════════════════════════════╝

┌───────────────────────────────────────────────────────────┐
│  PREGUNTA 1: ¿QUÉ CAMBIÓ?                                │
├───────────────────────────────────────────────────────────┤
│                                                           │
│  Respuesta: El diff lo muestra perfectamente              │
│                                                           │
│  ┌─────────────────────────────────────────┐             │
│  │ @@ -42,7 +42,7 @@                       │             │
│  │  float calculateQ(float resonance) {    │             │
│  │ -  return resonance / 2.0;              │             │
│  │ +  return std::max(resonance, 0.001f);  │             │
│  │  }                                      │             │
│  └─────────────────────────────────────────┘             │
│                                                           │
│  Conclusión: NO GASTES PALABRAS REPITIENDO EL DIFF        │
│                                                           │
│  ❌ "Changed calculateQ to use max"                       │
│     (Ya lo veo en el código)                              │
│                                                           │
│  ✅ "Prevent Q from reaching zero"                        │
│     (Explica el POR QUÉ)                                  │
│                                                           │
└───────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────┐
│  PREGUNTA 2: ¿POR QUÉ CAMBIÓ?                            │
├───────────────────────────────────────────────────────────┤
│                                                           │
│  Respuesta: NO ES OBVIO del código                        │
│                                                           │
│  Esta es tu PRIORIDAD MÁXIMA                              │
│                                                           │
│  El código muestra CÓMO                                   │
│  Tu mensaje explica POR QUÉ                               │
│                                                           │
│  🎯 CONTEXTO                                              │
│  ├─ ¿Qué problema existía?                                │
│  ├─ ¿Cómo se manifestaba?                                 │
│  ├─ ¿Por qué ahora?                                       │
│  └─ ¿Quién lo reportó/detectó?                            │
│                                                           │
│  🎯 DECISIÓN                                              │
│  ├─ ¿Por qué esta solución específicamente?               │
│  ├─ ¿Qué alternativas consideraste?                       │
│  ├─ ¿Por qué descartaste otras opciones?                  │
│  └─ ¿Hay trade-offs?                                      │
│                                                           │
│  EJEMPLO MALO:                                            │
│  "Changed to use max function"                            │
│  → No explica nada                                        │
│                                                           │
│  EJEMPLO BUENO:                                           │
│  "Q parameter of zero caused division by zero in          │
│   coefficient calculation, propagating NaN through        │
│   audio buffer. Clamping to 0.001 prevents this while     │
│   maintaining filter behavior (at Q=0, filter is          │
│   essentially bypass anyway)."                            │
│  → Contexto + razonamiento completo                       │
│                                                           │
└───────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────┐
│  PREGUNTA 3: ¿CÓMO IMPACTA?                              │
├───────────────────────────────────────────────────────────┤
│                                                           │
│  Respuesta: Necesita contexto del sistema                 │
│                                                           │
│  🌊 ONDAS DE IMPACTO                                      │
│                                                           │
│     Commit                                                │
│       │                                                   │
│       ├──► 🎵 USUARIOS                                    │
│       │    ¿Cambia comportamiento audible?                │
│       │    ¿Rompe presets existentes?                     │
│       │    ¿Mejor/peor performance?                       │
│       │                                                   │
│       ├──► 🔧 DEVELOPERS                                  │
│       │    ¿API changes?                                  │
│       │    ¿Nueva patterns a seguir?                      │
│       │    ¿Deprecated functionality?                     │
│       │                                                   │
│       ├──► 🏗️ SISTEMA                                     │
│       │    ¿Cambios en arquitectura?                      │
│       │    ¿Nuevas dependencias?                          │
│       │    ¿Memory/CPU implications?                      │
│       │                                                   │
│       └──► 📦 DEPLOYMENT                                  │
│            ¿Requiere migration?                           │
│            ¿Backward compatible?                          │
│            ¿Config changes needed?                        │
│                                                           │
│  EJEMPLO:                                                 │
│  "Users: Filter now has minimum Q of 0.001                │
│   (inaudible difference from true zero).                  │
│   Developers: No API changes, but be aware Q is           │
│   now clamped if you're doing calculations with it.       │
│   Performance: Negligible impact (~0.01% CPU)."           │
│                                                           │
└───────────────────────────────────────────────────────────┘

El Commit Message como Contrato

Un commit message es un contrato con el futuro:

┌──────────────────────────────────────────────────────┐
│              CONTRATO TEMPORAL                        │
├──────────────────────────────────────────────────────┤
│                                                       │
│  YO, [tu nombre], en esta fecha [timestamp],          │
│  certifico que:                                       │
│                                                       │
│  1. He entendido completamente el problema            │
│  2. He considerado las alternativas                   │
│  3. He elegido esta solución conscientemente          │
│  4. He documentado las consecuencias                  │
│  5. He testeado que funciona                          │
│                                                       │
│  Y me comprometo a que cualquier persona leyendo      │
│  este mensaje en el futuro pueda:                     │
│                                                       │
│  ✅ Entender POR QUÉ este cambio existe               │
│  ✅ Evaluar si es seguro modificarlo                  │
│  ✅ Encontrar contexto adicional si es necesario      │
│  ✅ Aprender de mis decisiones                        │
│                                                       │
│  Firmado: [commit hash]                               │
│                                                       │
└──────────────────────────────────────────────────────┘

📐 PARTE 2: GEOMETRÍA DEL MENSAJE PERFECTO

Anatomía Completa

Un commit message perfecto tiene estructura de tres capas, como un edificio:

┌─────────────────────────────────────────────────────────┐
│                    ARQUITECTURA                         │
└─────────────────────────────────────────────────────────┘

    🏛️ EDIFICIO DEL COMMIT MESSAGE

    ┌───────────────────────────────────────┐
    │  🎯 SUBJECT LINE (línea 1)            │  ← FACHADA
    │  50 chars max                         │    (lo que ve)
    │  type(scope): imperative description  │    (git log)
    └───────────────────────────────────────┘

    ┌───────────────────────────────────────┐
    │                                       │
    │  📖 BODY (líneas 3+)                  │  ← INTERIOR
    │  72 chars wrap                        │    (la historia)
    │                                       │    (git show)
    │  Párrafo 1: CONTEXTO                  │
    │  ¿Por qué este cambio?                │
    │                                       │
    │  Párrafo 2: SOLUCIÓN                  │
    │  ¿Qué hiciste y por qué?              │
    │                                       │
    │  Párrafo 3: CONSECUENCIAS             │
    │  ¿Qué cambia?                         │
    │                                       │
    └───────────────────────────────────────┘

    ┌───────────────────────────────────────┐
    │  🏷️ FOOTER                             │  ← FUNDACIÓN
    │  Metadata estructurado                │    (referencias)
    │                                       │
    │  BREAKING CHANGE: ...                 │
    │  Closes #123                          │
    │  Co-authored-by: ...                  │
    └───────────────────────────────────────┘

Capa 1: Subject Line - Los Primeros 50 Caracteres

La subject line es lo que TODOS verán. Es tu elevator pitch.

¿Por qué 50 caracteres?

RAZONES TÉCNICAS Y COGNITIVAS:

📺 TERMINAL WIDTH
├─ Terminales típicamente 80 chars
├─ Git log muestra hash + espacio + subject
├─ 50 chars deja espacio cómodo
└─ Previene line wrapping

┌────────────────────────────────────────────────────┐
│ $ git log --oneline                                │
│ abc123f feat(dsp): add reverb algorithm            │
│ def456g fix(ui): prevent knob overflow             │
│ ghi789h docs(api): clarify sample rate bounds      │
│         ↑                                          │
│         50 chars fits perfectly                    │
└────────────────────────────────────────────────────┘

🖥️ GITHUB/GITLAB UI
├─ Subject line es el título
├─ 50 chars se muestra completo
├─ > 72 chars se trunca con "..."
└─ Fuerza concisión

┌────────────────────────────────────────────────────┐
│ GitHub Commit List                                 │
│ ✅ feat(dsp): add reverb algorithm                 │
│ ❌ feat(dsp): add reverb algorithm with paramete...│
│              ↑ Truncado, perdiste info             │
└────────────────────────────────────────────────────┘

🧠 COGNITIVE LOAD
├─ Humanos procesan chunks de ~7±2 items
├─ 50 chars ≈ 7-10 palabras
├─ Scannable en un vistazo
└─ Memoria de trabajo no saturada

Formato Standard: Conventional Commits

┌─────────────────────────────────────────────────────────┐
│                  ANATOMÍA DETALLADA                      │
└─────────────────────────────────────────────────────────┘

    type  (  scope  ) :   imperative subject
    ────     ─────        ─────────────────
     │         │                 │
     │         │                 │
     ▼         ▼                 ▼
  NATURALEZA DÓNDE            QUÉ HACE
  del cambio               (como comando)

EJEMPLOS DISECCIONADOS:

✅ feat(dsp): add wet/dry mix to reverb
   ────┬──── ──┬── ────────┬──────────
       │       │           │
       │       │           └─► Acción específica
       │       │               "add X to Y"
       │       │
       │       └─────────────► Scope = subsistema
       │                       (dsp, ui, core, etc)
       └─────────────────────► Type = feature nueva
                               (incrementa MINOR version)

✅ fix(vst3): prevent crash on parameter automation
   ───┬──── ───┬─── ──────────┬────────────────────
      │        │               │
      │        │               └─► Acción + contexto
      │        │                   "prevent X on Y"
      │        │
      │        └─────────────────► Plugin format específico
      └──────────────────────────► Bug fix
                                   (incrementa PATCH version)

✅ docs(api): clarify parameter range expectations
   ────┬──── ──┬── ─────────┬─────────────────────
       │       │            │
       │       │            └─► Verbo de aclaración
       │       │                "clarify X"
       │       │
       │       └──────────────► API docs específicamente
       └──────────────────────► Solo documentación
                                (no version bump)

Tipos Semánticos Completos

╔═══════════════════════════════════════════════════════════════════════════╗
║  TYPE      │ SIGNIFICADO                │ VERSION  │ CUÁNDO USAR          ║
╠═══════════════════════════════════════════════════════════════════════════╣
║                                                                           ║
║  feat      │ Nueva funcionalidad        │ MINOR    │ Add new capability   ║
║            │ visible para usuarios      │ (1.X.0)  │ User-facing feature  ║
║            │                            │          │                      ║
║  fix       │ Bug fix que afecta         │ PATCH    │ Corrige algo roto    ║
║            │ comportamiento             │ (1.0.X)  │ Soluciona issue      ║
║            │                            │          │                      ║
║  docs      │ Solo documentación         │ None     │ README, comments     ║
║            │ No cambia código           │          │ API docs, guides     ║
║            │                            │          │                      ║
║  style     │ Formato, whitespace        │ None     │ Linting fixes        ║
║            │ No cambia lógica           │          │ Code formatting      ║
║            │                            │          │                      ║
║  refactor  │ Reorganización interna     │ None*    │ Mejora estructura    ║
║            │ Sin cambio funcional       │          │ Extract, rename      ║
║            │                            │          │                      ║
║  perf      │ Performance improvement    │ PATCH    │ Optimización CPU     ║
║            │ Comportamiento idéntico    │ (1.0.X)  │ Reduce latencia      ║
║            │                            │          │                      ║
║  test      │ Añadir/mejorar tests       │ None     │ New test cases       ║
║            │ No cambia src              │          │ Test refactoring     ║
║            │                            │          │                      ║
║  build     │ Build system/tools         │ None     │ CMake, Make, scripts ║
║            │ No afecta runtime          │          │ Compiler flags       ║
║            │                            │          │                      ║
║  ci        │ CI/CD configuration        │ None     │ GitHub Actions       ║
║            │ Pipelines, automation      │          │ Jenkins, Travis      ║
║            │                            │          │                      ║
║  chore     │ Maintenance, deps          │ None     │ Update dependency    ║
║            │ No src ni test             │          │ Regenerate files     ║
║            │                            │          │                      ║
║  revert    │ Revertir commit previo     │ Depends  │ Undo bad commit      ║
║            │ Referenciar commit         │          │ Roll back change     ║
║            │                            │          │                      ║
╚═══════════════════════════════════════════════════════════════════════════╝

* refactor puede ser PATCH si mejora performance o fixes subtle bugs

Scopes para AudioLab

JERARQUÍA DE SCOPES:

🌍 TOP-LEVEL (Subsistemas principales)
├─ core       → Audio engine fundamentals
├─ dsp        → Signal processing algorithms
├─ ui         → User interface components
├─ plugin     → Plugin wrapper/framework
├─ tests      → Testing infrastructure
├─ docs       → Documentation
└─ build      → Build system

🔍 MID-LEVEL (Componentes específicos)
├─ dsp/reverb     → Reverb module specifically
├─ dsp/eq         → Equalizer
├─ dsp/compressor → Compressor
├─ ui/knobs       → Knob controls
├─ ui/meters      → Level meters
└─ plugin/vst3    → VST3 wrapper

⚙️ MICRO-LEVEL (Features muy específicas)
├─ dsp/reverb/schroeder → Schroeder algorithm
├─ ui/knobs/rotation    → Rotation behavior
└─ plugin/vst3/params   → Parameter handling

REGLA DE SCOPE:
├─ General → Específico según necesidad
├─ Si scope > 15 chars, probablemente muy específico
└─ Balance: Útil pero no verboso

La Gramática del Subject: Imperativo

¿POR QUÉ IMPERATIVO?

IMPERATIVO = Modo comando
"Add feature" no "Added feature"
"Fix bug" no "Fixed bug"

RAZÓN CONCEPTUAL:
──────────────────
El commit es una instrucción al repositorio:
"When applied, this commit will [subject line]"

✅ "When applied, this commit will add reverb algorithm"
❌ "When applied, this commit will added reverb algorithm"

RAZÓN PRÁCTICA:
───────────────
Git mismo usa imperativo:
├─ "Merge branch 'feature'"
├─ "Revert 'bad commit'"
└─ Tu mensaje debe ser consistente

VERBOS IMPERATIVOS BUENOS:
┌────────────────────────────────────────────────────┐
│  AÑADIR        │  CORREGIR      │  CAMBIAR        │
│  ───────────   │  ──────────    │  ─────────      │
│  add           │  fix           │  change         │
│  implement     │  correct       │  update         │
│  introduce     │  repair        │  modify         │
│  create        │  resolve       │  adjust         │
│                │  prevent       │  refactor       │
│                                                    │
│  ELIMINAR      │  MEJORAR       │  DOCUMENTAR     │
│  ────────      │  ────────      │  ───────────    │
│  remove        │  improve       │  document       │
│  delete        │  optimize      │  clarify        │
│  drop          │  enhance       │  explain        │
│  clean         │  refine        │  describe       │
└────────────────────────────────────────────────────┘

COMPARACIÓN:

❌ PASADO (incorrecto)
├─ "Added reverb algorithm"
├─ "Fixed crash bug"
└─ "Updated documentation"

✅ IMPERATIVO (correcto)
├─ "Add reverb algorithm"
├─ "Fix crash bug"
└─ "Update documentation"

❌ GERUNDIO (incorrecto)
├─ "Adding reverb algorithm"
├─ "Fixing crash bug"
└─ "Updating documentation"

Ejemplos de Subject Lines

✅ EXCELENTES SUBJECT LINES

feat(dsp): add Schroeder reverb with configurable room size
└─► Type + scope + acción específica + contexto key

fix(vst3): prevent crash during rapid parameter automation
└─► Problema específico + contexto de cuándo ocurre

perf(core): reduce buffer allocation overhead by 40%
└─► Mejora + métrica cuantificable

docs(api): clarify sample rate constraints for processBlock
└─► Qué se clarificó + dónde

refactor(dsp): extract coefficient calculation to separate class
└─► Qué se reorganizó + cómo

test(reverb): add edge case tests for extreme parameter values
└─► Qué tests + por qué (edge cases)

build(cmake): enable LTO for release builds
└─► Sistema + qué feature específica

───────────────────────────────────────────────────────────

⚠️ ACEPTABLES PERO MEJORABLES

feat: add reverb
└─► ¿Qué tipo de reverb? ¿Dónde?
    MEJOR: feat(dsp): add plate reverb algorithm

fix: crash bug
└─► ¿Dónde crashea? ¿Cuándo?
    MEJOR: fix(ui): prevent crash on window resize

update docs
└─► ¿Qué docs? ¿Por qué?
    MEJOR: docs(readme): add installation instructions

───────────────────────────────────────────────────────────

❌ INACEPTABLES

fixed stuff
└─► Sin type, vago, pasado

WIP
└─► No es commit final, no commitear

asdf
└─► Sin significado

john's changes
└─► Qué cambios? Nombre no ayuda

bug fixes
└─► Plural = probablemente múltiples cambios
    DIVIDIR en commits separados

Added new reverb algorithm with improved performance and...
└─► > 50 chars, se truncará
    ACORTAR: feat(dsp): add optimized reverb algorithm

Capa 2: Body - La Historia Completa

El body es donde explicas el contexto completo. Aquí no hay límite - cuenta toda la historia.

Regla de 72 Caracteres

¿POR QUÉ 72 CHARS POR LÍNEA?

📺 TERMINAL COMFORT
├─ 80 chars es standard terminal width
├─ git log indenta con 4 espacios
├─ 80 - 4 - 4 (padding) = 72
└─ Previene wrapping forzado

VISUAL:
┌──────────────────────────────────────────────────────────────────────────┐
│ $ git log                                                                │
│                                                                          │
│ commit abc123f                                                           │
│                                                                          │
│     feat(dsp): add reverb algorithm                                     │
│                                                                          │
│     Audio was sounding too dry. Users requested reverb.                 │
│     Implemented Schroeder algorithm with 4 comb filters                 │
│     and 2 allpass filters.                                              │
│     │                                                                    │
│     └─► 72 chars se muestra perfectamente                               │
│                                                                          │
│     Performance: ~3% CPU at 48kHz                                       │
│                                                                          │
└──────────────────────────────────────────────────────────────────────────┘

📖 READABILITY
├─ Líneas muy largas son difíciles de leer
├─ 72 chars es óptimo para comprensión
├─ Studies show ~65-75 chars es sweet spot
└─ Fuerza estructura en párrafos

🔀 DIFF VIEWING
├─ Diffs añaden prefijos (+ - space)
├─ 72 chars en message + diff prefix = 75-76
└─ Cabe en terminal sin wrap

Estructura del Body en Párrafos

ESTRUCTURA NARRATIVA RECOMENDADA:
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  📍 PÁRRAFO 1: CONTEXTO Y PROBLEMA                      │
│  ═══════════════════════════════                        │
│                                                         │
│  ¿Qué situación existía antes?                          │
│  ¿Qué problema se manifestaba?                          │
│  ¿Por qué necesitaba solución?                          │
│  ¿Quién lo reportó/detectó?                             │
│                                                         │
│  TEMPLATE:                                              │
│  [Problema] was occurring when [situación].             │
│  [Síntomas observados]. [Impacto en users/system].      │
│                                                         │
│  EJEMPLO:                                               │
│  Audio clicks were occurring when users stopped         │
│  playback in certain DAWs (reported by 5+ users).       │
│  Investigation revealed that some hosts call            │
│  processBlock() one final time after transport          │
│  stops, catching our engine mid-cleanup.                │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  🔧 PÁRRAFO 2: SOLUCIÓN Y RAZONAMIENTO                  │
│  ══════════════════════════════════                     │
│                                                         │
│  ¿Qué approach tomaste?                                 │
│  ¿POR QUÉ este approach específicamente?                │
│  ¿Qué alternativas consideraste?                        │
│  ¿Por qué descartaste otras opciones?                   │
│  ¿Hay trade-offs?                                       │
│                                                         │
│  TEMPLATE:                                              │
│  Implemented [solución]. This approach was chosen       │
│  because [razones]. Considered [alternativa] but        │
│  [razón para descartar].                                │
│                                                         │
│  EJEMPLO:                                               │
│  Added state tracking to detect transport stop and      │
│  gracefully fade out the last buffer. This approach     │
│  was chosen over immediate silence because it           │
│  prevents abrupt discontinuities. Considered zero-      │
│  padding entire buffer, but that doesn't work well      │
│  with effects like reverb that have natural tail.       │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📊 PÁRRAFO 3: CONSECUENCIAS E IMPACTO                  │
│  ══════════════════════════════════════                 │
│                                                         │
│  ¿Qué cambia para users?                                │
│  ¿Qué cambia para developers?                           │
│  ¿Performance implications?                             │
│  ¿Breaking changes?                                     │
│  ¿Testing realizado?                                    │
│                                                         │
│  TEMPLATE:                                              │
│  [Mejora observable]. [Performance impact].             │
│  Developers should be aware that [implicación].         │
│  Tested in [ambientes].                                 │
│                                                         │
│  EJEMPLO:                                               │
│  Playback stop is now silent in all tested DAWs.        │
│  Very slight CPU increase during stop event (~0.1%),    │
│  but well within acceptable range. Developers should    │
│  be aware that stopProcessing() callback now takes      │
│  up to one buffer length longer to complete.            │
│  Tested in: Reaper, Ableton Live, Logic Pro.           │
│                                                         │
└─────────────────────────────────────────────────────────┘

Blank Lines: El Poder del Espacio en Blanco

IMPORTANCIA DE LA LÍNEA EN BLANCO:

❌ SIN BLANK LINES (difícil de leer):
──────────────────────────────────────
feat(dsp): add reverb algorithm
Audio was dry. Users wanted reverb. Implemented
Schroeder algorithm. Uses 4 comb filters and 2
allpass filters. Performance is good. Tested in
multiple DAWs. CPU usage is low.

TODO junto = wall of text = difícil scanear

✅ CON BLANK LINES (fácil de leer):
───────────────────────────────────
feat(dsp): add reverb algorithm

Audio was sounding too dry. Multiple users
requested reverb capability.

Implemented Schroeder algorithm with 4 comb
filters feeding into 2 allpass filters. This
architecture provides natural sound with
efficient CPU usage.

Performance: ~3% CPU at 48kHz, 512 samples.
Tested in Reaper, Ableton, Logic Pro.

Párrafos separados = estructura visual clara

REGLAS DE BLANK LINES:
┌────────────────────────────────────────────────┐
│  1. Línea en blanco entre subject y body       │
│     (Git requiere esto)                        │
│                                                │
│  2. Línea en blanco entre párrafos del body    │
│     (Mejora legibilidad)                       │
│                                                │
│  3. Línea en blanco antes del footer           │
│     (Separa metadata)                          │
│                                                │
│  4. NO múltiples blank lines consecutivas      │
│     (Una es suficiente)                        │
└────────────────────────────────────────────────┘

El footer es para información máquina-parseable.

ELEMENTOS DEL FOOTER:
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  🚨 BREAKING CHANGES                                    │
│  ══════════════════                                     │
│                                                         │
│  FORMATO:                                               │
│  BREAKING CHANGE: description of what broke             │
│                                                         │
│  EJEMPLO:                                               │
│  BREAKING CHANGE: Parameter IDs changed from index      │
│  to enum. Update all setParameter calls from           │
│  setParameter(0, value) to setParameter(Param::Gain,    │
│  value)                                                 │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  🔗 ISSUE REFERENCES                                    │
│  ══════════════════                                     │
│                                                         │
│  KEYWORDS:                                              │
│  Closes #123      → Cierra issue automáticamente        │
│  Fixes #456       → Synonym de Closes                   │
│  Resolves #789    → Synonym de Closes                   │
│  Refs #321        → Menciona sin cerrar                 │
│  See also #654    → Contexto relacionado                │
│                                                         │
│  MÚLTIPLES ISSUES:                                      │
│  Closes #123, #456, #789                                │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  👥 CO-AUTHORS                                          │
│  ═════════════                                          │
│                                                         │
│  FORMATO:                                               │
│  Co-authored-by: Name <email@example.com>               │
│                                                         │
│  EJEMPLO:                                               │
│  Co-authored-by: John Smith <john@audiolab.com>         │
│  Co-authored-by: Jane Doe <jane@audiolab.com>           │
│                                                         │
│  USO: Pair programming, collaborative work              │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  ✅ REVIEWERS                                           │
│  ═══════════                                            │
│                                                         │
│  FORMATO:                                               │
│  Reviewed-by: Name <email@example.com>                  │
│                                                         │
│  USO: Quién aprobó el código                            │
│                                                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📌 CUSTOM METADATA                                     │
│  ══════════════════                                     │
│                                                         │
│  Benchmark: [results]                                   │
│  Migration-guide: [link]                                │
│  Deprecates: [old API]                                  │
│  Depends-on: #123                                       │
│                                                         │
└─────────────────────────────────────────────────────────┘

EJEMPLO COMPLETO DE FOOTER:
───────────────────────────

BREAKING CHANGE: Reverb API changed from procedural
to class-based. See migration guide at docs/migration.md

Closes #234, #567
Refs #891

Benchmark: CPU reduced from 8% to 3% @ 48kHz
Performance-test: tests/perf/reverb_benchmark.cpp

Co-authored-by: Alice Johnson <alice@audiolab.com>
Reviewed-by: Bob Williams <bob@audiolab.com>

🎭 PARTE 3: NARRATIVA DEL BODY

El Body como Storytelling

El body de un commit message es narrativa técnica. Sigue principios de storytelling:

🎬 ESTRUCTURA NARRATIVA CLÁSICA

┌────────────────────────────────────────────────────────┐
│                                                        │
│  ACTO 1: SITUACIÓN INICIAL (Setup)                    │
│  ═══════════════════════════════                       │
│                                                        │
│  Establece el mundo antes del cambio                   │
│  Introduce el problema o necesidad                     │
│  Crea contexto y urgencia                              │
│                                                        │
│  🎯 OBJETIVO: Reader entiende POR QUÉ esto importa     │
│                                                        │
│  EJEMPLO:                                              │
│  "Our reverb implementation was using a simple         │
│   delay-based approach that sounded artificial.        │
│   Multiple users reported that mixes felt lifeless     │
│   and lacked depth. Professional producers were        │
│   using external reverb plugins instead of our         │
│   built-in one, indicating a quality gap."             │
│                                                        │
│  ELEMENTOS CLAVE:                                      │
│  ✓ Estado actual ("was using simple approach")        │
│  ✓ Problema observable ("sounded artificial")         │
│  ✓ Evidencia ("users reported", "producers using      │
│    external plugins")                                  │
│  ✓ Impacto ("quality gap")                             │
│                                                        │
├────────────────────────────────────────────────────────┤
│                                                        │
│  ACTO 2: CONFLICTO Y RESOLUCIÓN (Action)              │
│  ═════════════════════════════════                     │
│                                                        │
│  Explica qué decisiones tomaste                        │
│  Muestra el journey hacia la solución                  │
│  Justifica elecciones específicas                      │
│                                                        │
│  🎯 OBJETIVO: Reader entiende POR QUÉ esta solución   │
│                                                        │
│  EJEMPLO:                                              │
│  "Researched classic reverb algorithms and selected    │
│   Schroeder model for balance of quality and CPU       │
│   efficiency. Implemented with 4 parallel comb         │
│   filters (for density) feeding 2 series allpass       │
│   filters (for diffusion).                             │
│                                                        │
│   Considered Freeverb and Dattorro models but:         │
│   - Freeverb: Higher CPU cost (~2x)                    │
│   - Dattorro: More complex to tune parameters          │
│                                                        │
│   Schroeder provides 'good enough' quality for our     │
│   use case while maintaining real-time performance."   │
│                                                        │
│  ELEMENTOS CLAVE:                                      │
│  ✓ Research realizado ("researched classic algos")    │
│  ✓ Decisión explícita ("selected Schroeder")          │
│  ✓ Razonamiento ("balance quality and CPU")           │
│  ✓ Implementación ("4 comb + 2 allpass")              │
│  ✓ Alternativas consideradas (Freeverb, Dattorro)     │
│  ✓ Por qué descartadas (CPU, complexity)              │
│  ✓ Justificación final ("good enough for use case")   │
│                                                        │
├────────────────────────────────────────────────────────┤
│                                                        │
│  ACTO 3: NUEVO EQUILIBRIO (Resolution)                │
│  ═══════════════════════════════                       │
│                                                        │
│  Describe el nuevo estado del mundo                    │
│  Explica consecuencias e impactos                      │
│  Provee validación y evidencia                         │
│                                                        │
│  🎯 OBJETIVO: Reader entiende QUÉ CAMBIÓ              │
│                                                        │
│  EJEMPLO:                                              │
│  "Reverb now sounds natural and professional.          │
│   Initial user testing (n=10 producers) rated          │
│   quality 8.5/10 vs previous 4.2/10.                   │
│                                                        │
│   Performance impact: ~3% CPU @ 48kHz, 512 samples.    │
│   Acceptable for most systems. Users with CPU          │
│   constraints can disable or reduce reverb length.     │
│                                                        │
│   Developers: New ReverbParams struct with fields      │
│   roomSize, damping, wetDryMix. All parameters         │
│   smoothed to prevent zipper noise.                    │
│                                                        │
│   Tested in: Reaper, Ableton Live 11, Logic Pro X,    │
│   FL Studio 20. No compatibility issues found."        │
│                                                        │
│  ELEMENTOS CLAVE:                                      │
│  ✓ Mejora observable ("sounds natural")               │
│  ✓ Validación cuantitativa (8.5/10 vs 4.2/10)         │
│  ✓ Performance metrics (3% CPU)                        │
│  ✓ Trade-offs transparentes (CPU constraints)         │
│  ✓ API/developer impact (ReverbParams)                 │
│  ✓ Testing realizado (4 DAWs)                          │
│                                                        │
└────────────────────────────────────────────────────────┘

Técnicas Literarias para Commit Messages

Técnica 1: Show, Don't Tell

❌ TELL (vago, no útil):
────────────────────────
"Fixed performance issue"

Reader: ¿Qué issue? ¿Cómo era antes? ¿Cuánto mejoró?

✅ SHOW (específico, medible):
──────────────────────────────
"Reduced CPU usage from 45% to 12% by caching
filter coefficient calculations instead of
recomputing every sample. Coefficients only
change when parameters change (typically < 1Hz),
so caching eliminates 99.9% of redundant work."

Reader: 💡 Ahora entiendo exactamente qué y cuánto

────────────────────────────────────────────────

❌ TELL:
────────
"Made code cleaner"

✅ SHOW:
────────
"Extracted 200-line processAudio() function into
5 focused methods: readInput(), applyGain(),
applyEffects(), mix(), writeOutput(). Each method
now has single responsibility and is independently
testable."

────────────────────────────────────────────────

❌ TELL:
────────
"Improved algorithm"

✅ SHOW:
────────
"Switched from bubble sort O(n²) to quicksort O(n log n)
for parameter event sorting. With typical event count
of ~100/buffer, this reduces sorting time from 2.5ms
to 0.08ms, making it negligible."

Técnica 2: Justificar Decisiones

Cada decisión técnica tiene alternativas. Explica por qué elegiste esta específicamente.

TEMPLATE DE JUSTIFICACIÓN:
┌────────────────────────────────────────────────────────┐
│                                                        │
│  Chose [SOLUCIÓN] because [RAZÓN PRINCIPAL].          │
│                                                        │
│  Considered alternatives:                              │
│  - [ALTERNATIVA 1]: [Por qué NO]                       │
│  - [ALTERNATIVA 2]: [Por qué NO]                       │
│                                                        │
│  Trade-offs of chosen solution:                        │
│  - [TRADE-OFF 1]                                       │
│  - [TRADE-OFF 2]                                       │
│                                                        │
└────────────────────────────────────────────────────────┘

EJEMPLO REAL:
─────────────

Chose std::vector<float> for audio buffer because it
provides dynamic sizing with acceptable performance.

Considered alternatives:
- std::array: Rejected due to fixed size at compile
  time. Our buffer size varies with host settings.
- Raw float*: Rejected due to manual memory management
  complexity and safety concerns.
- boost::circular_buffer: Rejected due to unnecessary
  dependency (we don't need circular behavior).

Trade-offs of std::vector:
- Slight allocation overhead on resize (mitigated by
  reserving capacity upfront)
- Bounds checking in debug builds (acceptable, helps
  catch bugs early)

Benchmark: std::vector vs raw pointer showed < 0.5%
difference in release builds.

Técnica 3: Anticipar Preguntas

Piensa como el futuro reader. ¿Qué preguntarías?

PREGUNTAS COMUNES Y CÓMO RESPONDERLAS:

┌────────────────────────────────────────────────────────┐
│  ❓ "¿Por qué no simplemente usar X?"                  │
├────────────────────────────────────────────────────────┤
│                                                        │
│  → Explica limitaciones de X                           │
│  → Muestra por qué no aplica a este caso               │
│                                                        │
│  EJEMPLO:                                              │
│  "Why not use std::atomic? Our audio thread runs       │
│   every ~10ms and needs to read multiple related       │
│   parameters atomically as a group. std::atomic        │
│   only protects single variables. Lock-free ring       │
│   buffer allows atomic read of entire parameter set."  │
│                                                        │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│  ❓ "¿Hay implicaciones de performance?"               │
├────────────────────────────────────────────────────────┤
│                                                        │
│  → Incluye benchmarks o estimaciones                   │
│  → Compara before/after                                │
│  → Especifica condiciones de test                      │
│                                                        │
│  EJEMPLO:                                              │
│  "Benchmark (1000 iterations, 512 samples @ 48kHz):    │
│   Before: 2.4ms avg, 3.1ms worst case                  │
│   After:  0.8ms avg, 1.2ms worst case                  │
│   Test system: i7-9700K @ 3.6GHz                       │
│   Improvement: ~67% reduction"                         │
│                                                        │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│  ❓ "¿Es esto safe para modificar después?"            │
├────────────────────────────────────────────────────────┤
│                                                        │
│  → Explica dependencies y assumptions                  │
│  → Warning sobre qué NO cambiar                        │
│                                                        │
│  EJEMPLO:                                              │
│  "IMPORTANT: Filter coefficient calculation assumes    │
│   sample rate doesn't change during processing. If     │
│   implementing variable sample rate, must recalculate  │
│   coefficients in prepareToPlay() callback."           │
│                                                        │
└────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────┐
│  ❓ "¿Cómo se testea esto?"                            │
├────────────────────────────────────────────────────────┤
│                                                        │
│  → Describe testing approach                           │
│  → Lista edge cases cubiertos                          │
│                                                        │
│  EJEMPLO:                                              │
│  "Testing approach:                                    │
│   - Unit tests: Coefficient calculation correctness    │
│   - Integration: Full audio pipeline with test signal  │
│   - Regression: Golden master comparison (tolerance    │
│     0.001 due to floating point precision)             │
│   - Edge cases: SR 44.1k-192k, buffer 32-8192 samples  │
│   All tests in tests/dsp/filter_test.cpp"             │
│                                                        │
└────────────────────────────────────────────────────────┘

Técnica 4: Usar Números y Métricas

Números convierten vaguedad en precisión.

❌ VAGO:
────────
"Made it faster"
"Reduced memory usage"
"Improved quality"

✅ ESPECÍFICO:
──────────────
"Reduced latency from 23ms to 8ms"
"Decreased memory from 150MB to 45MB (70% reduction)"
"Increased SNR from 78dB to 96dB"

TIPOS DE MÉTRICAS ÚTILES:
┌────────────────────────────────────────────────────────┐
│                                                        │
│  ⏱️ PERFORMANCE                                        │
│  ├─ CPU %: "Reduced from 12% to 4%"                    │
│  ├─ Latency: "Decreased from 15ms to 5ms"              │
│  ├─ Throughput: "Increased from 100 to 300 ops/sec"    │
│  └─ Memory: "Allocation reduced from 2MB to 512KB"     │
│                                                        │
│  📊 QUALITY                                            │
│  ├─ SNR: "Improved from 80dB to 98dB"                  │
│  ├─ THD: "Reduced from 0.5% to 0.02%"                  │
│  ├─ Bit depth: "Increased from 16-bit to 24-bit"       │
│  └─ Sample rate: "Supports up to 192kHz (was 48kHz)"   │
│                                                        │
│  📏 CODE METRICS                                       │
│  ├─ LOC: "Reduced from 500 lines to 200"               │
│  ├─ Complexity: "Cyclomatic complexity 15 → 6"         │
│  ├─ Test coverage: "Increased from 65% to 92%"         │
│  └─ Dependencies: "Removed 3 external deps"            │
│                                                        │
│  👥 USER IMPACT                                        │
│  ├─ Bug reports: "Crash reports reduced 95%"           │
│  ├─ User rating: "Average rating 3.5 → 4.7 stars"      │
│  ├─ Load time: "Plugin scan 8s → 2s"                   │
│  └─ File size: "Binary size 45MB → 12MB"               │
│                                                        │
└────────────────────────────────────────────────────────┘

CONTEXTO ES CRÍTICO:
────────────────────
No solo "3% CPU" sino "3% CPU @ 48kHz, 512 samples"
No solo "faster" sino "2.3x faster in worst case"
No solo "uses less memory" sino "reduced from 150MB to 45MB"

⚡ PARTE 4: BREAKING CHANGES

La Anatomía del Breaking Change

Un breaking change es cualquier modificación que rompe código existente de usuarios.

┌──────────────────────────────────────────────────────────┐
│              ANTES → DESPUÉS                              │
├──────────────────────────────────────────────────────────┤
│                                                           │
│  CÓDIGO USUARIO (funcionaba)                              │
│       │                                                   │
│       │  myPlugin.setParameter(0, 0.5);                   │
│       │                                                   │
│       ▼                                                   │
│   ✅ WORKS (v1.x)                                         │
│                                                           │
│       │                                                   │
│  [BREAKING CHANGE COMMIT]                                 │
│       │                                                   │
│       ▼                                                   │
│   ❌ BREAKS (v2.0)                                        │
│                                                           │
│       │  Error: setParameter expects ParamID enum        │
│       │                                                   │
│       ▼                                                   │
│  USUARIO DEBE ACTUALIZAR:                                 │
│       myPlugin.setParameter(ParamID::Gain, 0.5);          │
│                                                           │
└──────────────────────────────────────────────────────────┘

Comunicando Breaking Changes

ESTRUCTURA MANDATORIA:

BREAKING CHANGE: [Descripción concisa de QUÉ se rompió]

Migration path:
1. [Paso específico]
2. [Paso específico]
3. [Estado final]

Example:
// Old code (broken)
[código que ya no funciona]

// New code (working)
[código correcto nuevo]

Additional notes:
- [Información importante]
- [Edge cases]
- [Deadline si hay deprecation period]

───────────────────────────────────────────────────────────

EJEMPLO COMPLETO:
─────────────────

feat(api): change parameter system to strongly-typed enums

BREAKING CHANGE: Parameter IDs changed from integer
index to strongly-typed enum. All setParameter(),
getParameter(), and parameterChanged() calls must be
updated.

Migration path:
1. Replace numeric parameter indices with ParamID enum
2. Update any parameter iteration loops
3. Recompile (will get compile errors until fixed)

Example:
// Old code (broken in v2.0)
processor.setParameter(0, gainValue);
processor.setParameter(1, panValue);
for (int i = 0; i < numParams; i++) {
    processor.getParameter(i);
}

// New code (v2.0)
processor.setParameter(ParamID::Gain, gainValue);
processor.setParameter(ParamID::Pan, panValue);
for (auto param : ParamID::All) {
    processor.getParameter(param);
}

Additional notes:
- Parameter order in ParamID enum matches old indices
  for backward compatibility where possible
- New parameters added at end to minimize disruption
- Compiler will catch all errors - no silent breakage
- See migration guide: docs/migration/v1-to-v2.md

Rationale: Type safety prevents common bugs where
wrong parameter index was used. Caught 23 existing
bugs during migration of our own presets.

Closes #445

Tipos de Breaking Changes

╔═══════════════════════════════════════════════════════════╗
║                 TAXONOMÍA DE BREAKS                       ║
╠═══════════════════════════════════════════════════════════╣
║                                                           ║
║  🔴 API SIGNATURE CHANGES                                 ║
║  ├─ Function parameters changed                           ║
║  ├─ Return type changed                                   ║
║  ├─ Function renamed/removed                              ║
║  └─ Class interface changed                               ║
║                                                           ║
║  🔴 BEHAVIOR CHANGES                                      ║
║  ├─ Algorithm output different                            ║
║  ├─ Parameter ranges changed                              ║
║  ├─ Default values changed                                ║
║  └─ Side effects added/removed                            ║
║                                                           ║
║  🔴 DATA FORMAT CHANGES                                   ║
║  ├─ Preset format incompatible                            ║
║  ├─ File format version bump                              ║
║  ├─ Network protocol change                               ║
║  └─ Serialization format change                           ║
║                                                           ║
║  🔴 DEPENDENCY CHANGES                                    ║
║  ├─ Minimum version requirements                          ║
║  ├─ Removed dependency (was required)                     ║
║  ├─ New required dependency                               ║
║  └─ Platform support dropped                              ║
║                                                           ║
╚═══════════════════════════════════════════════════════════╝

Minimizando Breaking Changes

ESTRATEGIAS PARA EVOLUCIONAR SIN ROMPER:

┌─────────────────────────────────────────────────────────┐
│  ESTRATEGIA 1: DEPRECATION PATH                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  v1.0: Original API                                     │
│  v1.5: Introduce new API, deprecate old (both work)     │
│  v2.0: Remove old API (breaking, but warned)            │
│                                                         │
│  // v1.5 code:                                          │
│  [[deprecated("Use setParameter(ParamID) instead")]]    │
│  void setParameter(int index, float value);             │
│                                                         │
│  void setParameter(ParamID id, float value); // new     │
│                                                         │
│  Users get compiler warnings for entire v1.x cycle      │
│                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  ESTRATEGIA 2: ADDITIVE CHANGES                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  Añadir sin remover:                                    │
│  ✅ Add new function (old still works)                  │
│  ✅ Add optional parameter with default                 │
│  ✅ Add new class (old class still exists)              │
│                                                         │
│  // Old still works                                     │
│  void process(Buffer& buffer);                          │
│                                                         │
│  // New API available                                   │
│  void process(Buffer& buffer, ProcessContext& ctx);     │
│                                                         │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  ESTRATEGIA 3: VERSIONED APIs                           │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  namespace v1 {                                         │
│      void process(...);  // original                    │
│  }                                                      │
│                                                         │
│  namespace v2 {                                         │
│      void process(...);  // new version                 │
│  }                                                      │
│                                                         │
│  Users opt-in to new version explicitly                 │
│                                                         │
└─────────────────────────────────────────────────────────┘

🎨 PARTE 5: EJEMPLOS EXEMPLARY

Ejemplo 1: Feature Compleja

feat(reverb): implement Schroeder reverb algorithm

Current reverb implementation was a placeholder using
simple delay with feedback. Multiple users reported it
sounded "lifeless", "metallic", and "artificial" (GitHub
issues #234, #267, #298). Professional producers were
bypassing our built-in reverb in favor of external plugins,
indicating a significant quality gap.

Implemented classic Schroeder reverb algorithm, which
provides natural-sounding reverberation with efficient
CPU usage. Architecture consists of:

- 4 parallel comb filters (create initial echo density)
  Delays: 29.7ms, 37.1ms, 41.1ms, 43.7ms @ 48kHz

- 2 series allpass filters (diffuse echoes into reverb tail)
  Delays: 5ms, 1.7ms @ 48kHz

This approach was chosen after evaluating:
- Freeverb: Higher quality but 2x CPU cost
- Dattorro: Excellent for large spaces but complex tuning
- Simple feedback delay: Current implementation (inadequate)

Schroeder provides optimal balance of quality and performance
for our target use case (mixing/mastering plugins).

Parameters exposed to users:
- Room size: 0-100% (scales comb delay times)
- Damping: 0-100% (controls high frequency decay)
- Wet/Dry mix: 0-100% (blend with dry signal)

All parameters smoothed using one-pole filter (20ms time
constant) to prevent zipper noise during automation.

Performance impact:
- CPU usage: ~3% @ 48kHz, 512-sample buffers
- Latency: 5.2ms (inherent to algorithm)
- Memory: 220KB per instance (for delay buffers)

Test system: i7-9700K @ 3.6GHz, 16GB RAM

Tested extensively in:
- Reaper 6.x (Windows/Mac)
- Ableton Live 11 (Windows/Mac)
- Logic Pro X (Mac)
- FL Studio 20 (Windows)

No compatibility issues or crashes observed.

Quality validation:
- A/B tested with professional reverbs (Valhalla, FabFilter)
- User testing (n=10 professional producers)
- Average quality rating: 8.5/10 (vs previous 4.2/10)
- "Good enough for 90% of use cases" - consistent feedback

Future improvements possible:
- Early reflections (for more realistic spaces)
- Modulation (for chorus-like effects)
- Multi-band processing (frequency-dependent decay)

Reference:
Schroeder, M.R. (1962) "Natural Sounding Artificial
Reverberation" Journal of the Audio Engineering Society

Closes #234
See also #267, #298

Análisis de este ejemplo: - ✅ Contexto completo (problema con evidencia) - ✅ Decisión justificada (comparación con alternativas) - ✅ Detalles técnicos (algoritmo explicado) - ✅ Métricas cuantitativas (CPU, latency, ratings) - ✅ Testing comprehensivo (4 DAWs) - ✅ Consecuencias claras (performance, quality) - ✅ Referencias académicas - ✅ Future work mencionado

Ejemplo 2: Bug Fix Crítico

fix(vst3): prevent crash during rapid parameter automation

VST3 hosts were crashing when automating parameters at
audio rate, particularly in Ableton Live's "Link" mode
where automation can send 100+ parameter changes per buffer.

Crash reproduced consistently:
1. Create automation clip in Ableton
2. Automate any parameter with high-frequency LFO
3. Playback → crash within 5-30 seconds

Stack traces indicated race condition in parameter update
mechanism. Root cause analysis revealed:

Problem: Parameter smoothing buffer was being written by
audio thread while UI thread was reading for display updates.
No synchronization mechanism existed between threads.

Audio thread (real-time):
  for each param change:
      smoothingBuffer[paramID] = newValue  // WRITE

UI thread (60 Hz):
  for each parameter:
      displayValue = smoothingBuffer[paramID]  // READ

Unsynchronized read/write on same memory location = data
race = undefined behavior = crash.

Solution: Implemented lock-free single-producer single-
consumer (SPSC) ring buffer for parameter changes.

Architecture:
- Audio thread is sole writer
- UI thread is sole reader
- No locks required (wait-free operations)
- Uses std::atomic with memory_order_acquire/release

Flow:
Audio thread: receives param change → write to ring buffer
UI thread: periodically polls ring buffer → update display

Trade-offs:
- UI updates now lag by maximum 1 buffer duration (~10ms @ 48kHz)
- This latency is imperceptible to users
- Previous "instant" updates were race condition anyway
- Eliminates ALL synchronization overhead in audio thread

Implementation details:
- Ring buffer size: 256 entries (power of 2 for fast modulo)
- Entry contains: {paramID, value, timestamp}
- Worst case: 256 param changes can be queued
- Overflow handling: drops oldest changes (graceful degradation)

Testing methodology:
1. Stress test: 10,000 parameter changes/second for 1 hour
   Result: Zero crashes

2. Thread sanitizer validation:
   Result: No data races detected

3. Real-world testing in all major DAWs:
   - Ableton Live 11 (Link mode, max automation)
   - Bitwig Studio 4 (modulation at audio rate)
   - Reaper 6 (automation with envelope follower)
   Result: Stable in all scenarios

Performance impact:
- Ring buffer operations: ~5 CPU cycles per change
- Negligible overhead (< 0.01% CPU)
- Memory footprint: 6KB per plugin instance

Developers: If implementing new parameter display code,
use getParameterFromQueue() instead of direct access to
parameter values. See docs/threading-model.md

Verified fix with original reporters - all confirmed crash
resolved.

Closes #567
Refs #601 (related threading issue in AU)

Co-authored-by: Threading Expert <expert@audiolab.com>

Análisis: - ✅ Problema reproducible descrito - ✅ Root cause analysis detallado - ✅ Solución técnica explicada - ✅ Trade-offs transparentes - ✅ Testing riguroso - ✅ Métricas de performance - ✅ Guidance para developers

Ejemplo 3: Refactor con Propósito

refactor(core): extract audio buffer management into BufferPool

Buffer allocation and management logic was scattered across
multiple classes (AudioProcessor, EffectsChain, MixerChannel).
Each module reimplemented similar patterns with subtle
differences, leading to:

- Inconsistent alignment (some SIMD-aligned, some not)
- Redundant allocation/deallocation overhead
- Difficult to optimize globally
- Hard to debug memory issues
- Code duplication (~300 lines duplicated across 6 files)

This refactor consolidates all buffer management into a single
BufferPool class with clear responsibilities.

NO FUNCTIONAL CHANGES - pure code reorganization.
All existing tests pass without modification.
Performance metrics identical before/after.

BufferPool responsibilities:
┌─────────────────────────────────────────────────────────┐
│ 1. Allocation     → Manages memory lifecycle            │
│ 2. Alignment      → Ensures SIMD alignment (16-byte)    │
│ 3. Zero-alloc API → Realtime-safe buffer access         │
│ 4. Debug tracking → Memory usage statistics             │
│ 5. Validation     → Bounds checking in debug builds     │
└─────────────────────────────────────────────────────────┘

Before (scattered):
AudioProcessor.cpp:     buffer = new float[size];
EffectsChain.cpp:       buf = (float*)aligned_alloc(16, sz);
MixerChannel.cpp:       data = malloc(size * sizeof(float));

After (centralized):
All modules:            buffer = bufferPool.acquire(size);

API design principles:
- acquire() / release() instead of new/delete
- RAII wrapper (ScopedBuffer) for exception safety
- Compile-time size known → stack allocation
- Runtime size → pool allocation
- Clear ownership semantics

Benefits of this refactor:

1. FUTURE OPTIMIZATION ENABLEMENT
   Single point of change for:
   - Buffer reuse / pooling (reduce allocations)
   - Arena allocation (cache-friendly)
   - Custom allocators (tracking, debugging)
   - Platform-specific optimizations

2. CONSISTENCY
   All buffers now guaranteed:
   - Properly aligned for SIMD
   - Initialized to zero
   - Bounds-checked in debug
   - Tracked for leak detection

3. DEBUGABILITY
   BufferPool can report:
   - Total memory allocated
   - Peak usage
   - Allocation/deallocation patterns
   - Leak detection (unreleased buffers)

4. MAINTAINABILITY
   Future buffer-related changes:
   - Before: Update 6+ files
   - After: Update BufferPool only

Metrics:
- Code reduction: 300 lines duplicated → 0
- New code added: 250 lines (BufferPool class)
- Net change: -50 lines
- Cyclomatic complexity reduced: avg 8 → 4

Testing approach:
- All existing tests pass (no behavior change)
- New tests for BufferPool: tests/core/buffer_pool_test.cpp
- Memory leak detection: Valgrind clean
- Address sanitizer: No issues

This refactor is part of larger audio engine modernization
roadmap. See docs/architecture/audio-engine-refactor.md

Next steps:
- Phase 2: Implement actual buffer pooling (reuse)
- Phase 3: Thread-local pools for lock-free allocation

No issues expected, but watch for:
- Performance regression (though unlikely given metrics)
- Unusual buffer allocation patterns (should be caught by tests)

Refs #734 (audio engine refactor epic)

Análisis: - ✅ Motivación clara (problema con código actual) - ✅ Scope explícito (no functional changes) - ✅ Arquitectura antes/después - ✅ Beneficios concretos (no vagos "better code") - ✅ Métricas de complejidad - ✅ Testing strategy - ✅ Roadmap context - ✅ Future direction


⚠️ PARTE 6: ANTI-PATTERNS VISUALIZADOS

Anti-Pattern 1: The Vague Message

❌ ANTI-PATTERN:
────────────────
fix: bug fixes

PROBLEMAS:
├─ ¿Qué bug?
├─ ¿Dónde estaba?
├─ ¿Cómo se manifestaba?
├─ ¿Por qué este fix?
└─ ¿Hay side effects?

IMPACTO:
Developer 6 meses después: 😤 😭 🤬
"No tengo idea qué hace este commit"

✅ CORRECTO:
─────────────
fix(dsp): prevent NaN in filter coefficient calculation

When resonance parameter was set to exactly 1.0, division
by (1.0 - resonance) resulted in division by zero,
propagating NaN through audio buffer causing silence.

Now clamp resonance to maximum 0.999 before calculation.
Filter response virtually identical (< 0.01dB difference
at resonance=0.999 vs 1.0).

Closes #789

Anti-Pattern 2: The "WIP" Commit

❌ ANTI-PATTERN:
────────────────
WIP

Commit único con "work in progress"

PROBLEMAS:
├─ No comunica nada
├─ No es atomic change
├─ No es reviewable
├─ Historia inútil
└─ Probablemente no debería commitearse

IMPACTO:
git log muestra:
* abc123 WIP
* def456 WIP
* ghi789 more WIP
* jkl012 actually WIP
└─► Historia completamente inútil

✅ CORRECTO:
─────────────
No commitear WIP.

En su lugar:
1. Termina un cambio atomic
2. Commit con mensaje descriptivo
3. Repeat

Si DEBES guardar trabajo incomplete:
├─ git stash (para guardar temporalmente)
├─ O commit en feature branch local (luego squash)
└─ Pero NUNCA push "WIP" a main/shared branch

Anti-Pattern 3: The Novel

❌ ANTI-PATTERN:
────────────────
feat: reverb

So I was thinking about how reverb algorithms work and I
read this really interesting paper by Schroeder from 1962
and it talked about how you can use comb filters and
allpass filters to create artificial reverberation and I
thought that would be really cool to implement so I started
coding and first I tried one approach but that didn't work
so well so then I tried another approach and that was better
but still not quite right so I did some more research and
found out about the freeverb algorithm which is based on
Schroeder but improved and I thought about using that but
it seemed complicated so I went back to the original
Schroeder and implemented that and it works pretty well I
think users will like it and the CPU usage is okay not
great but okay and I tested it in Reaper and it seems to
work fine so yeah that's what this commit does...

PROBLEMAS:
├─ Stream of consciousness (difícil seguir)
├─ Sin estructura (wall of text)
├─ Sin párrafos (difícil scanear)
├─ Demasiados detalles irrelevantes
└─ No conciso

IMPACTO:
Reader: "TLDR" (demasiado largo, no lo lee)

✅ CORRECTO:
─────────────
feat(dsp): implement Schroeder reverb algorithm

Implemented Schroeder reverb (4 comb + 2 allpass filters)
for natural-sounding reverberation.

Considered Freeverb but too complex for initial version.
Schroeder provides good quality/performance balance.

Performance: ~3% CPU @ 48kHz
Tested in: Reaper, Ableton, Logic

Closes #234

(Estructura, conciso, información key solamente)

Anti-Pattern 4: The Cryptic Reference

❌ ANTI-PATTERN:
────────────────
fix: per discussion in standup

PROBLEMAS:
├─ ¿Qué discussion?
├─ ¿Qué se decidió?
├─ No context para quien no estuvo
└─ Inútil en el futuro

O peor:
────────
fix: john's bug

├─ ¿Qué bug de John?
├─ ¿Cómo encuentro info?
└─ Nombres no son descriptivo

✅ CORRECTO:
─────────────
fix(ui): prevent knob overflow when value > 100%

Team discussion (2024-01-15 standup) identified that
knobs could display values > 100% due to automation
overshoot. Decided to clamp display to [0%, 100%] range
while preserving underlying parameter value for accuracy.

Original issue reported by John Smith in Slack #dev-audio

Closes #456

Anti-Pattern 5: The Multiple Personalities

❌ ANTI-PATTERN:
────────────────
Commit único que hace MÚLTIPLES cosas no relacionadas:

feat: add reverb, fix crash, update docs, refactor buffers

PROBLEMAS:
├─ No atomic (muchos cambios)
├─ Difícil de revert (todo o nada)
├─ Difícil de review (demasiado contexto)
├─ Difícil de cherry-pick
└─ Historia confusa (qué causó qué)

IMPACTO:
Si reverb tiene bug → revert entire commit
├─ Pierdes el crash fix (que era bueno)
├─ Pierdes el refactor (que era bueno)
└─ Pierdes los docs (que eran buenos)

✅ CORRECTO:
─────────────
DIVIDIR en commits separados:

1. feat(dsp): add reverb algorithm
2. fix(vst3): prevent crash on parameter automation
3. docs(api): update reverb API documentation
4. refactor(core): extract buffer management

Ahora cada commit:
├─ Es atomic (un solo propósito)
├─ Es revertible independientemente
├─ Es reviewable fácilmente
└─ Cuenta historia clara

Anti-Pattern 6: The Commit Message Graveyard

❌ ANTI-PATTERN:
────────────────
git log muestra:

* fix
* update
* changes
* stuff
* more changes
* fixes
* updates again
* final version
* actually final
* for real this time

PROBLEMAS:
├─ Historia completamente inútil
├─ Imposible entender qué cambió cuándo
├─ No puedes buscar nada
└─ Debugging nightmare

✅ CORRECTO:
─────────────
git log muestra:

* feat(ui): add spectrum analyzer display
* fix(dsp): prevent clicks in reverb tail
* perf(core): optimize buffer allocation
* docs(readme): add build instructions
* test(reverb): add edge case tests

Ahora:
├─ Historia cuenta una historia
├─ Puedes buscar "reverb" y encontrar related commits
├─ Cada commit tiene significado
└─ Debugging es posible (git bisect funciona)

🧠 PARTE 7: PSICOLOGÍA DEL MENSAJE

El Futuro Reader

Escribe siempre pensando en la persona que leerá tu commit en el futuro.

┌──────────────────────────────────────────────────────────┐
│             QUIÉN LEE TUS COMMIT MESSAGES                 │
├──────────────────────────────────────────────────────────┤
│                                                           │
│  👤 TÚ (6 meses después)                                  │
│  ├─ Has olvidado los detalles                             │
│  ├─ No recuerdas el contexto                              │
│  ├─ Necesitas entender POR QUÉ                            │
│  └─ Tiempo es escaso (debugging urgente)                  │
│                                                           │
│  👥 TEAMMATES ACTUALES                                    │
│  ├─ No estaban en la discusión                            │
│  ├─ Necesitan context rápido                              │
│  ├─ Evaluando si pueden cambiar código                    │
│  └─ Code review (entender decisiones)                     │
│                                                           │
│  🆕 NUEVOS DEVELOPERS                                     │
│  ├─ Aprendiendo el codebase                               │
│  ├─ Usando historia como documentación                    │
│  ├─ Entendiendo evolución del proyecto                    │
│  └─ Buscando patterns y best practices                    │
│                                                           │
│  🔍 DEBUGGERS (bajo estrés)                               │
│  ├─ Production está roto                                  │
│  ├─ Necesitan respuestas AHORA                            │
│  ├─ git bisect para encontrar regresión                   │
│  └─ Cada segundo cuenta                                   │
│                                                           │
│  🤖 AUTOMATED TOOLS                                       │
│  ├─ Changelog generation                                  │
│  ├─ Release notes                                         │
│  ├─ Semantic versioning                                   │
│  └─ CI/CD triggers                                        │
│                                                           │
└──────────────────────────────────────────────────────────┘

Empatía en Mensajes

ESCRIBE CON EMPATÍA:
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  🎯 OBJETIVO: Minimize time to understanding            │
│                                                         │
│  Reader tiene LIMITED TIME y COGNITIVE BUDGET           │
│                                                         │
│  TU TRABAJO: Hacer su vida más fácil                    │
│  ├─ Estructura clara (scaneable)                        │
│  ├─ Context upfront (no hagan adivinar)                 │
│  ├─ Decisiones justificadas (ahorra research)           │
│  └─ Links a recursos (deep dive si necesitan)           │
│                                                         │
└─────────────────────────────────────────────────────────┘

EJEMPLO DE EMPATÍA:

❌ SIN EMPATÍA:
───────────────
"Changed buffer size to 512"

Reader tiene que:
├─ Buscar por qué era diferente
├─ Adivinar por qué cambiaste
├─ No sabe si puede cambiar de vuelta
└─ Tiempo perdido: ~30 minutos research

✅ CON EMPATÍA:
───────────────
"Increase buffer size from 256 to 512 samples

Smaller buffer caused clicks on slower systems (< 2GHz CPU)
when effect chain exceeded processing budget. 512 samples
provides 10.6ms @ 48kHz vs previous 5.3ms.

Trade-off: Slightly higher latency, but eliminates clicks
for 95% of users (based on crash reports analysis).

If you need to change: Be aware that < 512 will cause
clicks on low-end systems. See performance testing guide:
docs/testing/performance.md"

Reader ahora tiene:
├─ Context completo (por qué cambió)
├─ Justificación (research ya hecho)
├─ Trade-offs (puede evaluar alternatives)
├─ Guidance (sabe implications de cambiar)
└─ Tiempo ahorrado: ~25 minutos

El Costo de Mensajes Malos

ECONOMÍA DE COMMIT MESSAGES:

┌─────────────────────────────────────────────────────────┐
│  INVERTIR 5 MINUTOS EXTRA AHORA                         │
│  ├─ Escribir mensaje claro y detallado                  │
│  └─ Costo: 5 minutos                                    │
│                                                          │
│  AHORRA 30+ MINUTOS POR LECTOR                          │
│  ├─ No tienen que investigar context                    │
│  ├─ No tienen que adivinar motivación                   │
│  ├─ No tienen que buscar discussions                    │
│  └─ Ahorro: 30 minutos × N readers                      │
│                                                          │
│  ROI: 6× para solo 1 reader                             │
│  ROI: 60× para 10 readers                               │
│  ROI: 600× para 100 readers (open source)               │
└─────────────────────────────────────────────────────────┘

COSTO DE MENSAJE MALO:

Mensaje vago: "fix bug"

Developer 1 (6 meses después, debugging):
├─ Lee commit: "fix bug"
├─ No entiende qué bug
├─ git show → mira diff
├─ Diff confusing, necesita más context
├─ Busca en issues (si tiene suerte hay ref)
├─ Lee thread completo en issue tracker
├─ Todavía no claro, busca en Slack history
├─ Finalmente entiende (maybe)
└─ TIEMPO TOTAL: 45 minutos

Developer 2, 3, 4, ... repiten mismo proceso
└─ TIEMPO PERDIDO: 45 min × N devs

Ahora multiplica por todos los commits vagos...
└─ COSTO ORGANIZACIONAL: ENORME

✂️ PARTE 8: EL ARTE DE LA CONCISIÓN

Concisión vs Completitud

EL BALANCE:
┌─────────────────────────────────────────────────────────┐
│                                                         │
│  ❌ DEMASIADO CORTO                                     │
│  "fix bug"                                              │
│  └─► Sin información útil                               │
│                                                         │
│  ❌ DEMASIADO LARGO                                     │
│  [5 páginas de stream of consciousness]                │
│  └─► Nadie lo lee                                       │
│                                                         │
│  ✅ GOLDILOCKS ZONE                                     │
│  Subject: Conciso pero informativo (≤50 chars)          │
│  Body: Completo pero estructurado (3-15 líneas típico)  │
│  └─► Perfecto balance                                   │
│                                                         │
└─────────────────────────────────────────────────────────┘

Técnicas de Concisión

TÉCNICA 1: Eliminar palabras redundantes
─────────────────────────────────────────

❌ "This commit adds a new feature that implements..."
✅ "Add feature to..."
   (Obvio que commit añade, que nuevo, que implementa)

❌ "Made changes to fix the issue where..."
✅ "Fix issue where..."
   (Obvio que hiciste cambios)

❌ "Updated the documentation to clarify..."
✅ "Clarify documentation for..."
   (Update es obvio, "to clarify" → "clarify")

─────────────────────────────────────────
TÉCNICA 2: Usar verbos precisos
─────────────────────────────────────────

❌ "Change reverb to be better"
✅ "Optimize reverb CPU usage"
   ("better" es vago, "optimize CPU" es específico)

❌ "Make buffer handling work correctly"
✅ "Fix buffer overflow in effect chain"
   (Específico problema)

─────────────────────────────────────────
TÉCNICA 3: Mover detalles al body
─────────────────────────────────────────

❌ SUBJECT: "Add reverb algorithm based on Schroeder 1962 paper with comb filters"
                └─► 72 chars, se truncará

✅ SUBJECT: "Add Schroeder reverb algorithm"
   BODY: "Based on Schroeder 1962 paper.
          Implements 4 comb + 2 allpass filters..."
          └─► Detalles en body

─────────────────────────────────────────
TÉCNICA 4: Bullet points para listas
─────────────────────────────────────────

❌ "Added reverb parameter and eq parameter and
    compressor parameter"

✅ "Add effect parameters:
    - Reverb: room size, damping, mix
    - EQ: frequency, Q, gain
    - Compressor: threshold, ratio, attack"

└─► Más legible, misma info

Template Final Resumido

╔═══════════════════════════════════════════════════════════╗
║              COMMIT MESSAGE TEMPLATE                      ║
╚═══════════════════════════════════════════════════════════╝

type(scope): imperative subject line (≤50 chars)

[Opcional pero recomendado: Blank line]

Paragraph 1: CONTEXT
What problem existed? Why does this change matter?

Paragraph 2: SOLUTION
What did you do? Why this approach specifically?
What alternatives were considered?

Paragraph 3: IMPACT
What changes for users/developers?
Performance implications?
Testing performed?

[Si aplica:]
BREAKING CHANGE: Description
Migration path steps...

[Metadata:]
Closes #123
Refs #456
Co-authored-by: Name <email>

╔═══════════════════════════════════════════════════════════╗
║                    CHECKLIST                              ║
╠═══════════════════════════════════════════════════════════╣
║                                                           ║
║  ✅ Subject ≤ 50 caracteres                               ║
║  ✅ Subject usa imperativo ("add" no "added")             ║
║  ✅ Type y scope presente (feat, fix, etc)                ║
║  ✅ Blank line después de subject                         ║
║  ✅ Body explica POR QUÉ (no QUÉ - eso lo muestra diff)   ║
║  ✅ Decisiones justificadas (por qué esta solución)       ║
║  ✅ Impacto descrito (qué cambia)                         ║
║  ✅ Breaking changes documentados si aplica               ║
║  ✅ Issues referenciados (Closes #123)                    ║
║  ✅ Testing mencionado                                    ║
║  ✅ Future reader puede entender sin context extra        ║
║                                                           ║
╚═══════════════════════════════════════════════════════════╝

🎓 CONCLUSIÓN: El Mensaje como Legacy

Tu commit message es tu legacy. Es cómo te recordarán los developers del futuro.

┌──────────────────────────────────────────────────────────┐
│                                                           │
│  "Code is read 10× more than it's written.               │
│   Commit messages are read 100× more than code."         │
│                                                           │
│  Invierte los 5 minutos extra.                            │
│  Future you te lo agradecerá.                             │
│                                                           │
│  Write for the archaeologist of the future.               │
│                                                           │
└──────────────────────────────────────────────────────────┘

REGLAS DE ORO:

  1. Explica el POR QUÉ, no el QUÉ (el diff muestra el qué)
  2. 50/72 rule: ≤50 chars subject, 72 chars body wrap
  3. Imperativo mood: "Add feature" no "Added feature"
  4. Contexto primero: Reader necesita entender problema
  5. Justifica decisiones: Por qué esta solución específicamente
  6. Anticipa preguntas: ¿Qué preguntarías tú en 6 meses?
  7. Testing mencionado: Qué validaste
  8. Breaking changes: Documenta obsesivamente
  9. Be empathetic: Ayuda al future reader
  10. Be concise: Completo pero no novela

Documento versión 1.0 AudioLab Foundation • Version Control • Commit Message Art "Tu commit message es tu carta al futuro. Escribe con amor."