La lucha duradera de la escritura estática frente a la dinámica: TypeScript no ayudará



Cuando mi amigo y yo estábamos en edad escolar y aspiramos a convertirnos en desarrolladores de software, soñamos con diseñar algunas cosas interesantes juntas, como un juego o una aplicación mega útil.

Elegí aprender C ++ y C #, eligió JavaScript. Terminamos la escuela, nos graduamos de nuestras universidades, servimos en el ejército y comenzamos nuestros trabajos. Tuvimos un tiempo bastante ocupado en ingeniería de software industrial, con muchos trabajos y puestos diferentes, y después de que todo comenzó a desgastarnos, recordamos dónde había comenzado todo.

Después de reunirnos finalmente como desarrolladores maduros, decidimos trabajar en nuestro propio proyecto: un videojuego en 2D. Como el dominio de mi amigo era front-end y yo era un desarrollador completo, nuestra elección inmediata de plataforma de desarrollo fue un navegador de Internet. Como solo estaba acostumbrado a trabajar con TypeScript cuando diseñaba front-end, pensamos, ok, no hay problema, después de todo, TS es solo JavaScript a escala. Usémoslo y las cosas saldrán bien. ¡Si supiera lo equivocado que estaba! Cuando comenzamos a discutir el proyecto, nos encontramos con un gran abismo de malentendidos entre nosotros.

Aquí estaba mi visión del juego. Estaba como, ok, tenemos tipos como juego, herramienta, elemento, mapa, ubicación. Tengo una comprensión básica de cómo funcionan juntos, así que los describo, compilo el proyecto y funciona. Después de todo, el compilador ha verificado mi código, y lo hice bien. Luego, comienzo a escribir código que usa esos tipos. Me hacen la vida mucho más fácil. Mi IDE me da información sobre herramientas y comprueba cualquier error. Si se puede compilar un proyecto, probablemente funcione. Pasé algo de esfuerzo en la descripción del tipo y arrojó resultados. Ese es mi enfoque en pocas palabras.

Mi amigo tuvo la idea opuesta: saltar a la escritura de códigos de inmediato sin tomarse el tiempo para describir los tipos. No estaba listo para definir el problema como una familia de tipos. No estaba interesado en usarlo como base, ya que no veía el problema como un conjunto de clases, tipos, registros de nada por el estilo. Pensé que era inconcebible. Ambos teníamos un punto, es solo que nuestros puntos eran mutuamente excluyentes.

En serio, estuvimos hablando durante horas, pero cada uno decía lo suyo, como si estuviéramos hablando idiomas diferentes. Eso sí, no podría culparnos de que estemos atrapados en nuestras viejas costumbres. Hace apenas un año, migré sin problemas del mundo de la programación orientada a objetos al mundo de la programación funcional y viceversa. Además, pasé bastante tiempo aprendiendo JS, y él, aprendiendo varios idiomas estáticamente escritos.

Sin embargo, para cualquier desarrollador, la tecnología que usaron para su primer trabajo real a menudo los define en la medida en que dos adultos experimentados simplemente carecen de la paciencia para escucharse mutuamente. Durante estos años de ingeniería de software, nuestras visiones se formaron de maneras tan diferentes que nuestros enfoques para la resolución de problemas simplemente no encajaban bien.

Al final abandonamos la idea de trabajar en equipo. Su primera respuesta podría ser que el problema estaba en nuestras personalidades. Puede que tengas razón, pero también vi que esto le sucedía a otros en la industria.

La diferencia fundamental e irreconciliable entre tipeo estático y dinámico


Mi código proporciona la solución al problema de cómo trabajar con él, mientras que el código de defensores de la escritura dinámica experimentada resuelve el problema de cómo funciona. Ambas mentalidades son legítimas y están respaldadas por las herramientas existentes, pero solo una puede tener la máxima prioridad en cualquier momento.

