
[12/04/2023] Así como el mundo del arte está lleno de opiniones muy divergentes sobre lo que define una gran obra de arte, los programadores a menudo no están de acuerdo sobre lo que hace un gran código, al menos más allá del requisito básico de que no debe bloquearse.
Cada desarrollador tiene su propio conjunto de reglas y pautas. Cuando un desarrollador afirma que no haga algo, probablemente se deba a que lo hizo una vez y fracasó gravemente. Pero pueden surgir nuevos problemas cuando compensamos en exceso un error corriendo en la dirección opuesta. Digamos que su equipo esquiva la trampa "x” eligiendo "y” en su lugar, pero resulta que "y” tiene sus propios problemas, lo que lleva a otro largo fin de semana perdido.
[Reciba lo último de CIO Perú suscribiéndose a nuestro newsletter semanal]
La buena noticia es que usted puede aprender tanto del error original como de la sobrecompensación. El mejor camino hacia el nirvana suele ser el del medio. En este artículo, analizamos algunos de los errores de programación más comunes, así como los peligros que implica hacer lo contrario.
Trabajar rápido e impreciso
Ignorar los conceptos básicos es una de las formas más fáciles de producir código inestable y propenso a fallas. Tal vez esto signifique ignorar cómo el comportamiento arbitrario del usuario podría afectar su programa. ¿Encontrará input de un cero su camino en una operación de división? ¿El texto enviado siempre tendrá la longitud correcta? ¿Sus formatos de fecha siguen el estándar correcto? ¿Se verifica el nombre de usuario con la base de datos? El más mínimo error puede hacer que el software falle.
Una forma de resolver esto es explotar las funciones de detección de errores del código. Un desarrollador al que le gusta trabajar rápido e impreciso podría envolver todo su stack con una gran condición para todas las excepciones posibles. Simplemente volcarán el error en un archivo de registro, devolverán un código de error y dejarán que otra persona se ocupe del problema. Sin sudor, ¿verdad?
Obsesionarse con los detalles
Algunos afirman que un buen programador es alguien que mira a ambos lados cuando cruza una calle de sentido único. Pero, al igual que el que trabaja rápido e impreciso, esta tendencia puede resultar contraproducente. El software que está demasiado asegurado puede ralentizar sus operaciones al máximo. Es posible que verificar algunos punteros nulos no haga mucha diferencia, pero algunos códigos son demasiado nerviosos, verificando a cada momento que las puertas estén bien cerradas y nunca conciliando el sueño. No se realiza ningún procesamiento en un sistema como este porque el código se pierde en un laberinto de verificaciones y autenticaciones.
El desafío es diseñar sus capas de código para verificar los datos cuando aparecen por primera vez y luego dejarlos navegar. Claro, habrá algunos errores como resultado, pero para eso está la verificación de errores.
Demasiada complejidad teórica
Algunos programadores adoptan el estudio de los algoritmos. Disfrutan diseñando estructuras de datos y algoritmos complejos porque quieren construir el stack más eficiente posible. Cada capa o biblioteca debe ser perfecta.
Es un buen deseo, pero en muchos casos, el resultado final es una aplicación enorme que consume demasiada memoria y se ejecuta como muy lento. En teoría, será rápido, pero no lo verá hasta que haya cien mil millones de usuarios con cincuenta millones de documentos por usuario.
Gran parte de la teoría algorítmica se centra en qué tan bien escalan los algoritmos y las estructuras de datos. El análisis solo se aplica realmente cuando los datos crecen. En muchos casos, la teoría no tiene en cuenta cuánto código se necesita para ahorrar tiempo.
En algunos casos, la teoría elude detalles cruciales. Uno de los mayores sumideros de tiempo es obtener datos de la memoria principal o, peor aún, de una base de datos en la nube. Centrarse en las cuestiones prácticas de dónde se almacenan los datos y cuándo se accede a ellos es mejor que una estructura de datos elaborada.
No existe suficiente complejidad teórica
La otra cara de la moneda de empantanarse en la teoría de la programación es ignorar el lado teórico de una estructura de datos o algoritmo. El código escrito de esta manera podría ejecutarse sin problemas en los datos de prueba, pero atascarse en el momento de la implementación, cuando los usuarios comienzan a introducir sus registros en el sistema.
Adaptar bien la escala es un desafío y, a menudo, es un error pasar por alto las formas en que la adaptación de escala puede afectar la forma en que se ejecuta el sistema. A veces, es mejor considerar estos problemas durante las primeras etapas de la planificación, cuando el pensamiento es más abstracto. Algunas características, como comparar cada entrada de datos con otra, son inherentemente cuadráticas, lo que significa que sus optimizaciones pueden crecer exponencialmente más lento. Volver hacia lo que usted promete puede marcar una gran diferencia.
Pensar en cuánta teoría aplicar a un problema es un metaproblema porque la complejidad a menudo aumenta exponencialmente. A veces, la mejor solución es una iteración cuidadosa con mucho tiempo para las pruebas de carga. Un viejo dicho afirma que "la optimización prematura es una pérdida de tiempo”. Comience con un programa básico, pruébelo y luego corrija las partes más lentas.
Demasiada fe en la inteligencia artificial
Estamos en un momento en el que se hace evidente que los algoritmos de inteligencia artificial pueden ofrecer resultados sorprendentes. El resultado es sorprendentemente realista y mejor de lo esperado. Muchos creen que ha llegado la era de la computadora inteligente.
La inteligencia artificial a veces proporciona datos que son increíblemente útiles. Los programadores han cambiado los motores de búsqueda por modelos de lenguaje extenso porque no soportan todos los anuncios y funciones "amplificadas” creadas por humanos. Desconfían de la interferencia humana y confían en el aprendizaje automático.
Sin embargo, es importante reconocer exactamente qué pueden hacer los algoritmos y cómo funcionan. Los sistemas de aprendizaje automático analizan los datos y luego crean una función elaborada que los imita. Son como loros inteligentes cuando entregan texto. El problema es que están programados para entregar todo con la misma autoridad confiada, incluso cuando están completamente equivocados. En el peor de los casos, una inteligencia artificial puede estar terriblemente equivocada y no darse cuenta al igual que nosotros.
No existen suficientes datos de entrenamiento
Un modelo de inteligencia artificial es tan bueno como sus datos de entrenamiento. Ahora que los algoritmos de aprendizaje automático son lo suficientemente buenos para que cualquiera los ejecute por capricho, se llamará a los programadores para que los conecten al stack para cualquier proyecto que esté listo.
El problema es que las herramientas de inteligencia artificial siguen siendo espeluznantes e impredecibles. Pueden ofrecer grandes resultados y también pueden cometer grandes errores. A menudo, el problema es que los datos de entrenamiento no son lo suficientemente amplios o representativos.
Un "cisne negro” es un escenario que no fue cubierto por los datos de entrenamiento. Son raros, pero pueden confundir por completo a una inteligencia artificial. Cuando no se encuentran eventos en los datos de entrenamiento, la inteligencia artificial puede producir una respuesta aleatoria.
Los programadores normalmente no están entrenados para la recopilación de datos. Entrenar un modelo de inteligencia artificial significa recopilar y curar datos, en lugar de solo escribir lógica. Es una mentalidad diferente a la que estamos acostumbrados, pero es esencial para crear modelos de inteligencia artificial confiables.
Confiar su seguridad a cajas mágicas
¿Preocupado por la seguridad? Solo agregue algo de criptografía. No se preocupe, afirma el vendedor: simplemente funciona.
Los programadores de computadoras son un grupo afortunado. Después de todo, los informáticos siguen creando maravillosas bibliotecas llenas de infinitas opciones para corregir los problemas de nuestro código. El único problema es que la facilidad con la que podemos aprovechar el trabajo de otra persona también puede ocultar problemas complejos que pasan desapercibidos o, peor aún, introducen nuevos obstáculos en nuestro código.
El mundo apenas comienza a comprender el problema de compartir demasiado código en demasiadas bibliotecas. Cuando apareció el error Log4j, muchos gerentes se sorprendieron al encontrarlo profundamente incrustado en su código. Mucha gente ha llegado a confiar en la herramienta que se puede encontrar dentro de las bibliotecas, que están dentro de otras bibliotecas, que se incluyeron en el código que se ejecuta como un servicio independiente.
A veces, el problema no está solo en una biblioteca sino en un algoritmo. La criptografía es una fuente importante de debilidad aquí, afirma John Viega, coautor de 24 Deadly Sins of Software Security: Programming Flaws and How to Fix Them. Demasiados programadores asumen que pueden vincularse en la biblioteca de encriptación, presionar un botón y tener una seguridad inquebrantable.
El Instituto Nacional de Estándares y Tecnología, por ejemplo, acaba de anunciar que retirará SHA-1, un estándar inicial para construir un hash de mensaje. Se encontraron suficientes debilidades para que sea hora de seguir adelante.
La realidad es que muchos de estos algoritmos mágicos tienen debilidades sutiles. Evitarlos requiere aprender más de lo que se encuentra en la sección de "inicio rápido” del manual.
Hacer su propia criptografía
Es posible que usted no pueda confiar en otras personas, pero ¿realmente puede confiar en sí mismo? A los desarrolladores les encanta soñar con escribir sus propias bibliotecas. Pero pensar que usted conoce una forma mejor de codificar puede volver a atormentarle.
"Hacer su propia criptografía es un cartel de bienvenida para los atacantes”, afirma John Viega, señalando que incluso los expertos cometen errores cuando intentan evitar que otros encuentren y exploten las debilidades en sus sistemas.
Entonces, ¿En quién confía? ¿En usted mismo o los llamados expertos que también cometen errores?
Podemos encontrar la respuesta en la gestión de riesgos. Muchas bibliotecas no necesitan ser perfectas, por lo que es más probable que agarrar una caja mágica sea mejor que el código que usted mismo escribe. La biblioteca incluye rutinas escritas y optimizadas por un grupo. Pueden cometer errores, pero el proceso más amplio eliminará muchos de ellos.
Confiar demasiado en el cliente
Los programadores a menudo olvidan que no tienen control total sobre su software cuando se ejecuta en la máquina de otra persona. Algunos de los peores errores de seguridad aparecen cuando los desarrolladores asumen que el dispositivo cliente hará lo correcto. Por ejemplo, el código escrito para ejecutarse en un navegador puede ser reescrito por el navegador para ejecutar cualquier acción arbitraria. Si el desarrollador no vuelve a verificar todos los datos que regresan, cualquier cosa puede salir mal.
Uno de los ataques más simples se basa en el hecho de que algunos programadores simplemente pasan los datos del cliente a la base de datos, un proceso que funciona bien hasta que el cliente decide enviar SQL en lugar de una respuesta válida. Si una página web solicita el nombre de un usuario y agrega el nombre a una consulta, el atacante podría escribir el nombre x; DROP TABLE users;. La base de datos asume diligentemente que el nombre es x, luego pasa al siguiente comando, eliminando la tabla llena con todos los usuarios.
Las personas inteligentes pueden abusar de la confianza del servidor de muchas más formas. Las encuestas web son invitaciones para inyectar sesgo. Los desbordamientos de búfer siguen siendo una de las formas más sencillas de corromper el software.
Para empeorar las cosas, pueden surgir graves agujeros de seguridad cuando se encadenan agujeros aparentemente benignos. Un programador puede permitir que el cliente escriba un archivo, asumiendo que los permisos del directorio detendrán cualquier escritura descarriada. Otro puede abrir los permisos solo para corregir un error aleatorio. Apartadas no presentan un problema; pero juntas, estas decisiones de codificación pueden otorgarle acceso arbitrario al cliente.
No confiar lo suficiente en el cliente
Demasiada seguridad también puede generar problemas. Tal vez no se trate de grandes agujeros, sino de problemas generales para toda la empresa. Los sitios de redes sociales y los anunciantes se han dado cuenta de que demasiada seguridad y una recopilación de datos intrusiva pueden desalentar la participación. La gente miente o se va.
Demasiada seguridad puede corroer otras prácticas. Hace solo unos días, me comentaron que la forma de resolver un problema con una pieza de software en particular era simplemente usar chmod 777 en el directorio y todo lo que contiene. Demasiada seguridad entorpeció el trabajo, dejándome aflojar las restricciones solo para mantener todo funcionando.
Debido a esto, muchos desarrolladores web buscan reducir la seguridad tanto como sea posible; no solo para facilitar que las personas interactúen con sus productos, sino también para ahorrarles la molestia de defender más que la cantidad mínima de datos requeridos. Una de las últimas tendencias es deshacerse de las contraseñas por completo. La gente no puede hacer un seguimiento de ellas. Entonces, para iniciar sesión, las páginas web envían un correo electrónico, de un solo uso, que no es muy diferente de un mensaje de restablecimiento de contraseña. Es un mecanismo más simple que, en última instancia, es casi igual de seguro.
Mi libro, Translucent Databases, describe varias formas en que las bases de datos pueden almacenar menos información mientras brindan los mismos servicios.
Cerrar la fuente
Uno de los desafíos más complicados para cualquier empresa es determinar cuánto compartir con los usuarios de software.
John Gilmore, cofundador de una de las primeras empresas de software de código abierto, Cygnus Solutions, afirma que la decisión de no distribuir código va en contra de la integridad de ese código. La distribución es una de las formas más fáciles de fomentar la innovación y, lo que es más importante, descubrir y corregir errores:
Un resultado práctico de abrir su código es que personas de las que nunca ha oído hablar contribuirán con mejoras a su software. Encontrarán errores e intentarán corregirlos; agregarán características; mejorarán la documentación. Incluso cuando su mejora se haya realizado de manera amateur, unos minutos de reflexión a menudo revelarán una forma más armoniosa de lograr un resultado similar.
Las ventajas son más profundas. A menudo, el código en sí se vuelve más modular y mejor estructurado a medida que otros lo recompilan y lo trasladan a otras plataformas. El simple hecho de abrir el código lo obliga a hacer que la información sea más accesible, comprensible y, por lo tanto, mejor. A medida que hacemos los pequeños ajustes para compartir el código, estos retroalimentan los resultados a la base del código.
La apertura como panacea
Se han lanzado millones de proyectos de código abierto y solo una pequeña parte ha atraído a más de unas pocas personas para ayudar a mantener, revisar o ampliar el código. En otras palabras, el "si lo construyes, ellos vendrán” de W.P. Kinsella no siempre produce resultados prácticos.
Si bien la apertura hace posible que otros participen y, por lo tanto, mejoren su código; el mero hecho de que sea abierto no servirá de mucho a menos que haya un incentivo para que los colaboradores externos hagan el trabajo. Las pasiones entre los defensores del código abierto pueden cegar a algunos desarrolladores ante la realidad de que la apertura por sí sola no previene los agujeros de seguridad, elimina los bloqueos o hace que un stack de código sin terminar sea inherentemente útil. La gente tiene otras cosas que hacer y un stack abierto de código a menudo compite con el trabajo remunerado.
Abrir un proyecto también puede agregar nuevos gastos para comunicaciones y documentación. Un proyecto de código cerrado requiere una documentación sólida para los usuarios, pero un proyecto de código abierto también requiere la documentación de la API y las hojas de ruta para el desarrollo futuro. Este trabajo adicional vale la pena para los proyectos grandes, pero puede ser una carga para los más pequeños.
Con demasiada frecuencia, el código que funciona parte del tiempo aparece en GitHub con la esperanza de que los duendes mágicos dejen de hacer zapatos y se apresuren a iniciar el compilador, una decisión que puede descarrilar el impulso de un proyecto antes de que realmente comience.
Goto Fail de Apple y la vulnerabilidad Log4j son solo dos buenos ejemplos de dónde se esconden los errores a simple vista durante años. La buena noticia es que alguien los encontró eventualmente. La mala noticia es que ninguno de nosotros sabe lo que aún no se ha encontrado.
La apertura del proyecto también puede eliminar el apoyo financiero y fomentar una especie de turba colectiva. Muchas empresas de código abierto tratan de mantener bajo su control algunas características patentadas; esto les da apalancamiento para hacer que la gente pague para apoyar al equipo central de desarrollo. Los proyectos que dependen más de los voluntarios que de los programadores pagados a menudo descubren que los voluntarios son impredecibles. Si bien la competitividad y la creatividad totalmente abiertas pueden generar excelentes resultados, algunos regresan a proyectos de código cerrado, donde la estructura, la jerarquía y la autoridad soportan el desarrollo metódico.
Basado en el artículo de Peter Wayner (InfoWorld) y editado por CIO Perú
Puede ver también: