Hace unas semanas, vi un tweet que decía “Escribir código no es el problema. Controlar la complejidad sí lo es”. Ojalá pudiera recordar quién dijo eso; Lo citaré mucho en el futuro. Esa afirmación resume muy bien lo que dificulta el desarrollo de software. No se trata sólo de memorizar los detalles sintácticos de algún lenguaje de programación, o de las múltiples funciones de alguna API, sino de comprender y gestionar la complejidad del problema que intentas resolver. Todos hemos visto esto muchas veces. Muchas aplicaciones y herramientas comienzan de forma sencilla. Hacen bien el 80% del trabajo, tal vez el 90%. Pero eso no es suficiente. La versión 1.1 tiene algunas características más, más avances en la versión 1.2 y, cuando llegas a la 3.0, una elegante interfaz de usuario se ha convertido en un desastre. Este aumento de la complejidad es una de las razones por las que las aplicaciones tienden a volverse menos utilizables con el tiempo. También vemos este fenómeno cuando una aplicación reemplaza a otra. RCS fue útil, pero no hizo todo lo que necesitábamos; SVN fue mejor; Git hace casi todo lo que puedas desear, pero a un costo enorme en complejidad. (¿Se podría gestionar mejor la complejidad de Git? No soy yo quien puede decirlo.) OS X, que solía pregonar “Simplemente funciona”, ha evolucionado hasta “solía funcionar”; El sistema tipo Unix más centrado en el usuario jamás construido ahora se tambalea bajo la carga de características nuevas y mal pensadas. Aprende más rápido. Excavar más hondo. Ver más lejos. El problema de la complejidad no se limita a las interfaces de usuario; ese puede ser el aspecto menos importante (aunque más visible) del problema. Cualquiera que trabaje en programación ha visto el código fuente de algún proyecto evolucionar desde algo breve, sencillo y limpio hasta una masa hirviente de bits. (Hoy en día, suele ser una masa hirviente de bits distribuidos). Parte de esa evolución está impulsada por un mundo cada vez más complejo que requiere atención a la programación segura, la implementación de la nube y otras cuestiones que no existían hace unas décadas. Pero incluso en este caso: un requisito como la seguridad tiende a hacer que el código sea más complejo, pero la complejidad en sí oculta problemas de seguridad. Decir «sí, agregar seguridad hizo que el código fuera más complejo» es incorrecto en varios frentes. La seguridad que se agrega como una ocurrencia tardía casi siempre falla. Diseñar la seguridad desde el principio casi siempre conduce a un resultado más simple que incorporar la seguridad como una ocurrencia tardía, y la complejidad seguirá siendo manejable si las nuevas funciones y la seguridad crecen juntas. Si nos tomamos en serio la complejidad, la complejidad de construir sistemas seguros debe gestionarse y controlarse en sintonía con el resto del software; de ​​lo contrario, se agregarán más vulnerabilidades. Eso me lleva a mi punto principal. Estamos viendo más código escrito (al menos en el primer borrador) por herramientas de inteligencia artificial generativa, como GitHub Copilot, ChatGPT (especialmente con Code Interpreter) y Google Codey. Una ventaja de las computadoras, por supuesto, es que no les importa la complejidad. Pero esa ventaja es también una desventaja importante. Hasta que los sistemas de inteligencia artificial puedan generar código de manera tan confiable como nuestra generación actual de compiladores, los humanos necesitarán comprender (y depurar) el código que escriben. Brian Kernighan escribió que “Todo el mundo sabe que, en primer lugar, depurar es dos veces más difícil que escribir un programa. Entonces, si eres tan inteligente como puedes cuando lo escribes, ¿cómo podrás depurarlo?” No queremos un futuro que consista en código demasiado inteligente para ser depurado por humanos, al menos no hasta que las IA estén listas para hacer esa depuración por nosotros. Los programadores realmente brillantes escriben código que encuentra una salida a la complejidad: código que puede ser un poco más largo, un poco más claro, un poco menos inteligente para que alguien pueda entenderlo más tarde. (El copiloto que se ejecuta en VSCode tiene un botón que simplifica el código, pero sus capacidades son limitadas). Además, cuando consideramos la complejidad, no estamos hablando solo de líneas de código individuales y funciones o métodos individuales. La mayoría de los programadores profesionales trabajan en sistemas grandes que pueden constar de miles de funciones y millones de líneas de código. Ese código puede tomar la forma de docenas de microservicios que se ejecutan como procesos asincrónicos y se comunican a través de una red. ¿Cuál es la estructura general, la arquitectura general, de estos programas? ¿Cómo se mantienen simples y manejables? ¿Qué opina de la complejidad al escribir o mantener software que puede sobrevivir a sus desarrolladores? Millones de líneas de código heredado que se remontan a las décadas de 1960 y 1970 todavía están en uso, muchas de ellas escritas en lenguajes que ya no son populares. ¿Cómo controlamos la complejidad cuando trabajamos con estos? Los humanos no gestionamos bien este tipo de complejidad, pero eso no significa que podamos comprobarlo y olvidarnos de él. A lo largo de los años, hemos mejorado gradualmente en la gestión de la complejidad. La arquitectura de software es una especialidad distinta que solo se ha vuelto más importante con el tiempo. Se está volviendo más importante a medida que los sistemas crecen y se vuelven más complejos, a medida que dependemos de ellos para automatizar más tareas y a medida que esos sistemas necesitan escalar a dimensiones que eran casi inimaginables hace unas décadas. Reducir la complejidad de los sistemas de software modernos es un problema que los humanos pueden resolver, y todavía no he visto evidencia de que la IA generativa pueda hacerlo. Estrictamente hablando, esa no es una pregunta que se pueda plantear todavía. Claude 2 tiene un contexto máximo (el límite superior de la cantidad de texto que puede considerar a la vez) de 100.000 tokens1; En este momento, todos los demás modelos de lenguaje grandes son significativamente más pequeños. Si bien 100.000 tokens es una cantidad enorme, es mucho más pequeño que el código fuente incluso de un software empresarial de tamaño moderado. Y aunque no es necesario comprender cada línea de código para realizar un diseño de alto nivel para un sistema de software, sí es necesario administrar mucha información: especificaciones, historias de usuarios, protocolos, restricciones, legados y mucho más. ¿Está un modelo de lenguaje a la altura de eso? ¿Podríamos siquiera describir el objetivo de “gestionar la complejidad” en una indicación? Hace unos años, muchos desarrolladores pensaron que minimizar las “líneas de código” era la clave para la simplificación y que sería fácil decirle a ChatGPT que resolviera un problema en la menor cantidad de líneas de código posible. Pero en realidad no es así como funciona el mundo, ni ahora ni en 2007. Minimizar líneas de código a veces conduce a la simplicidad, pero con la misma frecuencia conduce a encantamientos complejos que agrupan múltiples ideas en la misma línea, a menudo basándose en efectos secundarios no documentados. . Así no es como gestionar la complejidad. Mantras como DRY (No te repitas) suelen ser útiles (como lo son la mayoría de los consejos de The Pragmatic Programmer), pero cometí el error de escribir código que era demasiado complejo para eliminar una de dos funciones muy similares. Menos repetición, pero el resultado fue más complejo y más difícil de entender. Las líneas de código son fáciles de contar, pero si esa es su única métrica, perderá la noción de cualidades como la legibilidad que pueden ser más importantes. Cualquier ingeniero sabe que el diseño se trata de compensaciones (en este caso, compensaciones entre repetición y complejidad), pero por difíciles que puedan ser estas compensaciones para los humanos, no me queda claro que la IA generativa pueda mejorarlas, en todo caso. No estoy diciendo que la IA generativa no tenga un papel en el desarrollo de software. Ciertamente lo hace. Las herramientas que pueden escribir código son ciertamente útiles: nos ahorran buscar los detalles de las funciones de la biblioteca en manuales de referencia, nos ahorran recordar los detalles sintácticos de las abstracciones menos utilizadas en nuestros lenguajes de programación favoritos. Mientras no dejemos que nuestros propios músculos mentales se descompongan, estaremos adelante. Lo que sostengo es que no podemos involucrarnos tanto en la generación automática de código como para olvidarnos del control de la complejidad. Los modelos de lenguaje grandes no ayudan con eso ahora, aunque podrían hacerlo en el futuro. Sin embargo, si nos liberan para dedicar más tiempo a comprender y resolver los problemas de complejidad de mayor nivel, será una ganancia significativa. ¿Llegará el día en que un gran modelo de lenguaje pueda escribir un programa empresarial de un millón de líneas? Probablemente. Pero alguien tendrá que escribir el mensaje diciéndole qué hacer. Y esa persona se enfrentará al problema que ha caracterizado a la programación desde el principio: comprender la complejidad, saber dónde es inevitable y controlarla. Notas al pie Es común decir que una ficha es aproximadamente ⅘ de una palabra. Sin embargo, no está claro cómo se aplica eso al código fuente. También es común decir que 100.000 palabras es el tamaño de una novela, pero eso sólo es cierto para las novelas más bien cortas.

Source link