A medida que nuestra base de código evoluciona, a veces necesitamos modificar cientos o miles de componentes para seguir nuevos patrones, añadir funcionalidades o limpiar código heredado.
Estas refactorizaciones pueden ser tediosas, lentas y propensas a errores. Para mitigar estos problemas, solemos usar codemods para gestionar nuestras refactorizaciones a gran escala.
En esta entrada del blog, te explicaremos qué son los codemods, por qué deberías usarlos y cómo los hemos utilizado con éxito en Webflow.
¿Qué son los codemods?
«Codemod» es una abreviatura de «modificación de código». Por lo tanto, un codemod es simplemente un script que puedes ejecutar para modificar el código de tu base de código.
En su forma más simple, un codemod podría ser un sencillo «buscar y reemplazar» para renombrar funciones. Los ingenieros frontend que empezaron con React 15 o anterior quizás recuerden cuando algunos métodos del ciclo de vida de los componentes de clase quedaron obsoletos en React 16.3. El equipo de React recomendó renombrar métodos como componentWillMount a UNSAFE_componentWillMount para que siguieran funcionando en futuras versiones principales de React. Podrías hacer este cambio fácilmente con un «buscar y reemplazar» en tu IDE, pero el equipo de React también escribió un codemod para ti que podías ejecutar en su lugar. Este codemod recorría tu base de código y realizaba los cambios por ti.
Sin embargo, los codemods pueden hacer mucho más que solo renombrar cosas. También puedes usarlos para cambiar rutas de importación, modificar el uso de la sintaxis (como pasar de funciones regulares a funciones de flecha, o viceversa) o clasificar el orden de las variables en un archivo.
Incluso puedes considerar que ESLint (con la bandera --fix) y Prettier utilizan codemods, ya que ambas son herramientas que modifican tu código según las reglas que hayas configurado. La principal diferencia entre ESLint y Prettier y un script de codemod es que ESLint y Prettier generalmente se ejecutan como parte de tu proceso de integración continua (CI), por lo que son una parte constante de tu ciclo de vida de desarrollo y se ejecutan en cada confirmación o solicitud de extracción. Los codemods, por otro lado, suelen ser scripts que invocas manualmente y ejecutas solo una vez, y después ya no los necesitas.
¿Por qué usar codemods?
Entonces, ¿por qué querrías usar un codemod? Después de todo, todo lo que hemos descrito anteriormente es algo que también podrías hacer a mano sin necesidad de una herramienta automatizada. Podrías simplemente refactorizar todo el código tú mismo y darlo por terminado.
Los codemods pueden ser útiles por varias razones.
Quizás no estés trabajando en un solo monorepo, sino en cientos de repositorios, y los cambios deban realizarse en cada uno de ellos. Y tal vez no seas la única persona haciendo los cambios, por lo que necesites la ayuda de docenas de otros ingenieros. Escribir un codemod que otros ingenieros puedan usar simplifica enormemente la tarea para ellos. Ahora no tienes que explicar una y otra vez qué pasos deben seguirse y qué cambios deben hacerse. En su lugar, puedes hacer que cada ingeniero ejecute el codemod en sus repositorios asignados, y el codemod se encargará del trabajo pesado por ellos.
O bien, ¿qué pasa si se necesitan varios pasos para esta refactorización? Imaginemos que no es solo un «buscar y reemplazar», sino que necesitas añadir una importación para un nuevo componente, eliminar la importación del componente antiguo, reemplazar todas las instancias donde se usaba el componente antiguo para que ahora hagan referencia al nuevo, y también ajustar algunas de las propiedades que tienen nombres y valores diferentes. Son muchos pasos y hay margen para cometer errores, especialmente si tienes que hacerlo en cientos de archivos. Al fin y al cabo, somos humanos.
Por último, incluso las tareas sencillas de «buscar y reemplazar» pueden ser engañosamente complejas. Tal vez necesites reemplazar OldComponent con NewComponent. Podrías hacer una búsqueda global de OldComponent y reemplazarla con NewComponent para cada instancia encontrada. Pero, ¿qué pasa si OldComponent es una exportación por defecto de un archivo y alguien lo importó con un nombre diferente? ¿O qué pasa si es una exportación con nombre, pero alguien le puso un alias en su importación para usar un nombre nuevo? ¿Qué sucede si resulta que hay otro componente llamado OldComponent, pero el nombre es solo una coincidencia y ese componente no debería cambiarse?
Como puedes ver, las refactorizaciones pueden ser tediosas, propensas a errores, pueden implicar varios pasos y pueden involucrar a varias personas. ¿No sería genial si pudiéramos automatizar parte de este proceso para facilitárselo a todos los involucrados?
Una advertencia
Antes de continuar, para que quede claro: ¡escribir un codemod puede llevar tiempo! El equilibrio que debes considerar es cuánto tiempo te llevaría escribir el codemod frente a hacer las refactorizaciones manualmente. Ten en cuenta cuántos lugares necesitas modificar, cuántos pasos hay, qué tan propenso a errores puede ser el proceso y cuántas otras personas tendrán que participar.
Si se trata de una refactorización pequeña y bastante sencilla, y eres el único ingeniero trabajando en ella, puede que lo mejor sea no usar un codemod en este caso.

