Introducción a desarrollo guiado por especificación TDD

Introducción a desarrollo guiado por especificación TDD

En realidad TDD significa Desarrollo guiado por pruebas, del inglés Test Driven Development. Lamentablemente es un nombre que no le hace justicia, lo que realmente guía al desarrollo siguiendo esta práctica, son las especificaciones o comportamientos.

Ya sé, ya sé, para la gente que sabe que es BDD seguro se pregunta, ¿Guiado por comportamiento?, ¿Eso no es BDD?, pero el contenido de esta publicación clarificará la estrecha relación entre TDD y BDD. Mostraremos una perspectiva en la que en esencia, son lo mismo.

Origen de TDD en la vida

Una de las características principales y de hecho el primer paso cuando se aplica desarrollo guiado por pruebas es el entender bien el problema a resolver. Es por eso que se escriben las pruebas primero, esta idea ha existido desde que el ser humano hace cualquier tipo de trabajo, ejemplo:

Un cazador enseña a su hijo como cazar una cebra y le indica que el mejor lugar para clavarla es justo debajo de la oreja, donde puede atravesar su cráneo, pero ese es un tiro muy difícil. Le dice que el tiro más facil es “Apuntar a las rayas que la cebra tiene en el pecho”. Mientras el padre dibuja las rayas en el tronco de un árbol (simplicidad).

– Sigue practicando el tiro a una distancia de 20 pasos hasta que claves la lanza correctamente”, luego -“No espera, sostén la lanza así”, más tarde – “Si, así está mejor, continua así” … – “Ahora, pídele a tu tío que te enseñe cómo hacer que la cebra se acerque a una distancia de 10 pasos” (iterativo e incremental, lotes pequeños, ciclo de retroalimentacion)

Bob Allen de codecraftsmansaturdays

El objetivo del padre y del hijo es que este último pueda cazar una cebra (enfoque). 

Lo ideal es clavar la cebra justo debajo de su oreja. Esto es una tarea difícil para el hijo, entonces utilizando un proceso iterativo e incremental, se empieza con el paso más fácil hasta ahora conocido. El paso es apuntar al pecho de la cebra a una distancia de 20 pasos (simplicidad).

Después surge otro inconveniente, sostener la lanza correctamente, por eso se trabaja en ello. Posteriormente surge otra cosa, hacer que la cebra se acerque hasta 10 pasos de distancia (lotes pequeños, iteraciones y vamos incrementando hasta la meta). Aunque no se menciona la razón, podemos suponer que, el padre se quiere asegurar que el hijo pueda clavar la cebra en el primer tiro

Ahora bien, de momento se cumple el objetivo (enfoque), cazar a una cebra, por lo que el padre y el hijo pueden detenerse ahí. Conforme se empieza a trabajar en la solución, muchas veces esta es mucho más simple de lo que se pensaba en el inicio.

El punto es que este proceso iterativo e incremental permite ahorrarnos tiempo, dinero y esfuerzo implementando una solución mucho más simple. Y si te preguntas cuál es la solución complicada, esa es la de “clavarla justo debajo de la oreja”.

TDD está basado en principios ágiles y el manifiesto

Todo este proceso natural e inconsciente sobre como clavar a una cebra, descrito en la sección anterior, gira alrededor de principios ágiles. Y estos principios a su vez se relacionan fuertemente con el manifiesto ágil.

  1. Simplicidad, el arte de maximizar el trabajo no realizado, es esencial.
    1. Algunos conocerán los principios KISS y YAGNI en programación.
    2. Así también algunos habrán escuchado NO DESPERDICIAR de Lean manufacturing
    3. LOTES PEQUEÑOS en cada iteración, de Lean Manufacturing
  2. Ciclo de retroalimentación
  3. Proceso iterativo e incremental

Estos son los más importantes desde mi punto de vista y tal vez los que más podemos notar en el día a día.

Origen de TDD en programación

Para darte una idea del origen real de TDD, voy a citar varias fuentes.

No teníamos manuales para ENIAC. Aprendimos a programar estudiando los diagramas de bloques lógicos. Que bendición. Desde el principio supe cómo funcionaban las computadoras. Nos ganamos el respeto de los ingenieros desde el principio porque realmente sabíamos lo que estábamos haciendo y pudimos depurar mejor que ellos porque teníamos nuestros programas de prueba y nuestro conocimiento de la computadora.

Betty Jean Jennings Bartik, ENIAC programmer 1946

El primer ataque al problema puede realizarse antes de comenzar la codificación. Para determinar completamente la precisión de las respuestas, es necesario tener un caso de verificación calculado a mano con el cual comparar las respuestas que luego serán calculadas por la máquina.

Esto significa que las máquinas de programas almacenados realmente nunca se utilizan para un problema de una sola vez. Siempre debe haber un elemento de iteración para que valga la pena. Los cálculos manuales se pueden realizar en cualquier momento durante la programación. Sin embargo, con frecuencia las computadoras son operadas por expertos en computación para preparar los problemas como un servicio para ingenieros o científicos.

En estos casos, es muy deseable que el “cliente” prepare el caso de verificación, en gran parte porque tal procedimiento puede señalar errores lógicos y malentendidos entre el programador y el cliente. Si el cliente va a preparar la solución de prueba, lo mejor para él es comenzar mucho antes del problema real, ya que para cualquier problema importante se necesitarán varios días o semanas para calcular la prueba

Digital Computer Programming, D.D. McCracken, 1957

Otro dato, de la época de John Von Neumann, en una entrevista realizada a Gerald. M. Weingberg por Michael Bolton

Jerry: No llamábamos a esas cosas por esos nombres en ese entonces, pero si miras mi primer libro (Computer Programming Fundamentals, Leeds & Weinberg, first edition 1961 —MB) y muchos otros desde entonces, verás que siempre fue así como pensabamos que era la única forma lógica de hacer las cosas. Lo aprendí de Bernie Dimsdale, quien lo aprendió de von Neumann.

… luego me encontré con Bernie (en 1957), quien me mostró cómo la gente verdaderamente inteligente hacía las cosas. Mi ego estaba un poco en shock al principio, pero luego me di cuenta de que si von Neumann hacía las cosas de esta manera, yo debería hacerlo.

John von Neumann fue mucho más inteligente de lo que yo seré jamás, o de lo que la mayoría de la gente será, esto significa es que debemos aprender de él …

Geral M. Weinberg

La descripción original de TDD estaba en un libro antiguo sobre programación. El libro decía, tome la cinta de entrada, escriba manualmente lo que espera en la cinta de salida, luego programe hasta que la cinta de salida real coincida con la salida esperada.

Después de escribir el primer marco xUnit en Smalltalk, recordé haber leído esto y lo probé. Ese fue el origen de TDD para mí. Al describir TDD a programadores mayores, a menudo escucho: “Por supuesto. ¿De qué otra manera podrías programar? ” Por lo tanto, me refiero a mi papel como “redescubrir” TDD.

Kent Beck

Si analizamos un poco estos últimos datos, en esa época se necesitaban ejecutar pruebas y resolver el problema en papel antes de correr el programa en la computadora porque era muy tardado volver a ejecutar el programa después de una falla ya sea por el tiempo de “ejecución” o porque las máquinas de esa época simplemente no eran tan sofisticadas como las de hoy en día.

Todo esto nos indica la importancia de las pruebas y así no retrasar el tiempo necesario para que la solución funcione correctamente y con menos esfuerzo, en la actualidad no ha cambiado mucho, eso sí, tenemos otros problemas, además el software que se desarrolla hoy en día tiene mayor escala y las funcionalidades son más sofisticadas.

Otro punto que nos enseñan estos datos, es que la comunicación entre los involucrados desde un principio es importante para evitar errores lógicos y malentendidos entre el cliente y el programador. Aquí los clientes también pueden ser  usuarios, personas de UX y product owners.

Lo descrito por Kent Beck es muy interesante, programadores más viejos están de acuerdo en que no existe una mejor manera de programar que la de escribir las pruebas primero.

En conclusión, estos datos nos abre los ojos a que las pruebas siempre han sido parte del proceso de diseño y codificación. Y que se hacen lo más antes posible (test-first programming)

OK, ¿Qué es TDD?

Es una disciplina que te permite eliminar el miedo a modificar el código de un software, lo que resulta en la limpieza constante del código, por lo que se asegura que el software escale de manera fluida, sin retrasos y dolores de cabeza. Permitiendo tener un software lo suficientemente flexible para poder adaptarse a cualquier cambio.

TDD es una práctica ágil, por consiguiente basada en principios ágiles que nos permite tener una guía en el desarrollo de software para nunca perder el enfoque.

TDD se basa en la mentalidad de prevenir defectos en lugar de resolverlos después, es obvio que no se puede prevenir todos los casos, pero si un gran número con un conjunto de pruebas bien hechas.

Históricamente hablando, ha probado ser en la gran mayoría de los casos, la mejor manera de crear productos de calidad. Significa Desarrollo guiado por pruebas (Test Driven Development), pero como mencionamos antes, la palabra especificación, la describe mejor, algo así como SDD, Specification Driven Development.

Red-Green-Refactor

