Cómo evitar la maldición de la optimización prematura
Publicado: 2022-03-11Es casi digno de garantía, de verdad. Desde principiantes hasta expertos, desde arquitectura hasta ASM, y optimizando cualquier cosa, desde el rendimiento de la máquina hasta el rendimiento del desarrollador, es muy probable que usted y su equipo estén haciendo un cortocircuito en sus propios objetivos.
¿Qué? ¿Me? ¿ Mi equipo?
Esa es una acusación bastante fuerte para nivelar. Dejame explicar.
La optimización no es el santo grial, pero puede ser igualmente difícil de obtener. Quiero compartir con usted algunos consejos simples (y una montaña de trampas) para ayudar a transformar la experiencia de su equipo de autosabotaje a una de armonía, satisfacción, equilibrio y, eventualmente, optimización.
¿Qué es la optimización prematura?
La optimización prematura intenta optimizar el rendimiento:
- Al codificar por primera vez un algoritmo
- Antes de que los puntos de referencia confirmen que necesita
- Antes de perfilar señala dónde tiene sentido molestarse en optimizar
- En un nivel inferior al que dicta actualmente su proyecto
Ahora, soy optimista, Optimus.
Al menos, voy a fingir ser optimista mientras escribo este artículo. Por tu parte, puedes fingir que tu nombre es Optimus, así esto te hablará más directamente.
Como alguien en tecnología, probablemente a veces se pregunte cómo es posible que sea $year y, sin embargo, a pesar de todos nuestros avances, de alguna manera es un estándar aceptable para que $task consuma tanto tiempo. Quieres ser delgado. Eficiente. Increíble. Alguien como los programadores de Rockstar por los que claman esas ofertas de trabajo, pero con dotes de líder. Entonces, cuando su equipo escribe código, los alienta a hacerlo bien la primera vez (incluso si "correcto" es un término muy relativo, aquí). Saben que ese es el camino del codificador inteligente, y también el camino de aquellos que no necesitan perder el tiempo refactorizando más tarde.
Siento que. La fuerza del perfeccionismo a veces también es fuerte dentro de mí. Desea que su equipo dedique un poco de tiempo ahora para ahorrar mucho tiempo más adelante, porque todos han trabajado con su parte de "Código de mierda que escribieron otras personas (¿Qué diablos estaban pensando?)". Eso es SCOPWWHWTT para abreviar, porque sé que te gustan las siglas impronunciables.
También sé que no quieres que el código de tu equipo sea para ellos ni para nadie más en el futuro.
Entonces, veamos qué se puede hacer para guiar a su equipo en la dirección correcta.
Qué optimizar: bienvenido a esto siendo un arte
En primer lugar, cuando pensamos en la optimización de programas, a menudo asumimos de inmediato que estamos hablando de rendimiento. Incluso eso ya es más vago de lo que parece (¿velocidad? ¿uso de memoria? etc.), así que detengámonos ahí.
¡Hagámoslo aún más ambiguo! Solo al principio.
A mi cerebro lleno de telarañas le gusta crear orden siempre que sea posible, por lo que necesitaré hasta la última gota de optimismo para que considere que lo que estoy a punto de decir es algo bueno .
Existe una regla simple de optimización (del rendimiento) que dice No lo hagas . Esto suena bastante fácil de seguir con rigidez, pero no todo el mundo está de acuerdo. Tampoco estoy del todo de acuerdo con eso. Algunas personas simplemente escribirán mejor código desde el principio que otras. Con suerte, para cualquier persona, la calidad del código que escribirían en un proyecto completamente nuevo generalmente mejorará con el tiempo. Pero sé que, para muchos programadores, este no será el caso, porque cuanto más sepan, de más maneras se verán tentados a optimizar prematuramente.
Para muchos programadores... cuanto más saben, de más formas se verán tentados a optimizar prematuramente.
Así que este No lo hagas no puede ser una ciencia exacta, sino que solo pretende contrarrestar la necesidad interior típica del aficionado a la tecnología de resolver el rompecabezas. Esto, después de todo, es lo que atrae a muchos programadores al oficio en primer lugar. Lo entiendo. Pero pídeles que lo guarden , que resistan la tentación. Si uno necesita una salida para resolver acertijos en este momento , siempre puede incursionar en el Sudoku del periódico dominical, tomar un libro de Mensa o jugar al golf con algún problema artificial. Pero déjalo fuera del repositorio hasta el momento adecuado. Casi siempre , este es un camino más inteligente que la optimización previa.
Recuerde, esta práctica es lo suficientemente notoria como para que la gente se pregunte si la optimización prematura es la raíz de todos los males. (No iría tan lejos, pero estoy de acuerdo con el sentimiento).
No digo que debamos elegir la forma más descabellada que podamos pensar en cada nivel de diseño. Por supuesto que no. Pero en lugar de elegir el aspecto más inteligente, podemos considerar otros valores:
- El más fácil de explicar a su nuevo empleado
- Los que tienen más probabilidades de pasar una revisión de código por parte de su desarrollador más experimentado
- El más mantenible
- El más rápido para escribir
- El más fácil de probar
- El más portátil
- etc
Pero aquí es donde el problema se muestra difícil. No se trata solo de evitar la optimización de la velocidad, el tamaño del código, el consumo de memoria, la flexibilidad o la preparación para el futuro. Se trata de equilibrio y de si lo que estás haciendo está realmente en línea con tus valores y metas. Es completamente contextual y, a veces, incluso imposible de medir objetivamente.
¿Por qué es esto algo bueno? Porque la vida es así. Está desordenado. Nuestros cerebros orientados a la programación a veces quieren crear orden en el caos con tantas ganas que terminamos, irónicamente, multiplicándolo. Es como la paradoja de intentar obligar a alguien a que te ame. Si crees que has tenido éxito en eso, ya no es amor; mientras tanto, estás acusado de tomar rehenes, probablemente necesites más amor que nunca, y esta metáfora tiene que ser una de las más incómodas que podría haber elegido.
De todos modos, si crees que has encontrado el sistema perfecto para algo, bueno… disfruta de la ilusión mientras dure, supongo. Está bien, los fracasos son oportunidades maravillosas para aprender.
Tenga en cuenta la experiencia de usuario
Exploremos cómo la experiencia del usuario encaja entre estas posibles prioridades. Después de todo, incluso querer que algo funcione bien es, en algún nivel, una cuestión de UX.
Si está trabajando en una interfaz de usuario, sin importar qué marco o lenguaje use el código, habrá una cierta cantidad de repeticiones y repeticiones. Definitivamente puede ser valioso en términos de tiempo del programador y claridad del código para tratar de reducir eso. Para ayudar con el arte de equilibrar las prioridades, quiero compartir un par de historias.
En un trabajo, la empresa para la que trabajaba usaba un sistema empresarial de código cerrado que se basaba en una pila tecnológica obstinada. De hecho, fue tan obstinado que el proveedor que nos lo vendió se negó a realizar personalizaciones de la interfaz de usuario que no se ajustaban a las opiniones de la pila, porque era muy doloroso para sus desarrolladores. Nunca he usado su pila, así que no los condeno por esto, pero el hecho es que este intercambio de "bueno para el programador, malo para el usuario" fue tan engorroso para mis compañeros de trabajo en ciertos contextos que terminé escribiendo un complemento de terceros para volver a implementar esta parte de la interfaz de usuario del sistema. (Fue un gran impulsor de la productividad. ¡A mis compañeros de trabajo les encantó! Más de una década después, sigue ahorrando tiempo y frustración a todos).
No estoy diciendo que la opinión sea un problema en sí mismo; solo que demasiado se convirtió en un problema en nuestro caso. Como contraejemplo, uno de los grandes atractivos de Ruby on Rails es precisamente que es obstinado, en un ecosistema front-end donde uno fácilmente siente vértigo por tener demasiadas opciones. (¡Dame algo con opiniones hasta que pueda descubrir la mía!)
Por el contrario, puede tener la tentación de coronar a UX como el rey de todo en su proyecto. Un objetivo digno, pero déjame contarte mi segunda historia.
Unos años después del éxito del proyecto anterior, uno de mis compañeros de trabajo se me acercó para pedirme que optimizara la experiencia de usuario mediante la automatización de cierto escenario desordenado de la vida real que a veces surgía, para que pudiera resolverse con un solo clic. Empecé a analizar si era posible diseñar un algoritmo que no tuviera falsos positivos o negativos debido a los muchos y extraños casos extremos del escenario. Cuanto más hablaba con mi compañero de trabajo al respecto, más me daba cuenta de que los requisitos simplemente no iban a dar resultado. El escenario solo aparecía de vez en cuando, mensualmente, digamos, y actualmente le tomó a una persona unos minutos resolverlo. Incluso si pudiéramos automatizarlo con éxito, sin ningún error, tomaría siglos para que el tiempo de desarrollo y mantenimiento requerido se retribuya en términos de tiempo ahorrado por mis compañeros de trabajo. La persona que complace a la gente en mí tuvo un momento difícil para decir "no", pero tuve que interrumpir la conversación.
Así que deje que la computadora haga lo que pueda para ayudar al usuario, pero solo hasta cierto punto. Sin embargo, ¿cómo sabes hasta qué punto es eso?
Un enfoque que me gusta tomar es perfilar la UX como sus desarrolladores perfilan su código. Averigüe de sus usuarios dónde pasan más tiempo haciendo clic o escribiendo lo mismo una y otra vez, y vea si puede optimizar esas interacciones. ¿Puede su código hacer algunas conjeturas informadas sobre lo que es más probable que ingresen y convertirlo en un valor predeterminado sin entrada? Aparte de ciertos contextos prohibidos (¿confirmación de EULA sin clic?), esto realmente puede marcar una diferencia en la productividad y la felicidad de sus usuarios. Haz algunas pruebas de usabilidad en los pasillos si puedes. A veces, puede tener problemas para explicar qué es fácil de ayudar para las computadoras y qué no... pero, en general, es probable que este valor sea de gran importancia para sus usuarios.
Evitar la optimización prematura: cuándo y cómo optimizar
A pesar de nuestra exploración de otros contextos, supongamos ahora explícitamente que estamos optimizando algún aspecto del rendimiento de la máquina sin procesar para el resto de este artículo. Mi enfoque sugerido también se aplica a otros objetivos, como la flexibilidad, pero cada objetivo tendrá sus propias trampas; el punto principal es que la optimización prematura para cualquier cosa probablemente fallará.
Entonces, en términos de rendimiento, ¿qué métodos de optimización hay que seguir realmente? Vamos a profundizar en.

