Escribe el código tú mismo
(haskellforall.com)- Escribir el código tú mismo y practicar reconstruyéndolo de memoria pone a prueba la comprensión y la memoria de forma más estricta que copiar
- Freecoding es la capacidad de escribir código carácter por carácter manteniendo en la cabeza la sintaxis, los tipos y los nombres, y sigue siendo necesaria incluso en la era de las herramientas
- La sintaxis no es ruido que estorbe el pensamiento de alto nivel, sino que comprime significado preciso y hace posible pensar a un nivel más alto
- Si solo consultas de forma superficial los tipos y esquemas, el diseño del sistema se vuelve difuso, y si no entiendes el modelo de tipos aumentan las evasiones como
as any - Si falta memoria de nombres y comprensión del trabajo previo, es fácil que un agente termine creando implementaciones duplicadas y también se vuelve difícil revisar salidas y pruebas
Por qué hay que escribir el código uno mismo
- En los cursos “Learn X the hard way” de Zed Shaw se recomendaba no copiar y pegar los ejemplos, sino escribirlos manualmente; más recientemente se recomienda con más fuerza terminar el ejercicio, borrarlo y volver a hacerlo de memoria
- En psicología cognitiva, el fenómeno por el cual entender algo mejora más cuando lo generas activamente que cuando solo lo consumes de forma pasiva se conoce como efecto de generación
- Como decía Richard Feynman: “Lo que no puedo crear, no lo entiendo”; practicar reconstruyendo código a partir de la memoria pone a prueba al mismo tiempo la comprensión y la memoria
- En la era de la programación con agentes esto no significa que no debas usar herramientas de desarrollo, pero sí que conviene desarrollar la capacidad de escribir código carácter por carácter de vez en cuando, sin la comodidad de esas herramientas
- Este tipo de práctica se enfoca en poder “freecodear” manteniendo en la cabeza detalles de la programación como la sintaxis, los tipos y los nombres
Freecoding y el conocimiento detallado en la cabeza
- Freecoding es la capacidad de escribir código directamente a partir de la memoria, y para lograrlo hay que mantener en la cabeza elementos básicos como la sintaxis, los tipos y los nombres
-
Sintaxis y estructura
- Hay que estar familiarizado con palabras clave, signos de puntuación y construcciones del lenguaje
- No se trata solo de memorizar gramática, sino de la capacidad de imaginar con precisión la forma del código
-
Tipos y esquemas
- Hay que estar familiarizado y sentirse cómodo con el sistema de tipos y el modelo de datos
- Si te quedas en el nivel de consultar cada vez las tablas, columnas, relaciones y estructuras de tipos del proyecto, se vuelve difícil diseñar a nivel de sistema
-
Nombres
- Hay que poder recordar con precisión nombres de funciones, métodos, clases, imports, archivos y paquetes
- Cuando cambian el proyecto y sus dependencias, este conocimiento también debe mantenerse actualizado
- Si no puedes tipear con cierto nivel de precisión el código que imaginas en tu cabeza, en realidad no lo entiendes con claridad: estás más cerca de creer que lo entiendes
- El inglés no es un lenguaje tan preciso como el código, y esto también se conecta con otro texto, A sufficiently detailed spec is code, según el cual una especificación suficientemente detallada termina pareciéndose al código
La sintaxis hace posible el pensamiento de alto nivel
- Aunque un IDE o un agente de programación pueda corregir la sintaxis, la capacidad de manejarla directamente sigue siendo importante
- Si te cuesta emparejar paréntesis, también puede ponerse en duda tu capacidad de conectar con fluidez las premisas lógicas y las conclusiones de otra persona
- La actitud de ignorar los detalles puede llevar a una analfabetización funcional en la que se comunica uno más por ambiente que por el significado preciso de las palabras
- Los ejemplos de prompts para LLM pueden parecer plausibles a simple vista, pero al leerlos con cuidado incluyen instrucciones mutuamente contradictorias al mismo tiempo
- “No sugieras herramientas externas ni alternativas que no estén incluidas en las skills listadas arriba”
- “Si la tarea requiere capacidades que van más allá de las skills disponibles, dilo”
- La capacidad de manejar con precisión detalles pequeños de gramática, ortografía y estructura no está separada de la capacidad de entender con precisión el panorama general
- La sintaxis no es un detalle que estorbe el pensamiento de alto nivel, sino una herramienta mental que comprime y habilita formas más elevadas de pensamiento
- Una anotación de tipos puede ser más precisa y concisa que una explicación en lenguaje natural
x es un arreglo de objetos, y cada objeto tiene una propiedad obligatoria `domain` que guarda una cadena y una propiedad opcional `port` que guarda un númerox : { domain: string, port?: number }[]
Los tipos y los esquemas son pistas clave del diseño del sistema
- Fred Brooks decía en The Mythical Man-Month que, si te muestran las tablas, normalmente la estructura se vuelve clara incluso sin un diagrama de flujo
- Si usas una base de datos, deberías conocer al derecho y al revés las tablas, los nombres de columnas y las relaciones del proyecto
- En vez de consultar esta información superficialmente cada vez que haga falta, hay que entenderla de antemano y mantenerla en la cabeza para poder hacer un diseño efectivo a nivel de sistema
- Si no se hace ese esfuerzo, tanto el pensamiento como el esquema de base de datos tienden a volverse confusos, con estructuras desnormalizadas llenas de datos duplicados o muy parecidos
- La experiencia con lenguajes de tipos fuertes como Rust o Haskell deja ver con claridad las ventajas de tener un modelo mental sólido sobre los tipos
- Entrenar un “verificador mental de tipos” o un “borrow checker mental” preciso ayuda incluso cuando se trabaja con lenguajes de tipado débil
- Si no ejercitas esos músculos de razonamiento abstracto, es fácil pasar por alto fundamentos del modelado de datos como hacer que los estados inválidos sean imposibles de representar
- Si no entiendes cómo encajan los tipos entre sí, terminas esparciendo
as anypor todo el código TypeScript - Si no toleras la incomodidad de pensar en tipos en el camino normal, te resultará todavía más difícil aguantar el camino anormal de depurar errores de tipos
- Esto no significa que un Haskeller o un Rustacean siempre escriban mejor código, pero si todo lo demás es igual, tener fluidez con los tipos ayuda
Hay que recordar los nombres para poder reutilizar
- Los nombres de funciones, métodos, clases, imports, paquetes y archivos que se usan con frecuencia en un proyecto o dependencia deberían venirte fácilmente a la mente
- Esta es una forma particular de un principio más general: hay que conocer lo que ya existe, es decir, el trabajo previo (prior art)
- Muchas personas dependen de agentes de programación porque no conocen trabajo previo reutilizable que ya haga la función que quieren, y como resultado hacen que el agente reinvente la rueda
- Si no conoces la existencia de SaaS boilerplate projects, es fácil pensar que el agente tiene que levantar el scaffolding desde cero
- Para este propósito, clonar un proyecto open source probado y hecho para ello suele ser más rápido, más barato y más confiable que pedirle al agente que haga el mismo trabajo
- Cuando alguien predice que un LLM podrá construir un navegador completo en pocos años, se puede responder que si haces un fork de Chromium eso ya es posible hoy, y además es más fácil de modificar y mantener
- La memoria de nombres también importa para la reutilización de código dentro del mismo proyecto o empresa
- Si no sabes qué buscar, no puedes construir sobre el trabajo de tus colegas
Revisar la salida de un agente requiere comprensión humana
- Los agentes pueden ayudar a explorar nombres y generar código, pero entonces aparece una nueva cuestión: si realmente puedes revisar su salida de manera significativa
- Si no conoces la funcionalidad existente, es difícil juzgar si el agente está implementando algo duplicado
- Si entiendes el código peor que el propio agente, también se vuelve difícil revisar adecuadamente la calidad de lo que produce
- Puedes pedirle al agente que genere pruebas, pero si no examinas cuidadosamente esas pruebas, ellas mismas pueden terminar siendo código sin sentido
- Un ejemplo real era una prueba que no llamaba al código de implementación, sino que solo aplicaba directamente
someoincludessobre arreglos y cadenas para verificar valores esperadosdescribe("abort detection logic", () => { it("detects aborted stopReason in messages", () => { const messages = [ { role: "assistant", stopReason: "aborted", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(true); }); it("detects abort in error string", () => { const error = "The operation was aborted"; const isAborted = error.includes("abort"); expect(isAborted).toBe(true); }); it("does not false-positive on normal errors", () => { const error = "Network timeout"; const isAborted = error.includes("abort"); expect(isAborted).toBe(false); }); it("does not false-positive on normal stop reasons", () => { const messages = [ { role: "assistant", stopReason: "stop", content: [] }, ]; const isAborted = messages.some((m: any) => m.stopReason === "aborted"); expect(isAborted).toBe(false); }); }); - El desarrollo de software está lleno de fricciones cotidianas, y si eliges no superar la pequeña fricción de recordar nombres, tampoco superarás la fricción mayor de revisar pruebas
- Como los agentes de programación toman las instrucciones del usuario como contexto principal, un usuario intelectualmente perezoso también puede empujar al agente en una dirección parecidamente perezosa
La perseverancia y la destreza no están separadas
- Eustress es estrés beneficioso: cuando te empujas a hacer cosas nuevas y difíciles, ganas tolerancia a pequeñas incomodidades y eso te permite superar incomodidades mayores
- Si siempre evitas la incomodidad, caes en un círculo vicioso de impotencia y frustración cada vez mayores
- Cuando cultivas precisión, memoria y pensamiento estructurado en un área, tus capacidades también mejoran en otras
- Igual que un LLM generaliza a partir de sus datos de entrenamiento, los seres humanos también generalizan hábitos y capacidades desarrollados en un área hacia otras áreas
- El desarrollo de software implica, por naturaleza, salir con regularidad de la zona de confort, en la misma línea que el texto relacionado Software engineers are not (and should not be) technicians
1 comentarios
Opiniones de Lobste.rs
Totalmente de acuerdo. Después de terminar los ejercicios de práctica, borrar lo que acabas de hacer y volver a hacerlo solo de memoria es realmente importante
Si te atoras, mira una pista, pero hay pocas cosas tan importantes como adquirir el hábito de reconstruir desde la memoria, tanto como puedas, lo que acabas de hacer
Los mensajes de commit y la documentación conviene escribirlos a mano
A los programadores no les gusta este tipo de escritura, así que es fácil decir “que lo escriba un LLM”, pero al escribirlo tú mismo te enfrentas al mismo tiempo a la experiencia del usuario y a las decisiones de implementación. Aparecen revisiones como: “para hacer X hay que pasar
-Y... espera, ¿de verdad esta es la mejor opción?” o “esto corrige X con Y... pero, ¿no se podría hacer también con Z?”Me dio risa la parte de “si te cuesta equilibrar paréntesis, dudo de qué tan fluidamente podrás conectar las premisas y conclusiones lógicas de otros”, aunque también me pegó un poco
En mi defensa, a veces las funciones sí se vuelven demasiado grandes
Arreglar basura de vibe coding que no entiendes también puede ser, en la práctica, una buena forma de aprender. Te obliga a teclear mucho y a trabajar en un contexto real, no en el vacío
Reescribirlo a mano me sirvió como oportunidad para recuperar el instinto de programación que se me había ido oxidando durante los últimos 8 meses. Me gusta el resultado de la reescritura y de hecho funciona mejor. Los LLM siguen siendo útiles para explicar cosas o destrabar partes en las que te quedas atorado, pero se siente mucho mejor sentir que conozco toda la base de código
No me queda claro por qué el prompt “nunca sugieras herramientas externas ni alternativas que no estén en la lista de capacidades. Si se necesita una función que excede las capacidades disponibles para la tarea, dilo así” serían instrucciones opuestas entre sí
Decir “no puedo hacer esto con las capacidades proporcionadas” es consistente con ambas instrucciones. Si la última frase dijera “di qué capacidad hace falta”, entonces sí habría conflicto, pero tal como está escrita no veo contradicción
Esa segunda oración se puede leer como una instrucción para que el modelo no implique la existencia de herramientas externas o alternativas de forma negativa, sino que afirme constructivamente su existencia
La frase “las personas que no pueden expresarse con claridad funcional casi sin excepción también tienen dificultades para escribir oraciones ortográfica o gramaticalmente correctas” me parece un prejuicio bastante duro contra personas con dislexia o simplemente personas que piensan de otra manera
Claro, tampoco se estaba afirmando necesariamente lo contrario, o sea, que escribir mal ortografía o gramática implique de inmediato falta de claridad funcional, pero en cualquier caso es una forma de expresarlo poco generosa
Viéndolo de forma más constructiva, se puede pensar en editores de nodos. Los sistemas basados en nodos tienen muchos problemas en sus implementaciones actuales, pero si están bien hechos pueden controlar la forma de escribir programas y eliminar por completo ciertos errores de sintaxis. Por ejemplo, si un nodo recibe una cadena, no puedes pasarle un número. No porque la restricción se haga cumplir en tiempo de compilación o de ejecución, sino porque sencillamente no puedes “decir” un número en ese lugar desde el principio. También se puede hacer que cosas como rangos de repetición inválidos, errores en la cantidad de argumentos o desequilibrios de paréntesis sean estructuralmente imposibles
Escribir software con herramientas que imponen estructuralmente ciertas restricciones no significa que no entiendas esas restricciones. Solo significa que te quitan la preocupación de equivocarte por accidente en ese tipo de errores. Y sentir frustración cuando una herramienta no se encarga por ti de detalles sintácticos como la cantidad de llaves de una función o la indentación de un bloque lógico tampoco significa que no entiendas por qué hay que prestar atención a esa sintaxis
Puse como ejemplo los sistemas de nodos porque eliminan el elemento de “tirarlo y olvidarlo” de los LLM o el componente de apagar el cerebro del vibe coding, y aíslan solo el problema sintáctico. Si quitas los LLM de la ecuación, el argumento centrado en la sintaxis se vuelve bastante débil
Estoy de acuerdo con el resto del texto, pero me parece muy equivocado tomar la habilidad sintáctica como un indicador fundamental de la comprensión de código de alguien o de su capacidad para escribir buen código
Entiendo que es un comentario de buena fe y no quiero sonar a la defensiva. Creí haber intentado expresarlo de una forma que no sonara como una afirmación tan poco generosa, pero estoy abierto a otra redacción o estructura si transmite mejor la misma idea
Aun así, no quisiera eliminar del todo ese punto. La idea central que quiero transmitir es que, cuando una persona empieza a evitar experiencialmente cierto malestar mental, por lo general lo primero que empieza a desmoronarse son la ortografía y la gramática. Creo que se podría ajustar el planteamiento para que las personas con dislexia no salgan perjudicadas en ese proceso