En la práctica, consiste en tres pasos segun Kent Beck:

  1. RED. Escribe una pequeña prueba que no funcione, tal vez ni siquiera compile al principio
  2. GREEN. Haz que la prueba funcione rápidamente, cometiendo los pecados necesarios en el proceso.
  3. REFACTOR. Elimina todo el código duplicado creado

Ahora bien, cuáles son los beneficios principales:

  • Ciclo de retroalimentación y comunicación
  • Crear calidad, no medirla
  • Herramienta de diseño
  • Elimina el miedo a cambiar o mejorar el código
  • Documentación precisa

TDD como ciclo de retroalimentación y comunicación

Las organizaciones que diseñan sistemas (en el sentido amplio) están limitadas a producir diseños que son copias de las estructuras de comunicación de dichas organizaciones.

Melvin E. Conway, 1968

Individuos e interacciones sobre procesos y herramientas

Manifiesto ágil

Un ciclo de retroalimentación es muy importante para todo lo que hacemos, entre más pronto tengamos retroalimentación, más pronto podemos reaccionar y corregir nuestras acciones.

Cualquier metodología, framework o proceso ágil tiene como uno de sus elementos principales uno o varios ciclos de retroalimentación. Por ejemplo, en scrum, cada iteración llamada sprint es un ciclo de retroalimentación, las retroespectivas es claramente otro. Todos los días el daily es un ciclo retroalimentación. Cada release es un ciclo retroalimentación.

Estos ciclos de retroalimentación nos permiten tener una excelente comunicación y de forma constante.

TDD es lo mismo que BDD

Pero hablando en conjunto, desde el punto de vista técnico y de negocio, trasladado al código, el cual es el que nos permite cumplir con el comportamiento deseado de una determinada aplicación, ¿Existe algún ciclo de retroalimentación? ¿Qué nos permita corregir errores lo más pronto posible?

Respuesta ante el cambio sobre seguir un plan

Manifiesto ágil

Claro que sí, existen dos prácticas muy útiles para esto, una se llama BDD y la otra TDD, estas prácticas responden dos preguntas muy importantes:

  1. ¿Estamos construyendo el software correcto?, ¿El que el usuario final necesita?
  2. ¿Estamos construyendo correctamente el software, ¿Con la suficiente flexibilidad que permita adaptarse a las nuevas necesidades de los usuarios?

Aunque BDD y TDD parecen que son dos cosas diferentes, en realidad son lo mismo, TDD se empezó a utilizar primero, de hecho se creaban pruebas de aceptación, de integración y unitarios usando TDD. Luego, apareció ATDD y BDD (con ayuda de DDD) como extensiones de TDD.

En realidad BDD aporta a TDD:

  1. La mejora en la retroalimentación y comunicación entre todo el equipo, programadores y no programadores
  2. Comportamientos documentados con mayor claridad y precisión

Lo que escribimos en código de prueba son el reflejo de las especificaciones, el objetivo primordial de las pruebas no es verificar exactamente que una clase o método se ejecute correctamente, sino cómo se comporta según el deseo implícito de los usuarios finales, y este deseo implícito se plasma explícitamente en las especificaciones creadas antes de empezar escribir código de programación. 

Lo anterior es muy importante porque nos da el objetivo y el foco correcto a nuestro desarrollo.  Esta idea la podemos decir de las siguientes formas, desarrollo guiado por:

  • Especificación.
  • El dominio
  • Comportamiento
  • Pruebas, la definición más conocida y que lamentablemente genera más confusión.

TDD para crear calidad, no para medirla

La palabra Testing (Prueba) en el mundo de QA (Quality assurance), quiere decir probar después de que el software está desarrollado, probar y probar hasta comprobar que el software no tiene defectos. Debido a esto, las pruebas de QA son una forma de medir la calidad de un producto, pero no es una forma de construir un producto con calidad.

El ciclo de retroalimentación que nos da el escribir y terminar una funcionalidad y luego probar es ineficiente porque la retroalimentación se obtiene muy tarde. Es posible trabajar en algo equivocado por minutos, horas y hasta días, a veces ese trabajo tiene que ser desechado o mínimo con muchos cambios.

Cuando la retroalimentación se retrasa mucho tiempo, por ejemplo, después de un ciclo de pruebas por parte de QA, el desarrollador encargado no tiene el contexto o no lo recuerda bien. Y esto genera más gastos, porque una vez reportado por QA se debe de encontrar la fuente del defecto y replicarlo, luego entender el problema, entender el código relacionado y además el tiempo que se tome corregirlo. Y otra vez QA prueba y reporta si el bug ha sido arreglado o no.

De nuevo el principio de Lean manufacturing sobre trabajar sobre cosas simples y pequeñas, no esperar a encontrar errores o mitigarlos hasta el final de la cadena de producción, sino en cada pequeña etapa del proceso tener uno o más ciclos completos de retroalimentación.