Esta no es una iniciativa de base, es Triple-Eh
El TL; DR es: Trabaja hacia abajo desde la parte superior. Las optimizaciones de nivel superior se pueden realizar antes en el proyecto y las de nivel inferior se deben dejar para más adelante. Eso es todo lo que necesita para obtener la mayor parte del significado de la frase "optimización prematura"; hacer las cosas fuera de este orden tiene una alta probabilidad de hacer perder el tiempo a su equipo y ser contraproducente. Después de todo, no escribes todo el proyecto en código de máquina desde el principio, ¿verdad? Así que nuestro modus operandi AAA es optimizar en este orden:
- Arquitectura
- Algoritmos
- Montaje
La sabiduría común dice que los algoritmos y las estructuras de datos son a menudo los lugares más efectivos para optimizar, al menos en lo que respecta al rendimiento. Sin embargo, tenga en cuenta que la arquitectura a veces determina qué algoritmos y estructuras de datos se pueden usar.
Una vez descubrí una pieza de software que elaboraba un informe financiero consultando una base de datos SQL varias veces para cada transacción financiera y luego hacía un cálculo muy básico en el lado del cliente. A la pequeña empresa que usaba el software le tomó solo unos meses de uso antes de que incluso su cantidad relativamente pequeña de datos financieros significara que, con computadoras de escritorio nuevas y un servidor bastante robusto, el tiempo de generación de informes ya era de varios minutos, y esto fue uno que necesitaban usar con bastante frecuencia. Terminé escribiendo una declaración SQL sencilla que contenía la lógica de suma, frustrando su arquitectura al mover el trabajo al servidor para evitar toda la duplicación y los viajes de ida y vuelta de la red, e incluso varios años de datos más tarde, mi versión podría generar el mismo informe en meros milisegundos en el mismo hardware antiguo.
A veces, no tiene influencia sobre la arquitectura de un proyecto porque es demasiado tarde para que un cambio de arquitectura sea factible. A veces, sus desarrolladores pueden eludirlo como lo hice en el ejemplo anterior. Pero si está al comienzo de un proyecto y tiene algo que decir sobre su arquitectura, ahora es el momento de optimizarlo.
Arquitectura
En un proyecto, la arquitectura es la parte más costosa de cambiar después del hecho, por lo que este es un lugar donde puede tener sentido optimizar desde el principio. Si su aplicación es para entregar datos a través de avestruces, por ejemplo, querrá estructurarla hacia paquetes de baja frecuencia y alta carga útil para evitar que un cuello de botella sea aún peor. En este caso, será mejor que tenga una implementación completa de Tetris para entretener a sus usuarios, porque una ruleta de carga simplemente no va a ser suficiente. (Bromas aparte: hace años estaba instalando mi primera distribución de Linux, Corel Linux 2.0, y estaba encantado de que el largo proceso de instalación incluyera precisamente eso. Habiendo visto las pantallas de infomerciales del instalador de Windows 95 tantas veces que las había memorizado, este fue un soplo de aire fresco en ese momento.)
Como ejemplo de que el cambio de arquitectura es costoso, la razón por la que el informe SQL mencionado anteriormente es tan poco escalable en primer lugar queda clara en su historial. La aplicación había evolucionado con el tiempo, desde sus raíces en MS-DOS y una base de datos personalizada de cosecha propia que ni siquiera era originalmente multiusuario. Cuando el proveedor finalmente hizo el cambio a SQL, el esquema y el código de informes parecen haber sido portados uno por uno. Esto les permitió años de impresionantes mejoras de rendimiento de más del 1000% para esparcir a lo largo de sus actualizaciones, siempre que lograron completar el cambio de arquitectura al hacer uso de las ventajas de SQL para un informe determinado. Bueno para negocios con clientes fijos como mi entonces empleador, y claramente intentando priorizar la eficiencia de codificación durante la transición inicial. Pero satisfacer las necesidades de los clientes, en algunos casos, con la misma eficacia con la que un martillo gira un tornillo.
La arquitectura se trata en parte de anticipar hasta qué punto su proyecto necesitará poder escalar y de qué manera. Debido a que la arquitectura es de tan alto nivel, es difícil ser concreto con lo que se debe y no se debe hacer sin limitar nuestro enfoque a tecnologías y dominios específicos.
Yo no lo llamaría así, pero todos los demás lo hacen
Afortunadamente, Internet está plagado de sabiduría recopilada sobre la mayoría de los tipos de arquitectura jamás soñados. Cuando sabes que es hora de optimizar tu arquitectura, investigar las trampas se reduce a descubrir la palabra de moda que describe tu brillante visión. Lo más probable es que alguien haya pensado de la misma manera que usted, lo haya intentado, haya fallado, repetido y publicado al respecto en un blog o un libro.
La identificación de palabras de moda puede ser complicada de lograr simplemente buscando, porque para lo que usted llama FLDSMDFR, alguien más ya acuñó el término SCOPWWHWTT, y describen el mismo problema que está resolviendo, pero usando un vocabulario completamente diferente al que usted usaría. ¡Comunidades de desarrolladores al rescate! Ingrese a StackExchange o HashNode con una descripción tan completa como pueda, además de todas las palabras de moda que su arquitectura no es , para que sepan que realizó una investigación preliminar suficiente. Alguien estará encantado de iluminarte.
Mientras tanto, algunos consejos generales pueden ser buenos elementos de reflexión.
Algoritmos y Ensamblaje
Dada una arquitectura propicia, aquí es donde los codificadores de su equipo obtendrán la mayor cantidad de T-bling por su tiempo. La evitación básica de la optimización prematura también se aplica aquí, pero sus programadores harían bien en considerar algunos de los detalles en este nivel. Hay tanto en lo que pensar cuando se trata de detalles de implementación que escribí un artículo separado sobre optimización de código dirigido a programadores de primera línea y senior.
Pero una vez que usted y su equipo han implementado algo sin optimizar en términos de rendimiento, ¿realmente lo dejan en No hacerlo ? ¿ Nunca optimizas?
Estás bien. La siguiente regla es, solo para expertos, no lo hagas todavía .
¡Es hora de comparar!
Tu código funciona. Tal vez es tan lento que ya sabe que necesitará optimizar, porque es un código que se ejecutará con frecuencia. Tal vez no esté seguro, o tenga un algoritmo O(n) y piense que probablemente esté bien. No importa cuál sea el caso, si alguna vez vale la pena optimizar este algoritmo, mi recomendación en este punto es la misma: ejecute un punto de referencia simple.
¿Por qué? ¿No está claro que mi algoritmo O(n³) no puede ser peor que cualquier otra cosa? Bueno, por dos razones:
- Puede agregar el punto de referencia a su conjunto de pruebas, como una medida objetiva de sus objetivos de rendimiento, independientemente de si se están cumpliendo actualmente.
- Incluso los expertos pueden, sin darse cuenta, hacer las cosas más lentas. Incluso cuando parece obvio. Realmente obvio
¿No me crees en ese segundo punto?
Cómo obtener mejores resultados con hardware de $1400 que con hardware de $7000
Jeff Atwood de StackOverflow señaló una vez que a veces (generalmente, en su opinión) puede ser más rentable simplemente comprar un mejor hardware que gastar el valioso tiempo del programador en la optimización. Bien, supongamos que ha llegado a una conclusión razonablemente objetiva de que su proyecto encajaría en este escenario. Supongamos además que lo que está tratando de optimizar es el tiempo de compilación, porque es un proyecto de Swift considerable en el que está trabajando, y esto se ha convertido en un cuello de botella bastante grande para los desarrolladores. ¡Hora de comprar hardware!
¿Qué deberías comprar? Bueno, obviamente, yen por yen, el hardware más caro tiende a funcionar mejor que el hardware más barato. Entonces, obviamente, una Mac Pro de $ 7,000 debería compilar su software más rápido que una Mac Mini de gama media, ¿verdad?
¡Incorrecto!
Resulta que a veces más núcleos significa una compilación más eficiente... y en este caso particular, LinkedIn descubrió por las malas que lo contrario es cierto para su pila.
Pero he visto a la gerencia que cometió un error más: ni siquiera compararon el antes y el después, y descubrieron que una actualización de hardware no hacía que su software se "sintiera" más rápido. Pero no había forma de saberlo con seguridad; y además, todavía no tenían idea de dónde estaba el cuello de botella, por lo que seguían descontentos con el desempeño, ya que habían gastado el tiempo y el dinero que estaban dispuestos a asignar al problema.
OK, ya he comparado. ¿Puedo optimizar todavía ?
Sí, suponiendo que haya decidido que necesita hacerlo. Pero tal vez esa decisión deba esperar hasta que se implementen más o todos los otros algoritmos también, para que pueda ver cómo encajan las partes móviles y cuáles son las más importantes a través de la creación de perfiles. Esto puede ser a nivel de aplicación para una aplicación pequeña, o puede que solo se aplique a un subsistema. De cualquier manera, recuerde, un algoritmo en particular puede parecer importante para la aplicación en general, pero incluso los expertos, especialmente los expertos, son propensos a diagnosticarlo erróneamente.
Piensa antes de interrumpir
"No sé ustedes, pero..."
Como último elemento de reflexión, considere cómo puede aplicar la idea de la falsa optimización a una visión mucho más amplia: su proyecto o empresa en sí, o incluso un sector de la economía.
Lo sé, es tentador pensar que la tecnología salvará el día y que podemos ser los héroes que lo hagan posible.
Además, si no lo hacemos nosotros, alguien más lo hará.
Pero recuerda que el poder corrompe, a pesar de las mejores intenciones. No voy a vincular a ningún artículo en particular aquí, pero si no ha vagado por ninguno, vale la pena buscar algunos sobre el impacto más amplio de la disrupción de la economía y a quiénes esto a veces sirve en última instancia. Es posible que se sorprenda de algunos de los efectos secundarios de tratar de salvar el mundo a través de la optimización.
Posdata
¿Notaste algo, Optimus? La única vez que te llamé Optimus fue al principio y ahora al final. No te llamaron Optimus a lo largo del artículo. Seré honesto, lo olvidé. Escribí todo el artículo sin llamarte Optimus. Al final, cuando me di cuenta de que debía volver atrás y esparcir tu nombre por todo el texto, una vocecita dentro de mí dijo, no lo hagas .