Caso de uso de ejemplo
¡Muy bien, basta de teoría y antecedentes! Veamos un ejemplo real.
En Webflow Conf 2023, presentamos una interfaz de usuario totalmente nueva para las superficies de nuestros productos Designer y Dashboard. Todos los cambios en la interfaz estaban protegidos por un indicador de funciones (feature flag), lo que nos permitió alternar fácilmente entre la interfaz antigua y la nueva durante las pruebas, y simplemente activar el interruptor para el gran lanzamiento.
Para facilitar esto, escribimos un componente de orden superior (HoC) llamado pickComponent que nos permitía intercambiar los componentes antiguos y nuevos en la interfaz.
Un ejemplo sencillo podría verse así:
// Input component definition
const InputD1 = emotionStyled('input')({
padding: '$2 $5',
});
const InputD2 = emotionStyled('input')({
padding: '$1 $5',
});
const Input = pickComponent(InputD1, InputD2);
// Input component usage
const CommercePriceInput = (props) => {
// additional component setup here
return <Input {...props} />;
};En el fragmento de código anterior, tenemos un componente Input con una versión antigua (InputD1) y una nueva (InputD2). El HoC pickComponent contiene la lógica para renderizar InputD1 cuando el indicador de función está desactivado y renderizar InputD2 cuando está activado. Luego, en cualquier lugar donde se use este componente en la aplicación, se puede hacer referencia a él simplemente como Input. De esta manera, la lógica del indicador de función y el comportamiento de alternancia se mantienen dentro de una única instancia del componente, en lugar de estar dispersos en docenas de lugares diferentes donde se utiliza.
Después de Webflow Conf 2023 y el lanzamiento de la nueva interfaz, llegó el momento de limpiar el código heredado. Ya no necesitábamos usar el HoC pickComponent, ni tampoco las definiciones de los componentes antiguos. Por lo tanto, el fragmento de código anterior podría simplificarse así:
// Input component definition
const Input = emotionStyled('input')({
padding: '$1 $5',
});
// Input component usage
const CommercePriceInput = (props) => {
// additional component setup here
return <Input {...props} />;
};¿Tiene sentido? Ahora imagina esto, pero en cientos de lugares. ¡Había mucho trabajo de limpieza por hacer!
¿Cómo limpiamos todo este código sin causar regresiones en nuestra aplicación y sin morir de aburrimiento? Convertimos esto en un desafío divertido: ¿qué pasaría si pudiéramos escribir un codemod para que hiciera el trabajo por nosotros?
Decidimos usar jscodeshift, que es “un kit de herramientas para ejecutar codemods en múltiples archivos JavaScript o TypeScript”.
Pruebas unitarias
Una ventaja de usar jscodeshift es que es posible escribir pruebas unitarias para verificar tu codemod. Para ello, creas accesorios de prueba (test fixtures) con tu entrada y la salida esperada. Puedes crear tantos como quieras para estar seguro de que tu codemod funcionará como se espera en una variedad de casos. Luego, las pruebas simplemente toman la entrada, ejecutan el codemod y comparan la salida real con la esperada. Si coinciden, la prueba pasa; si no, la prueba falla.
Para los ingenieros que aman el desarrollo guiado por pruebas (TDD), es posible que se les haya encendido la bombilla: ¡es el momento perfecto para usar TDD! Primero podemos escribir un montón de entradas y salidas esperadas y luego redactar nuestro codemod para que las pruebas pasen, y eso fue exactamente lo que hicimos.
Terminamos escribiendo 21 casos de prueba para este codemod de limpieza de pickComponent. Ya les mostramos un ejemplo en los dos fragmentos de código anteriores. Veamos un par más.
Ejemplo de entrada 2:
const HintD2 = emotionStyled(HintButton)({
height: '16px',
});
const Hint = pickComponent(HintButton, HintD2);
const BranchConflictsContent = () => {
return (
<div>
<Hint hint="Hint text here" />
</div>
);
};
export default BranchConflictsContent;Ejemplo de salida 2:
const Hint = emotionStyled(HintButton)({
height: '16px',
});
const BranchConflictsContent = () => {
return (
<div>
<Hint hint="Hint text here" />
</div>
);
};
export default BranchConflictsContent;Ejemplo de entrada 3:
const SectionLabelContainerD1 = styled(FlexRow)(({theme: {gutters}}) => ({
paddingRight: gutters.x8,
}));
const SectionLabelContainer = pickComponent(SectionLabelContainerD1, FlexRow);
const StyledIslandD1 = styledDiv(
({isFirst, isLast, nextIsExpanded, expanded, theme: {colors}}) => ({
borderTop: !isFirst && expanded && `1px solid ${colors.borderDeep}`,
borderBottom:
isLast || nextIsExpanded ? null : `1px solid ${colors.borderDeep}`,
})
);
const StyledIsland = pickComponent(StyledIslandD1, 'div');Ejemplo de salida 3:
// @SPRING_CLEANING: D2 cleanup. Consolidate component usage.
const SectionLabelContainer = FlexRow;
// @SPRING_CLEANING: D2 cleanup. Consolidate component usage.
const StyledIsland = 'div';Después de definir todos esos accesorios de prueba para nuestras entradas y salidas, los ejecutamos en un archivo de prueba como este:
const defineTest = require('jscodeshift/dist/testUtils').defineTest;
describe('Codemod', () => {
const tests = [
'example1',
'example2',
// ...example3 through example20
'example21',
];
tests.forEach((test) => {
describe(test, () => {
defineTest(
__dirname,
'pickComponentCleanup',
{},
`pickComponentCleanup/${test}`
);
});
});
});El método defineTest proviene de jscodeshift, y los argumentos que proporcionas le indican dónde encontrar el codemod a ejecutar y las entradas y salidas a probar.
AST explorer
En este punto, ya teníamos un codemod básico y pruebas fallidas para los diversos ejemplos de código que debíamos manejar. ¡Había llegado el momento de escribir el codemod!
Para escribir un codemod, ayuda tener una idea básica de cómo funcionan. Los codemods no actúan directamente sobre el código que les das. Primero, convierten tu código de entrada en un árbol de sintaxis abstracta (AST), que es una representación abstracta basada en la sintaxis y los tokens utilizados. Luego, el codemod recorre el AST y modifica los nodos del árbol. Finalmente, compila el AST de vuelta al JavaScript o TypeScript con el que estamos acostumbrados a trabajar.
El AST no es un concepto que la mayoría de los ingenieros encuentre a diario, a menos que trabajen con codemods o compiladores. Para ayudarte a entender cómo se ve un AST, puedes echar un vistazo a AST explorer, una herramienta gratuita en línea que te permite pegar código a la izquierda y visualizarlo como un AST a la derecha. También puedes escribir codemods directamente allí para ver el resultado que generan.
AST explorer crea un entorno de pruebas ideal para experimentar con los cambios en tu codemod. Veamos un ejemplo:

En la esquina superior izquierda tenemos nuestro código de entrada. Contiene un array llamado tips y una función llamada printTips.
En la esquina superior derecha vemos el AST de ese código. Al pasar el cursor sobre partes del árbol a la derecha, verás resaltado el código correspondiente a la izquierda. En el AST, verás palabras clave como VariableDeclaration, ArrayExpression, Literal, FunctionDeclaration, Identifier y más.
En la esquina inferior izquierda vemos un ejemplo de codemod que invierte los nombres de las funciones o variables. El código de entrada se transforma en un AST en la línea 9, el AST se recorre y modifica en las líneas 10-15, y luego se convierte de nuevo en código de salida en la línea 16.
En la esquina inferior derecha vemos el código resultante. Como este codemod invierte los nombres, vemos que tips se convirtió en spit y printTips en spiTtnirp. ¡Incluso funciones integradas como forEach y console.log fueron invertidas!
Ahora, si estuvieras escribiendo tu propio codemod, podrías pegar tu código, ver cómo se traduce a un AST y empezar a escribir hasta que el resultado sea exactamente lo que buscas.
Eso es exactamente lo que hicimos en Webflow. Tomamos nuestras pruebas unitarias fallidas, pegamos los ejemplos en AST explorer, escribimos el codemod para obtener el resultado correcto y repetimos el proceso. El codemod fue ganando complejidad, y nuestras pruebas aseguraron que no rompiéramos nada de lo anterior. ¡Fue un juego divertido de experimentación y TDD!
Resultados
Entonces, ¿cómo salió todo? Para esta limpieza de la interfaz, escribimos dos codemods: uno para nuestro HoC pickComponent que mencionamos antes, y otro para un hook llamado useIsD2, que servía para verificar si una bandera de función estaba activa o no. Tardamos cerca de una semana en escribir ambos.
Ejecutamos estos codemods en gran parte de nuestra base de código poco después de la Webflow Conf 2023, y las actualizaciones fueron fluidas. Como otras partes aún no habían migrado a la nueva interfaz, tuvimos que dejar algo para después. De hecho, no fue hasta noviembre de 2024 que todo estuvo oficialmente en la nueva interfaz y pudimos terminar la limpieza. En ese momento, volvimos a ejecutar los codemods y funcionaron a la perfección. ¡Tenerlos listos un año después nos ahorró muchísimo tiempo! Y lo mejor: en ese año, migramos todo de Flow a TypeScript, y los codemods originales funcionaron sin problemas, solo tuvimos que especificar que el analizador ahora era tsx y no flow.
Al final, los codemods modificaron más de 20,000 líneas de código, y pudimos integrar y desplegar los cambios sin regresiones visuales importantes. ¡Un gran logro!
Los codemods no son la solución para todo, pero sin duda son una herramienta muy útil para tener a mano, especialmente a medida que tu repositorio, tu equipo y la complejidad de tu código crecen.
¡Gracias por leer y feliz programación!

¡Estamos contratando!
Buscamos talento en producto e ingeniería para que se una a nuestra misión de poner superpoderes de desarrollo al alcance de todos.