Ayuda a devops

Esto último va de la mano con la integración continua y despliegue continuo, si las pruebas fallan, no se puede establecer una integración y mucho menos un despliegue a producción.

Dado que un ciclo de retroalimentación temprana sucede en varios niveles del software, las incidencias aparecen antes de una integración y también mucho antes de un despliegue.

Claro que nunca se podrá mitigar todas los bugs o incidencias, pero por supuesto que se puede disminuir en una gran proporción, esto de igual manera resulta en reducción de costos, tiempo y esfuerzo.

El ambiente en los equipos se vuelve productivo, mejora la comunicación y seguridad en que de verdad se está entregando un producto de calidad, esto produce estimaciones más precisas, seguras, y honestas.

TDD como herramienta de diseño

Colaboración con el cliente sobre negociación contractual

Manifiesto ágil

Al escribir las pruebas primero (test-first programming) nos enfocamos en que el código de producción sea accesible desde el código de pruebas y sea fácil de probar. Esto quiere decir que desde un inicio nos preocupamos por el diseño y la arquitectura de nuestro código de producción, porque para que nuestro código de producción sea accesible y fácil de probar debe estar desacoplado.

Al escribir las pruebas, nuestra perspectiva de programador cambia, ahora pensamos en como otros programadores van a usar la API de nuestra funcionalidad antes de crearla, también desde la perspectiva de negocio porque cuando escribimos nuestras pruebas, a menudo nuevas dudas surgen que debemos indagar con personas que no son necesariamente programadores (muchas veces con cliente y usuarios).

Cada una de nuestras pruebas deben ejecutarse independientemente de las otras, en  nuestro esfuerzo de hacer que las pruebas se ejecuten independientemente, mejoramos el diseño del software, por ejemplo, una prueba unitaria corre dentro de un ambiente que no es real, se ejecuta sobre lo que llamamos test fixtures y sus dependencias (muchas de las cuales serán código de producción) deben ser lo bastante flexibles de tal manera que permitan ser sustituidas, invocadas y verificadas fácilmente. Esto permite crear un software con alta cohesión y bajo acoplamiento, es decir, con un buen diseño y una buena arquitectura.

Nuestros diseños deben consistir en muchos componentes altamente cohesivos y débilmente acoplados, solo para facilitar las pruebas.

Kent Beck

El código de pruebas debe desarrollarse con la más alta calidad posible, deben ser fácil de entender y fácil de ejecutar de manera independiente, y al enfocarnos en este aspecto, el código de producción creado también resulta de la más alta calidad posible. Sin perder de vista que esto sucede paso a pasito, en un proceso enfocado a la simplicidad, iterativo e incremental.

El resultado de un buen diseño, también resulta en que las modificaciones de código se hacen mucho más rápido, y esto quiere decir que no necesitas quedarte horas extras programando, tendrás más tiempo personal, que puedes compartir con tu familia, hijos, con tu pareja (o amante), etcétera.

TDD elimina el miedo a modificar el código

¿Por qué la mayoría de los programadores tienen miedo de hacer cambios a su código? ¡Tienen miedo de romper el código! ¿Por qué tienen miedo de romper el código? Porque no tienen pruebas.

Robert C. Martin, The Clean Coder: A Code of Conduct for Professional Programmers

Las pruebas son más importantes que el código de producción porque permiten que el sistema se vuelva flexible, de otra manera el miedo a la modificación del código crece, porque se teme romper la funcionalidad, y entonces el software ya no es adaptable, poco a poco, entre más evoluciona, menos flexible se vuelve por miedo a realizar modificaciones que rompan el producto.

¿No te ha pasado en algún proyecto, donde necesitas hacer unas modificaciones al código, y existen partes que mejor ni las tocas por miedo de romper alguna funcionalidad?, porque realmente no estás seguro de lo que esa parte del código hace y no tienes un conjunto de pruebas que respalde tus cambios.

¿Qué es código legacy?

Codigo que los programadores tienen miedo cambiar

Eli Lopia CEO de typemock

Código sin pruebas

Michael C. Feather

TDD permite mitigar el miedo de modificar tu código o el de otra persona. ¿Cómo puedes tener seguridad de que las funcionalidades que desarrollas tienen la calidad suficiente para decir que está terminado? Sin un conjunto de pruebas no tienes esa seguridad, y como ya hemos comentado antes, esperar retroalimentación hasta el ciclo de pruebas de un equipo de QA, es demasiado tarde.

Cuando se tiene un buen conjunto de pruebas, no tienes miedo de hacer los cambios necesarios, porque si rompes algo, las pruebas te lo advierten casi en tiempo real mientras estás realizando las modificaciones.