La escritura estática es buena para proyectos a gran escala que involucran a cientos de desarrolladores que trabajan en ellos durante años, mientras que la escritura dinámica es buena para equipos y proyectos más pequeños que a menudo requieren código de solo escritura. La escritura dinámica le permite ahorrar tiempo y esfuerzos al comienzo del desarrollo, mientras que la escritura estática le da un impulso al final.

La idea de poner los tipos primero ha afectado seriamente mi pensamiento como desarrollador. Después de haber elegido C # al comienzo de mi carrera, tengo una escritura estática codificada en mi mentalidad, y ahora estoy pagando el precio de esta inflexibilidad. Una vez que veo una tarea, trato de imaginar su solución como un conjunto de tipos y reglas de su relación. Cuando estoy desarrollando un módulo, mi primer paso es definir los tipos que opera y usa para interactuar con su entorno. Simplemente no recuerdo cómo solía resolver problemas antes de eso.

Todo el proceso de aprender a programar en Java se trata de aprender a diseñar y usar tipos. .NET CLR, el tiempo de ejecución de C #, se basa en tipos y para tipos. La escritura estática se encuentra en el núcleo del paradigma de programación orientada a objetos (hola, clases JS, decidí darle un descanso). Las implementaciones canónicas de la mayoría de los patrones OOP están saturadas con la palabra clave Interface, lo que no tiene ningún sentido en un lenguaje de tipo dinámico.

Los patrones de diseño en sí mismos son conceptos de lenguaje cruzado, pero ¿alguien puede decirme por qué demonios necesitaría el patrón de estado en un lenguaje de tipo dinámico? ¿Qué hay de Builder? Estos patrones no tienen nada que ver con el desarrollo, son principalmente sobre tipos. Los tipos y OOP tienen un vínculo estrecho.

No puede construir su lógica de negocios en los tipos y, sin embargo, no sabe nada sobre ellos cuando comienza a escribir código. Es por eso que tenemos desarrolladores front-end que arman su código con una increíble cantidad de pruebas unitarias que verifican específicamente la base del código para detectar cualquier tipo de error.
Todos sabemos que la protección basada en la cobertura del código es una ilusión. Las pruebas se escriben manualmente y, por definición, son menos confiables que el sistema incorporado de verificación de tipos disponible en el idioma.

No significa que los idiomas escritos dinámicamente no tengan sentido (aunque confieso que creo que no). Significa que, al usarlos, debe alejarse de OOP como paradigma dominante. Toda esta unidad de datos y operaciones basadas en datos es para personas que tienen mecanografía estática.

Los desarrolladores con los que me he encontrado no creen que el tipeo estático afecte la forma de codificación en ninguna medida, por lo que simplemente escriben su código como si estuvieran usando un lenguaje dinámico, solo que agregan la verificación de tipo. Creo que esto es inherentemente incorrecto. Es especialmente obvio en el caso del front-end moderno.
Lo sé, lo sé, hay un tabú para criticar a los desarrolladores front-end. Mi amigo y yo una vez armamos un robot de inteligencia artificial que controlaba a los usuarios de Twitter, y conseguimos que Brendan Eich lo atacara. En serio, el creador de JavaScript tuvo un intercambio de comentarios con nuestra red neuronal.



Por alguna razón, estos tipos simplemente no están listos para vivir en un mundo donde su visión tiene deficiencias tangibles. Es por eso que critico solo a aquellos de ellos que manipulan mi proyecto definitivamente tipado para reelaborarlo de una manera relajada, "De cualquier manera".

Seguiríamos viviendo en nuestros pequeños mundos, pero TypeScript nos unió


Tómeme por ejemplo: debido a las restricciones de tipo, mi código es imposible de usar incorrectamente. Cuando estoy trabajando en un proyecto, confío en que otros también usen tipos. Entonces mi código funcionará tal como está diseñado. Por lo tanto, deliberadamente no cubro todos los casos en los que este código se puede usar incorrectamente (porque escribir lo hace imposible). Pero luego un desarrollador de JS se une a mi proyecto, toma mi tipo, lo envuelve en Any y comienza a usarlo incorrectamente, lo que resulta en errores difíciles de replicar.