Individuos e interacciones sobre procesos y herramientas

Manifiesto ágil

Segun Ken Beck el miedo tiene los siguientes efectos, te hace:

  • Inseguro sobre el código que escribes
  • Querer comunicar menos
  • Tímido a la retroalimentación
  • Gruñón

Ningún efecto de arriba es de ayuda a la hora de programar y menos cuando nos enfrentamos a un problema complicado. Así que Kent Beck recomienda los siguientes puntos usando TDD:

  • En lugar de dudar de tu código, empieza a aprender cosas concretas lo más rápido posible. Paso a pasito, lotes pequeños y de manera iterativa e incremental con el ciclo red-green-refactor.
  • En lugar de quedarte callado, comunícate más claramente. Al usar TDD aumentas la calidad y por consiguiente estás dispuesto a comunicar dudas o problemas que enfrentas a la ahora de programar
  • En lugar de evitar retroalimentación, busca ayuda, y retroalimentación concreta.
  • (Sobre lo gruñón, estas por tu cuenta). Pero con más seguridad en tu código, menos problemas y más tiempo personal, seguro que tu nivel de gruñón bajará.

TDD como documentación precisa

Software funcionando sobre documentación extensiva

Manifiesto ágil

Para tener una documentación detallada es necesario mucho tiempo y esfuerzo porque la documentación siempre cambia conforme se le agregan nuevas funcionalidades o se modifican las ya existentes. Entonces ese tiempo y esfuerzo mejor se invierte en obtener software funcionando que puede ser presentado al cliente y obtener retroalimentación. Es muy difícil mantener sincronizada la documentación con el verdadero funcionamiento (con el código), con el tiempo la documentación miente sobre la funcionalidad, la única fuente de verdad es el código, el código no miente.

La mejor documentación e instrucciones de como funciona una pieza del sistema se encuentran en las personas y el código que ellas crean. Es de valor inigualable poner énfasis en la excelencia técnica y al buen diseño para mejorar la agilidad, es decir, código limpio, bien diseñado y utilizando TDD y BDD. De esta forma no necesitas llegar al detalle de alguna pieza del sistema, simplemente la usas con seguridad del resultado debido al buen diseño y a la calidad de la misma, lo que por supuesto te ahorra muchísimo tiempo. Claro que esto no se puede lograr sin las personas, sin su disciplina y auto organización.

Para ponerlo más claro, cuando tienes la disciplina de hacer TDD, cada una de las pruebas que creas, describe perfectamente como cada función o método, clase y/o componente se utiliza, de una manera tan precisa que si algo va mal cuando se está modificando, al menos una de las pruebas fallara casi al instante.

Cuando realizas pruebas de métodos y funciones, tienes que describir que parámetros reciben y que resultados regresan ¿Qué más documentación necesitas? Tampoco digo que nunca se haga documentación, claro que si, pero es una inversión de tiempo, dinero y esfuerzo, que puedes disminuir considerablemente y que puedes posponer hasta las etapas más estables del producto, cuando se sabe que su funcionalidad cambiará poco o en intervalos más largos de tiempo.

Al final, como programadores, siempre nos aseguramos o solo confiamos al 100% sobre como funciona cualquier API de software hasta que le echamos un ojo al código y lo probamos. Y sería estupendo que la funcionalidad del código esté descrita detalladamente a través de un conjunto de pruebas.

References

https://arialdomartini.wordpress.com/2012/07/20/you-wont-believe-how-old-tdd-is/

http://homepages.cs.ncl.ac.uk/brian.randell/NATO/nato1968.PDF

https://www.developsense.com/blog/2011/01/jerry-weinberg-interview-from-2008/

https://associationforsoftwaretesting.org/2014/11/02/how-to-sell-tdd-to-x/

https://www.computerworld.com/article/2470893/jean-bartik–last-of-the-original-eniac-programmers–86.html

https://en.wikipedia.org/wiki/ENIAC

https://es.wikipedia.org/wiki/Jean_Jennings_Bartik

https://www.quora.com/Why-does-Kent-Beck-refer-to-the-rediscovery-of-test-driven-development-Whats-the-history-of-test-driven-development-before-Kent-Becks-rediscovery

https://agileforall.com/history-of-tdd-as-told-in-quotes/

Test-Driven development: by example

Paradigma de programación en JavaScript

JavaScript programming paradigm

What is a programming paradigm?

The model or map of how we see the real world, that is a paradigm, it is a way of seeing and doing things. Following this logic, a programming paradigm is nothing more than a way of viewing and creating programming code.

Paradigms are powerful because they create the glasses or lenses through which we see the world.

Stephen R. Covey

There are three main programming paradigms used today and in JavaScript they have always existed since its first version.

  • Structured programming paradigm
  • Object-oriented
  • Functional

Now, if we stick to the phrase of Stephen R. Covey, that paradigms create the lenses through which we see the world. Let's imagine that the three previous paradigms, each, are a pair of glasses that we put on when programming and that we can change those glasses according to our visual needs (for programming or problem solving).

Structured programming paradigm

Origin

In 1954 the FORTRAN programming language appeared, then in 1958 ALGOL and in 1959 COBOL. Time after Edsger Wybe Dijkstra in 1968 discover the structured programming paradigm, we say “discover” because they didn't actually invent it. Although this programming paradigm was formalized some time after the appearance of these programming languages, it was possible to do structured programming in them.

Dijkstra He recognized that programming was difficult, and programmers don't do it very well, which I totally agree with. A program of any complexity has many details for the human brain. In fact, the Neuroscience tells us that the focused mind It can only work with four pieces of data at a time, at most. For this reason, if one small detail is neglected, we create programs that seem to work well, but fail in ways you never imagine.

Evidence

Dijkstra's solution was to use tests, only these tests used a lot of mathematics, which was quite difficult to implement. During his research he found that certain uses of GOTO made it difficult to decompose a large program into smaller pieces, it could not be applied “divide and conquer”, necessary to create reasonable evidence.

Sequence, selection and iteration

Patrones de la programación estructurada
Patterns of structured programming

Böhm and Jacopini They proved two years earlier that all programs can be built by just three structures: sequence, selection and iteration. Dijkstra already knew these three structures and discovered that these are the ones necessary to make any part of the program tested. This is where the structured programming paradigm is formalized.

Edsger tells us that it is bad practice to use GOTO and suggests better using the following control structures:

  • If, then, else (selection)
  • do, while, until (iteration or repetition)

Decomposition of small units easy to test

Nowadays most programming languages use structured programming, JavaScript uses this type of programming. The simplest example is if conditions.

const EDAD_MINIMA = 18;
if (edad >= EDAD_MINIMA) {
	// hacer algo porque es mayor que 18
} else {
	// Hacer otra cosa en caso contraio
}

And if we isolate it in a function, we have a decomposition of functionalities or units that can be tested more easily. In the next section we will see the importance of having a completely probable unit

const EDAD_MINIMA = 18;
functions esMayorEdad(edad) {
  if (edad >= EDAD_MINIMA) {
     return true;
  } else {
     return false;
  }
}

With this condition we have direct control of what can happen if the age is greater than or equal to 18, and also in the event that the age is younger.

Let's not be so strict either, surely the GOTO ruling has its merits and perhaps it was very efficient in some cases, but I think that to avoid misuses that probably caused disasters, Dijkstra recommended to stop using GOTO.

Test, divide and conquer

Structured programming allows us to apply the “divide and conquer” philosophy, because we allows you to create small unit tests, until the entire program is covered. The mathematical solution of Wybe Dijkstra It was never built, precisely because of its difficulty in implementation. And unfortunately today there are still programmers who do not believe that formal tests are useful for create high quality software.

And I say create, because verify correct operation after create, is nothing more than a simple measurement and the opportunity to reduce time and money is lost.

Scientific method, experiments and impiricism

Método científico, experimentos y pruebas
Scientific method, experiments and tests, by ThisIsEngineering Raeng on unsplash

The good news is that the mathematical method is not the only way to verify our code, we also have the scientific method. Which cannot prove that things are absolutely correct from a mathematical point of view. What we can do is create experiments and obtain enough results to verify that our theories work. Since our theories work on these experiments, then we conclude that they are “correct” enough for our validation purposes.

If the theory is easily validated as false, then it is time to modify or change it. This is the scientific method and this is how tests are currently done:

First we establish our theory (write the test), then we build the experiments (write production code) and finally we verify (run the test) and repeat this cycle until we have enough evidence.

Doesn't this sound like a current agile practice called TDD?

Tests show the presence, not the absence, of defects

Edsger Wybe Dijkstra said “Testing shows the presence, not the absence, of defects.” That is, a program can be proven wrong by testing, but it cannot be proven correct. All we can do with our tests is validate that our program works well enough for our goals. But we can never be one hundred percent sure of the absence of defects.

If we can never be one hundred percent sure that our code is correct, even with its tests, what makes us think that without tests we deliver a program with sufficient validity to affirm that no negligence is consciously committed?

direct control

lto structured programming teaches us about direct control, that is, controlling the sequential flow of operations through the control structures, without neglecting the recommendation of Wybe Dijkstra about GOTO. This recommendation also applies to statements that drastically change the normal flow of operations, for example a break within a cycle for nested, that break It breaks direct control because it abruptly complicates the understanding of the algorithm. Let's not be purists either, there will surely be cases where you need these sentences, but in the first instance, try to avoid them.