Los desarrolladores de JavaScript confían en que TypeScript es el mismo JS anterior, pero con una opción para agregar verificaciones de tipo estático en caso de que las necesiten. Esto esta mal. TypeScript es cien veces más poderoso, pero solo están interesados ​​en una fracción de su potencial.

Su argumento asesino es que TypeScript es solo un superconjunto de JS. En la práctica, uno no puede ignorar el hecho de que TypeScript es un lenguaje independiente, incluso si uno es un maldito rey del front-end. Porque requiere un enfoque diferente: el de verificación estática, no dinámica.

El principal beneficio del tipeo estático es que le brinda garantías. Si lo usa en un módulo y elige no usarlo en otro, entonces solo pierde su tiempo y energía en describir y diseñar esos tipos, sin obtener ninguna garantía.

Muchos piensan que TypeScript es un compromiso de los sistemas de tipos entre JS y Java. Bueno, no es un compromiso de ningún tipo, tiene un sistema de tipo especial propio.

Lo peor de todo es que hoy en día uno de cada dos puestos frontales requiere dominio de TypeScript. Esto estimula a los desarrolladores de JS a echar un vistazo a las características de TypeScript e inmediatamente saltar a escribir código en él, generando una proliferación de prácticas dañinas. Las situaciones ocurren cuando realmente no necesitan una verificación de tipo estático, pero se les impuso un poco, por lo que se metieron con eso. Necesitamos finalmente reconocer que los enfoques de programación con tipeo estático vs dinámico están en conflicto entre sí y no se pueden mezclar.

Veo JavaScript como una gran herramienta para escribir código de pirateo rápido que proporciona soluciones sin resolver abstracciones innecesarias. El ejemplo más escandaloso aquí es el patrón Ice Factory. Puede alimentar sus instancias y las envolverá en la inmutabilidad de tiempo de ejecución. Si proceso mi objeto a través de dicha fábrica, devolverá su equivalente, pero si trato de cambiar una de sus propiedades, arrojará una excepción. WAT?!?

El patrón surgió porque los desarrolladores front-end escucharon en algún lugar sobre la inmutabilidad genial, por lo que decidieron arrastrar esta mierda a su idioma, que lo necesita como un agujero en la cabeza. En TypeScript, puedo diseñar una fábrica similar, pero con una restricción en el tiempo de compilación de mutaciones y sin ninguna excepción.

Por otro lado, apenas necesito esto porque existe la programación funcional pura. Tomemos F #, Haskell, OCaml, Clojure, ReasonML, por ejemplo: tienen una prohibición de mutabilidad lista para usar. Pero algo me dice que si un desarrollador front-end pone sus manos en un lenguaje funcional, estará dispuesto a actualizarlo para que su comportamiento sea similar al de JavaScript.

Eso es porque elegir tu religión de mecanografía es un boleto de ida. Todas las soluciones intermedias proporcionan una ilusión de compromiso. O confías en los tipos o no. No sé si mi vida sería diferente si hubiera comenzado a aprender C # y JavaScript en paralelo. Hoy estoy tan desesperadamente identificándome con mi mentalidad que simplemente no veo ninguna ventaja de la escritura dinámica (y no deseo verlas). Existen, solo fuera de mi rango de visibilidad, por lo que todo lo que puedo hacer es cerrar los ojos ante ellos como lo hago ante cualquier fenómeno que tenga que soportar en este mundo. Sé que estoy equivocado, pero tengo que trabajar aquí y ahora, y no tengo el presupuesto para seguir sentado en la cerca.

Así que no quiero buscar compromisos, en su lugar lo aclararé. Si solo está dando sus primeros pasos en el desarrollo, ¡comience con la escritura estática!

Source: https://habr.com/ru/post/437844/


All Articles