In conclusion, the structured programming paradigm teaches us:

The direct, clear and explicit control of what a piece of code does

Object-oriented programming paradigm

Arised from functions?

The object-oriented programming paradigm was discovered in 1966 by Ole Johan Dahl and Kristen Nygaard when they were developing simula 67. Two years before structured programming and its adoption by the programming community was some time later. In 1967 it was launched Simulates 67, Simulates It was the first object-oriented programming language, it basically added classes and objects to ALGOL, initially Simula was going to be a kind of extension of ALGOL.

During the development of Simula, it was observed that the execution of an ALGOL function requires certain data, which could be moved to a tree structure. This tree structure is a Heap.

The heap allowed you to declare local variables of the function that can exist even after the function returns a value. Doesn't this last sound like what closure is in JavaScript?

The parent function becomes a constructor, the local variables become the properties, and the child (nested) functions become its methods. In fact, this pattern is still widely used by Javascript to create modules and use functional inheritance. This also describes how classes work in Javascript, behind are functions.

Spread of polymorphism

All this helped a lot to implement polymorphism in object-oriented programming languages. Ole Johan Dahl and Kristen Nygaard invented the notation:

object.function(parametro)

The execution of function that belongs to object, but more importantly, we pass a message through parameters from the first part of the code to the second part located in a different object.

Molecular biology and message passing

Paso de mensajes entre células
Passing messages between cells, Image modified from “Signaling molecules and cellular receptors: Figure 1,” by OpenStax College, Biology (CC BY 3.0).

Some time later it was created Smalltalk, a much more sophisticated and modern object-oriented programming language, in charge of this project was Alan Kay. This person is credited with the formal definition of object-oriented programming.

The main influence on Alan Kay's object-oriented programming was biology, he has a degree in biology. Although it is obvious that it was influenced by Simula, the influence of LISP (functional programming language) is not so obvious.

From the beginning he thought of objects as interconnected cells in a network, from which they could communicate through messages.

Alan Kay commented:

I'm sorry I coined the term a long time ago. Objects for programming because it made people focus on the least important part. The big idea is “Sending messages“.

Alan Kay

My English is bad so you can check the original text here.

Better object composition over class inheritance

Smalltalk It also allowed the creation of Self, created at Xerox Parc and later migrated to Sun Microsystems Labs. It is said that Self It is an evolution of smalltalk.

In this programming language the idea of prototypes, eliminating the use of classes to create objects, this language uses the objects themselves to allow one object to reuse the functionalities of another.

Self It is a fast language and is generally known for its great performance, self It did a good job on garbage collection systems and also used a virtual machine to manage its execution and memory.

The virtual machine Java HotSpot It could be created thanks to Self. Lars Bak one of Self's latest contributors, he was in charge of creating the V8 Javascript engine. Due to the influence of Self we have today the engine of JavaScript V8, which uses it node.js, mongoDB and Google Chrome internally.

Self followed one of the book's recommendations Design Patterns: Elements of Reusable Object-Oriented Software, long before it came out, published this recommendation:

Better composition of objects to class inheritance.

Design Patterns: Elements of Reusable Object-Oriented Software

Example

Currently Javascript uses prototypes for code reuse, you could say that it uses composition, instead of inheritance. Let's look at an example:

const person = {
    greet () {
      return 'Hola'
    }
};
// se crea un programador con el prototipo igual al objeto persona
const programmer = object.create(persona);
programmer.programar  = () => 'if true';
const saludo = programmer.greet();
const codigo = programmer.programar();
console.log(saludo); // 'Hola'
console.log(codigo); // 'if true'

The object programmer It is based on the prototype of `person`. It doesn't inherit, it just directly reuses the functionality of `person`.

JavaScript emerged shortly after JAVA, in the same year. JavaScript took influences from Self, and in turn Self was influenced by smalltalk.

The syntax of current classes in Javascript is nothing more than a facade, well a little more accurately they are an evolution of the constructor functions, but internally, their basis are functions and prototypes.

Communication with message passing

As we already saw, the origin of object-oriented programming has its origins in functions, and the main idea has always been communication between objects through message passing. We can say that Object-oriented programming tells us of message passing as a key piece for the communication of objects, for example, when a method of one object invokes the method of another.

object.function(parametro);

In conclusion, the object-oriented programming paradigm teaches us:

Message passing for communication between objects

Functional programming paradigm

lambda calculation

Image taken from https://www.slideshare.net/FASTPresentations/introduction-to-lambda-calculus-using-smalltalk-by-facundo-javier-gelatti

Functional programming is the oldest programming paradigm, in fact its discovery was long before computer programming, it was discovered in 1936 by Alonzo Church, when I invented the lambda calculus, based on the same problem to be solved by your student Alan Turing.

As a curious fact, the symbol of the lambda calculus is:

λ

The first language based on functional programming was LISP, created by John McCarthy. As we saw previously, Alan Kay also took influence from LISP to create Smalltalk, with this we can see that the programming paradigms can be united and their relationship between them arises from the idea of how to solve problems more efficiently, they are not fighting, nor Separated, they seek the same goal and somehow evolve together.

The basis of the lambda calculus is immutability, We will review this concept later.

Similarities with OO and structured paradigms

As we saw in the structured programming paradigm, we can decompose our programs into smaller units called procedures or functions. Since functional programming is the oldest paradigm, we can say that the structured programming paradigm also relies on functional programming.

Now, if we analyze the notation a little object.function(x) that we saw in the object-oriented programming paradigm section, is not very different from function(object, parameters), these two lines below are the same, the fundamental idea is the passing of messages, as Alan Kay tells us.

object.function(parametro);
function(objeto, parametro);

Smalltalk uses the “The Actor Model”, which says that actors communicate with each other through messages.

On the other hand we have LISP, which has a “Function dispatcher model” What we currently call functional language, these models are identical, because what functions and methods do is send messages between actors.

An example of this is when a function calls another function or when an object's method is invoked, what happens is that actors exist and they communicate with each other through messages.

Message passing again

So we can emphasize that the main idea of OOP is the sending of messages and that it is not very different from functional programming, in fact according to what we already established in the previous sections, OOP was born from a functional base. And here it is important to highlight what he said Alan Kay Co-creator of SmallTalk:

I'm sorry I coined the term a long time ago. Objects for programming because it made people focus on the least important part. The big idea is “Sending messages“.

Alan Kay

From the communication models between Smalltalk and LISP messages, it was created scheme.

scheme has the goodness to use tail recursion and closure to obtain a functional language, closure allows access to the variables of an external function even when it has already returned a value. This is the same principle that gave rise to object-oriented programming by Ole Johan Dahl and Kristen Nygaard. Do you remember the closure question in Javascript?

Javascript uses closure a lot in its functional programming paradigm. JavaScript took influences from scheme, at the same time scheme was influenced by LISP.

No side effects

The basis of the lambda calculus is immutability, therefore, lImmutability is also the basis of the functional programming paradigm.

We as functional programmers must follow this principle and limit object mutations as much as possible to avoid side effects.

I repeat, the main idea of functional programming is immutability, which allows you to create your programs with fewer errors by not producing side effects that are difficult to control.

As an example, suppose we have an object person and we want to “change” the name, the most obvious thing would be to modify the name property directly, but what happens if that object is used elsewhere and the name is expected to be “Jaime”, that is why instead of changing the name property , we only create a new person object with a different name, without modifying the original.

functions cambiarNombre(name, person) {
  return {
    name: name,
    edad: person.edad
  }
}
const James = { name: 'Jaime', edad: 30 };
const juan = cambiarNombre('Juan', jaime);
console.log(jaime); // { nombre: 'Jaime', edad: 30 }
console.log(juan); // { nombre: 'Juan', edad: 30 }

Here you will find more details about the fundamental principles of functional programming:

Introduction to functional programming

Finally, the functional programming paradigm teaches us:

No side effects, for clear expression and less prone to errors

Conclusion

It is important to note that if the three programming paradigms were implemented between 1958 with LISP (functional programming) and Simula (object-oriented programming) in 1966, and the discovery of the structured programming paradigm in 1968, in only a period of 10 For years there has been innovation in programming paradigms, and new paradigms have not really emerged, unless they are based on these three main ones.

Right now more than 60 years have passed, which tells us the importance and firmness they have despite the time that has passed.

  • Functional programming paradigm (1958, already implemented in a programming language for computers, remember that it was discovered in 1936)
  • Object Oriented (1966)
  • Structured (1968)

The implementation of these three paradigms from the beginning within JavaScript has made this programming language the global success it is today. And it proves to us the value of combining paradigms when writing our programs.

JavaScript is a multi-paradigm programming language, so you can combine the paradigms to create much more efficient and expressive code using:

  • The direct, clear and explicit control of what a piece of code does
  • Message passing for communication between objects
  • No side effects, for clear expression and less prone to errors

References

https://en.wikipedia.org/wiki/Structured_programming

https://en.wikipedia.org/wiki/Structured_program_theorem

http://wiki.c2.com/?AlanKayOnMessaging

https://www.slideshare.net/FASTPresentations/introduction-to-lambda-calculus-using-smalltalk-by-facundo-javier-gelatti

Clean architecture book

en_USEN